mercredi 9 février 2022

Propagating a VisualState of your ListViewItemPresenter to your DataTemplate using the Parent element [XAML]

In this Article we are going to see how to Propagate a VisualState of your ListViewItemPresenter to your DataTemplate using the Parent element

My need was that I needed to be able to pass down the information that an item was selected so that I could have the round blue border around the icon.  One solution would have been to add a isSelected property to my header items however I wished something simple to manage the selected state of my header.

Here is a snapshot of what I wanted:



In the end you have two options to propagate the VisualState.  First you can use this option which I found on Microsoft Docs 

Visual states for elements that aren't controls

Visual states are sometimes useful for scenarios where you want to change the state of some area of UI that's not immediately a Control subclass. You can't do this directly because the control parameter of the GoToState method requires a Control subclass, which refers to the object that the VisualStateManager acts upon. Page is a Control subclass, and it's fairly rare that you'd be showing UI in a context where you don't have a Page, or your Window.Content root isn't a Control subclass. We recommend you define a custom UserControl to either be the Window.Content root or be a container for other content you want to apply states to (such as a Panel ). Then you can call GoToState on your UserControl and apply states regardless of whether the rest of the content is a Control. For example you could apply visual states to UI that otherwise consists of just a SwapChainPanel so long as you placed that within your UserControl and declared named states that apply to the properties of the parent UserControl or of the named SwapChainPanel part of the template.

Source: https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.visualstatemanager?view=winrt-19041

However this was a total fail for me as it never worked and also seemed overkill to set a Usercontrol inside my DataTemplate, I do think that with a bit more work I could have gotten it to work but belived that a simpler solution was possible for my needs.

Binding to the ParentElement

In the end the solution that I found that worked the best for my needs was to bind the element to its parent so that when the parent knew that its item was being selected I could pass down this information to my the DataTemplate. The idea was to bind the BorderThickness of my elements in my DataTemplate to it ParentElement, in xaml this means:

BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}"

Here my TemplatedParent is the ListViewItemPresenter that is located in my ListView. And with this I was able to pass the different states values Selected/UnSelected to my DataTemplate.  I do agree that If i was able to pass all the different states to my DataTemplate that would have been better but this is the next best thing for me.

Here is my DataTemplate:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<DataTemplate x:Key="KeyHeaderFollowingItemTemplate" x:DataType="m:HeaderItemViewModel">
	<Grid
		x:Name="RootGrid"
		Width="235"
		Height="65"
		Margin="4"
		HorizontalAlignment="Stretch"
		VerticalAlignment="Stretch">
		<Grid.RowDefinitions>
			<RowDefinition Height="65" />
		</Grid.RowDefinitions>

		<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
			<Grid>
				<Border
					Height="58"
					Margin="0,0,12,0"
					Background="Transparent"
					BorderBrush="{StaticResource DmLightBlueBrush}"
					BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}"
					CornerRadius="5">
					<Grid>
						<controls:ImageEx
							x:Name="HeaderImg"
							Grid.Row="0"
							Height="51"
							Margin="0,0,0,0"
							HorizontalAlignment="Center"
							VerticalAlignment="Center"
							BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}"
							CornerRadius="4"
							Source="{Binding HeaderImg}"
							Stretch="UniformToFill" />
					</Grid>
				</Border>
			</Grid>

			<TextBlock
				Name="TitleTxt"
				Width="160"
				Margin="0,8,0,8"
				VerticalAlignment="Center"
				FontFamily="Retina"
				FontSize="16"
				FontWeight="Bold"
				LineHeight="16"
				Text="{Binding HeaderTitle, FallbackValue=Loading}"
				TextTrimming="WordEllipsis"
				TextWrapping="Wrap" />
		</StackPanel>
	</Grid>
</DataTemplate>

Happy Coding!