问题
I am using Visual State Adaptive Trigger to change page appearance depending on effective screen resolution. This works great, but I cannot get it to work for ListView Items.
My ListView looks something like this:
<ListView x:Name="listView" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="20" Text="{Binding MyItem}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I can change the TextBlock font size statically in XAML. But I don't know how to reference this in VisualState setter target. Being automatically generated child, I cannot give TextBlock a name. My Visual State code is below. I've put ??? where I want to refer to ListView TextBlock item FontSize.
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="NormalStateReadView">
<!-- VisualState to be triggered when window width is <720 effective pixels -->
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="???" Value="20" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup>
<VisualState x:Name="NormalStateReadView">
<!-- VisualState to be triggered when window width is >=720 effective pixels -->
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="???" Value="30" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
Any help appreciated.
回答1:
We can't use AdaptiveTrigger
for an element inside the DataTemplate
. You should use UserControl to do it. This answer may help you. If you want a video tutorial then this video may help you. But I have a little workaround for it.
Here is my method:
Create a
TextBlock
outside theDataTemplate
and setVisibility="Collapsed"
<TextBlock Name="SetFontSize" Visibility="Collapsed"/>
Note: You could also use BindableValueHolder instead of this and set the Value property of it using
AdaptiveTrigger
.<helpers:BindableValueHolder x:Name="SetFontSize"/>
Use
AdaptiveTrigger
to change theFontSize
of thisTextBlock
<VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualState x:Name="NormalStateReadView"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="0" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="SetFontSize.FontSize" Value="20" /> </VisualState.Setters> </VisualState> </VisualStateGroup> <VisualStateGroup> <VisualState x:Name="WideStateReadView"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="720" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="SetFontSize.FontSize" Value="30" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>
DataBind the
FontSize
of theTextBlock
inside theDataTemplate
to the outerTextBlock
<TextBlock FontSize="{Binding Path=FontSize, ElementName=SetFontSize}"/>
Here is your code:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="NormalStateReadView">
<!-- VisualState to be triggered when window width is <720 effective pixels -->
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SetFontSize.FontSize" Value="20" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup>
<VisualState x:Name="WideStateReadView">
<!-- VisualState to be triggered when window width is >=720 effective pixels -->
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SetFontSize.FontSize" Value="30" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock Name="SetFontSize" Visibility="Collapsed"/>
<ListView x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="{Binding Path=FontSize, ElementName=SetFontSize}" Text="{Binding MyItem}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
回答2:
You will need to wrap your your template inside a UserControl
for adaptive triggers to work; also the visual states should go inside the template as well, and make sure it's under the first direct child (i.e. Grid
) of the UserControl
. I see you have defined two visual state groups but really you just need one.
<ListView.ItemTemplate>
<DataTemplate>
<UserControl>
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="NarrowState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Title.(TextBlock.FontSize)" Value="24" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="WideState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Title.(TextBlock.FontSize)" Value="36" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock x:Name="Title" Text="{Binding Property1}" />
</Grid>
</UserControl>
</DataTemplate>
</ListView.ItemTemplate>
Update
Here is what I think a much more flexible solution. It needs some code to setup but the outcome is that you get clean, reusable and readable code.
The idea is to obtain a reference of the top level VisualStateGroup
which is defined outside of the ListView
, mostly at the Page
level.
Then, create three VisualState
s (i.e. Narrow, Normal & Wide) and attach them to a new VisualStateGroup
which should belong to the first child of the UserControl
.
Once we have referenced these two VisualStateGroup
s, we can monitor the top level one's CurrentStateChanged
and update the state in the second one accordingly.
All the logic above can be wrapped by a Behavior
from the UWP XAML Behavior nuget package.
Once the Behavior in place, you can define your inner states as easy as -
<DataTemplate>
<UserControl>
<Interactivity:Interaction.Behaviors>
<local:VisualStateInTemplateBehavior ParentVisualStateGroup="{Binding ElementName=AdaptiveVisualStates}">
<local:VisualStateInTemplateBehavior.NarrowState>
<VisualState x:Name="Narrow">
<VisualState.Setters>
<Setter Target="Title.(TextBlock.FontSize)" Value="24" />
</VisualState.Setters>
</VisualState>
</local:VisualStateInTemplateBehavior.NarrowState>
<local:VisualStateInTemplateBehavior.NormalState>
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Target="Title.(TextBlock.FontSize)" Value="32" />
</VisualState.Setters>
</VisualState>
</local:VisualStateInTemplateBehavior.NormalState>
<local:VisualStateInTemplateBehavior.WideState>
<VisualState x:Name="Wide">
<VisualState.Setters>
<Setter Target="Title.(TextBlock.FontSize)" Value="40" />
</VisualState.Setters>
</VisualState>
</local:VisualStateInTemplateBehavior.WideState>
</local:VisualStateInTemplateBehavior>
</Interactivity:Interaction.Behaviors>
Working Demo
Feel free to check out a working sample here.
来源:https://stackoverflow.com/questions/44751821/how-to-use-visualstate-adaptivetrigger-to-change-listview-item-fontsize