Skip to content

Commit 4065216

Browse files
Initial switch over to inversion of paradigm for Header behaviors to enable more scenarios
1 parent e113a05 commit 4065216

File tree

7 files changed

+39
-67
lines changed

7 files changed

+39
-67
lines changed

components/Behaviors/samples/Headers/FadeHeaderBehaviorSample.xaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010

1111
<!-- We need to set height here to force scrolling in the sample -->
1212
<ListView Height="300">
13-
<interactivity:Interaction.Behaviors>
14-
<behaviors:FadeHeaderBehavior />
15-
</interactivity:Interaction.Behaviors>
1613
<ListView.Header>
1714
<Grid MinHeight="100"
1815
Background="#FF0078D7">
16+
<interactivity:Interaction.Behaviors>
17+
<behaviors:FadeHeaderBehavior />
18+
</interactivity:Interaction.Behaviors>
1919
<TextBlock HorizontalAlignment="Center"
2020
VerticalAlignment="Center"
2121
Text="Sample Text" />
Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Header Behaviors
33
author: michael-hawker
4-
description: Behaviors modifying the headers of ListView based components when scrolling.
4+
description: Behaviors for modifying an element's movement/response when scrolling within a ScrollViewer.
55
keywords: Behaviors
66
dev_langs:
77
- csharp
@@ -12,22 +12,30 @@ issue-id: 0
1212
icon: Assets/Behaviors.png
1313
---
1414

15-
The `FadeHeaderBehavior`, `QuickReturnHeaderBehavior`, and `StickyHeaderBehavior` apply behaviors to `ListView`, `GridView`, and `HeaderedTreeView` Headers.
15+
The `FadeHeaderBehavior`, `QuickReturnHeaderBehavior`, and `StickyHeaderBehavior` most commonly apply behaviors to `ListView`, `GridView`, and `HeaderedTreeView` elements in their `Header`. It can also be applied to any element contained at the top of a `ScrollViewer`.
16+
17+
They use composition animations to allow the visual of an element of a scrolling viewport to be manipulated for various effects.
18+
19+
To use the behavior, place it on the _element in the header to be manipulated_.
20+
21+
> [!NOTE]
22+
> This is different in 8.x than it was in 7.x where the behavior was placed on the parent containing element (e.g. `ListView`).
23+
> This change was made to enable more scenarios for these components.
1624
1725
## FadeHeaderBehavior
1826

19-
The FadeHeaderBehavior causes the Header of the scrolling collection to fade in and out as the user scrolls at the top of the collection.
27+
The FadeHeaderBehavior causes the element in the scrolling collection to fade in and out as the user scrolls at the top of the collection.
2028

2129
> [!Sample FadeHeaderBehaviorSample]
2230
2331
## QuickReturnBehavior
2432

25-
The QuickReturnHeaderBehavior causes the Header of the scrolling collection to return back into view as soon as the user scrolls up even if they are not near the top of the collection.
33+
The QuickReturnHeaderBehavior causes the element in the scrolling collection to return back into view as soon as the user scrolls up even if they are not near the top of the collection.
2634

2735
> [!Sample QuickReturnHeaderBehaviorSample]
2836
2937
## StickyHeaderBehavior
3038

31-
The StickyHeaderBehavior causes the Header of the scrolling collection to stay in view as the user scrolls up and down in the collection.
39+
The StickyHeaderBehavior causes the element in the scrolling collection to stay in view as the user scrolls up and down in the collection.
3240

3341
> [!Sample StickyHeaderBehaviorSample]

components/Behaviors/samples/Headers/QuickReturnHeaderBehaviorSample.xaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010

1111
<!-- We need to set height here to force scrolling in the sample -->
1212
<GridView Height="300">
13-
<interactivity:Interaction.Behaviors>
14-
<behaviors:QuickReturnHeaderBehavior />
15-
</interactivity:Interaction.Behaviors>
1613
<GridView.Header>
1714
<Grid MinHeight="100"
1815
Background="#FF0078D7">
16+
<interactivity:Interaction.Behaviors>
17+
<behaviors:QuickReturnHeaderBehavior />
18+
</interactivity:Interaction.Behaviors>
1919
<TextBlock HorizontalAlignment="Center"
2020
VerticalAlignment="Center"
2121
Text="Sample Text" />

components/Behaviors/samples/Headers/StickyHeaderBehaviorSample.xaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
<!-- We need to set height here to force scrolling in the sample -->
1515
<controls:HeaderedTreeView Height="300"
1616
ItemsSource="{x:Bind Items, Mode=OneWay}">
17-
<interactivity:Interaction.Behaviors>
18-
<behaviors:StickyHeaderBehavior />
19-
</interactivity:Interaction.Behaviors>
2017
<controls:HeaderedTreeView.Header>
2118
<Grid MinHeight="100"
2219
Background="#FF0078D7">
20+
<interactivity:Interaction.Behaviors>
21+
<behaviors:StickyHeaderBehavior />
22+
</interactivity:Interaction.Behaviors>
2323
<TextBlock HorizontalAlignment="Center"
2424
VerticalAlignment="Center"
2525
Text="Sample Text" />

components/Behaviors/src/Headers/FadeHeaderBehavior.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ protected override bool AssignAnimation()
3434
var scrollPropSet = _scrollProperties!.GetSpecializedReference<ManipulationPropertySetReferenceNode>();
3535

3636
// Use the ScrollViewer's Y offset and the header's height to calculate the opacity percentage. Clamp it between 0% and 100%
37-
var headerHeight = (float)HeaderElement.RenderSize.Height;
37+
var headerHeight = (float)AssociatedObject.RenderSize.Height;
3838
var opacityExpression = ExpressionFunctions.Clamp(1 - (-scrollPropSet.Translation.Y / headerHeight), 0, 1);
3939

4040
// Begin animating

components/Behaviors/src/Headers/HeaderBehaviorBase.cs

Lines changed: 15 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,6 @@ public abstract class HeaderBehaviorBase : BehaviorBase<FrameworkElement>
2424
protected CompositionPropertySet? _animationProperties;
2525
protected Visual? _headerVisual;
2626

27-
/// <summary>
28-
/// Gets or sets the target element for the Header behavior to be manipulated within the viewport.
29-
/// </summary>
30-
/// <remarks>
31-
/// Set this using the header of a ListView or GridView.
32-
/// </remarks>
33-
public FrameworkElement HeaderElement
34-
{
35-
get { return (FrameworkElement)GetValue(HeaderElementProperty); }
36-
set { SetValue(HeaderElementProperty, value); }
37-
}
38-
39-
/// <summary>
40-
/// Defines the Dependency Property for the <see cref="HeaderElement"/> property.
41-
/// </summary>
42-
public static readonly DependencyProperty HeaderElementProperty = DependencyProperty.Register(
43-
nameof(HeaderElement), typeof(FrameworkElement), typeof(HeaderBehaviorBase), new PropertyMetadata(null, PropertyChangedCallback));
44-
45-
/// <summary>
46-
/// If any of the properties are changed then the animation is automatically started.
47-
/// </summary>
48-
/// <param name="d">The dependency object.</param>
49-
/// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
50-
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
51-
{
52-
if (d is HeaderBehaviorBase @base)
53-
{
54-
@base.AssignAnimation();
55-
}
56-
}
57-
5827
/// <summary>
5928
/// Attaches the behavior to the associated object.
6029
/// </summary>
@@ -95,23 +64,23 @@ protected virtual bool AssignAnimation()
9564
return false;
9665
}
9766

98-
// TODO: What if we attach to the 'header element' and look up for the ScrollViewer?
9967
if (_scrollViewer == null)
10068
{
101-
_scrollViewer = AssociatedObject as ScrollViewer ?? AssociatedObject.FindDescendant<ScrollViewer>();
69+
// TODO: We probably want checks which provide better guidance if we detect we're not attached correctly?
70+
_scrollViewer = AssociatedObject.FindAscendant<ScrollViewer>();
10271
}
10372

10473
if (_scrollViewer == null)
10574
{
10675
return false;
10776
}
10877

109-
var listView = AssociatedObject as ListViewBase ?? AssociatedObject.FindDescendant<ListViewBase>();
78+
var itemsControl = AssociatedObject.FindAscendant<ItemsControl>();
11079

111-
// TODO: Is this required?
112-
if (listView != null && listView.ItemsPanelRoot != null)
80+
if (itemsControl != null && itemsControl.ItemsPanelRoot != null)
11381
{
114-
Canvas.SetZIndex(listView.ItemsPanelRoot, -1);
82+
// This appears to be important to force the items within the ScrollViewer of an ItemsControl behind our header element.
83+
Canvas.SetZIndex(itemsControl.ItemsPanelRoot, -1);
11584
}
11685

11786
if (_scrollProperties == null)
@@ -124,20 +93,15 @@ protected virtual bool AssignAnimation()
12493
return false;
12594
}
12695

127-
// Implicit operation: Find the Header object of the control if it uses ListViewBase
128-
if (HeaderElement == null && listView != null)
129-
{
130-
HeaderElement = (listView.Header as FrameworkElement)!;
131-
}
132-
133-
if (HeaderElement == null || HeaderElement.RenderSize.Height == 0)
96+
// Implicit operation: Double-check that we have an element associated with us (we should) and that it has size
97+
if (AssociatedObject == null || AssociatedObject.RenderSize.Height == 0)
13498
{
13599
return false;
136100
}
137101

138102
if (_headerVisual == null)
139103
{
140-
_headerVisual = ElementCompositionPreview.GetElementVisual(HeaderElement);
104+
_headerVisual = ElementCompositionPreview.GetElementVisual(AssociatedObject);
141105
}
142106

143107
if (_headerVisual == null)
@@ -146,8 +110,8 @@ protected virtual bool AssignAnimation()
146110
}
147111

148112
// TODO: Not sure if we need to provide an option to turn these events off, as FadeHeaderBehavior didn't use these two, unlike QuickReturn/Sticky did...
149-
HeaderElement.SizeChanged -= ScrollHeader_SizeChanged;
150-
HeaderElement.SizeChanged += ScrollHeader_SizeChanged;
113+
AssociatedObject.SizeChanged -= ScrollHeader_SizeChanged;
114+
AssociatedObject.SizeChanged += ScrollHeader_SizeChanged;
151115

152116
_scrollViewer.GotFocus -= ScrollViewer_GotFocus;
153117
_scrollViewer.GotFocus += ScrollViewer_GotFocus;
@@ -177,9 +141,9 @@ protected virtual void RemoveAnimation()
177141
_scrollViewer.GotFocus -= ScrollViewer_GotFocus;
178142
}
179143

180-
if (HeaderElement != null)
144+
if (AssociatedObject != null)
181145
{
182-
HeaderElement.SizeChanged -= ScrollHeader_SizeChanged;
146+
AssociatedObject.SizeChanged -= ScrollHeader_SizeChanged;
183147
}
184148

185149
StopAnimation();
@@ -210,9 +174,9 @@ private void ScrollViewer_GotFocus(object sender, RoutedEventArgs e)
210174
{
211175
var point = element.TransformToVisual(scroller).TransformPoint(new Point(0, 0));
212176

213-
if (point.Y < HeaderElement.ActualHeight)
177+
if (point.Y < AssociatedObject.ActualHeight)
214178
{
215-
scroller.ChangeView(0, scroller.VerticalOffset - (HeaderElement.ActualHeight - point.Y), 1, false);
179+
scroller.ChangeView(0, scroller.VerticalOffset - (AssociatedObject.ActualHeight - point.Y), 1, false);
216180
}
217181
}
218182
}

components/Behaviors/src/Headers/QuickReturnHeaderBehavior.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ private void ScrollViewer_ViewChanged(object? sender, ScrollViewerViewChangedEve
9090
{
9191
if (_animationProperties != null && _scrollViewer != null)
9292
{
93-
var headerHeight = HeaderElement.ActualHeight;
93+
var headerHeight = AssociatedObject.ActualHeight;
9494
if (_headerPosition + headerHeight < _scrollViewer.VerticalOffset)
9595
{
9696
// scrolling down: move header down, so it is just above screen

0 commit comments

Comments
 (0)