如何刷新VisualStateManager 'Focused' VisualState的样式效果?
How to refresh style effects of 'Focused' VisualState of VisualStateManager?
我有如下所示的 ListBoxItem 样式,当 listBoxItem 保留在提到的 VisualState 中时,会出现失去 'Focused' 视觉效果的问题。如何强制适当的视觉效果出现在“Focused”ListBoxItem 上?
实现bug的场景:
1) 单击 ListBoxItem -> 它接收焦点和样式(ok)。
2) 将指针移到此 ListBoxItem -> 它仍然聚焦并接收“MouseOver”的样式(它位于单独的 VisualStateGroup“CommonStates”中,如此处建议:https://msdn.microsoft.com/en-us/library/system.windows.controls.listboxitem%28v=vs.95%29.aspx)(确定)。
3) 使鼠标离开 ListBoxItem 的边界 -> 它失去了 MouseOver(ok) 的样式。
4) 现在,ListBoxItem 处于 'CommonStates' 的 'Normal' 状态并且也是 'Focused',但没有 'Focused' 的样式(这是我的问题)。
5) 尝试单击此 ListBoxItem 无效
能不能给点建议,怎么处理?
我正在使用 Silverlight,所以触发器被禁止 4 我。
这是我的风格:
<Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="ContentControl.Foreground" Value="{StaticResource ViewModeButtonForeground}" />
<Setter Property="Focusable" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="RootElement">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="(ContentControl.Foreground)">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonForeground}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonOuterBorder_MouseOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonBackground_MouseOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="(ContentControl.Foreground)">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonForeground_Focused}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonOuterBorder_Focused}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonBackground_Normal}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused"></VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Border x:Name="CheckOuterBorder" CornerRadius="2" BorderThickness="1" BorderBrush="Transparent" Background="Transparent">
</Border>
<Grid x:Name="GridContent" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Width="16" Height="16" VerticalAlignment="Center" HorizontalAlignment="Left" Source="{Binding ImgSource, Mode=OneWay}"/>
<ContentControl x:Name="Content" Grid.Column="1" Margin="3,0,0,0" Foreground="{TemplateBinding Foreground}" Content="{Binding Title}" ContentTemplate="{TemplateBinding ContentTemplate}" VerticalAlignment="Center" Width="Auto"/>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ViewModeSelectionListBoxStyle" TargetType="ListBox">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="IsTabStop" Value="True" />
<Setter Property="ItemContainerStyle" Value="{StaticResource ListBoxItemStyle1}" />
<Setter Property="Focusable" Value="True" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
如果我将我的 VisualStateGroup 从“FocusStates”更改为 SelectionStates,它的行为是一样的。我希望它与焦点而不是选择相关联,因为如果用户单击某处并且另一个控件也将具有类似的边框,则 UI 中可能存在视觉不匹配。
顺便说一句:我在另一个 msdn 网站上读到:
“对于在其 ControlTemplate 中定义的每个 VisualStateGroup,该控件始终处于一种状态,并且仅当它从同一 VisualStateGroup 进入另一种状态时才离开一种状态。”
从我的代码隐藏中,我确保 listBoxItem 仍然是重点。此外,我试图在鼠标离开我的 listBoxItem 后强制进入该状态 - 没有结果(简化:ActiveViewDefinition 是列表框中实际聚焦项目的替代品):
private void It_MouseLeave(object sender, MouseEventArgs e)
{
ListBoxItem lbItem = sender as System.Windows.Controls.ListBoxItem;
if (lbItem != null && lbItem.Content == ActiveViewDefinition)
{
VisualStateManager.GoToState(lbItem, "Focused", true);
}
}
简单。来自不同组的 VisualStates 操作相同元素的相同属性。
您始终需要使用单独的元素(或使用共享元素的单独属性)来指示来自不同组的状态,否则它们自然会相互冲突并弄乱彼此的设置(正如您所观察到的)。
伪xaml中的一个例子:
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"><NoChange/></VisualState>
<VisualState x:Name="MouseOver"><Change What="TheBorder" Color="Red"/></VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"><Change What="TheBorder" Color="Blue"/></VisualState>
</VisualStateGroup>
<Border x:Name="TheBorder" Color="Transparent"/>
最初边框是透明的。
现在,当我们将鼠标移到 Border 上时会发生什么?
mouseOver 状态将其更改为红色。现在我们单击我们的控件(假设它被选中并且可以聚焦)。 focusState 将其更改为蓝色。现在我们将鼠标移开它。 normalState 将其更改为初始状态:透明。 focusState 效果丢失。
我们来看看修改后的例子:
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"><NoChange/></VisualState>
<VisualState x:Name="MouseOver"><Change What="TheMouseoverBorder" Color="Red"/></VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"><Change What="TheFocusBorder" Color="Blue"/></VisualState>
</VisualStateGroup>
<Border x:Name="TheFocusBorder" Color="Transparent"/>
<Border x:Name="TheMouseoverBorder" Color="Transparent"/>
最初两个边框都是透明的。
现在当我们将鼠标移到它们上面时会发生什么?
mouseOver 状态将 TheMouseoverBorder 更改为红色。现在我们单击我们的控件(假设它被选中并且可以聚焦)。 focusState 将 TheFocusBorder 更改为蓝色。现在我们将鼠标移开它。 normalState 将 TheMouseoverBorder 改回透明。 focusState 仍然可见,因为 TheFocusBorder 不受其他状态更改的影响并且仍然是蓝色。
[编辑]
如果您绝对必须从不同的 VisualStateGroups 修改同一元素的相同 属性,您可以使用某种具有结果输出 属性 的聚合器,或者使用某种 fallthrough机制.
我可以想到几种可能的解决方案来从不同的 VisualStateGroups 修改相同的前景 属性。您可以尝试一下,看看它们是否有效。
掉线
<VisualState x:Name="MouseOver"><Change What="Outer" ForegroundColor="Red"/></VisualState>
...
<VisualState x:Name="Focused"><Change What="Inner" ForegroundColor="Blue"/></VisualState>
<ContentControl x:Name="Outer">
<ContentControl x:Name="Inner">
...
</ContentControl>
</ContentControl>
理论上:如果两个状态都处于活动状态,则内部状态获胜(重点:蓝色),如果只有鼠标悬停状态处于活动状态,则 属性 未在内部明确设置,将从外部继承.未经测试。试试吧。
聚合器
<VisualState x:Name="MouseOver"><Change What="Aggregator" MouseOverColor="Red"/></VisualState>
...
<VisualState x:Name="Focused"><Change What="Aggregator" FocusedColor="Blue"/></VisualState>
<InvisibleAggregator x:Name="Aggregator"/>
<ContentControl Foreground="{Binding ElementName=Aggregator, Path=EffectiveColor}"/>
我有如下所示的 ListBoxItem 样式,当 listBoxItem 保留在提到的 VisualState 中时,会出现失去 'Focused' 视觉效果的问题。如何强制适当的视觉效果出现在“Focused”ListBoxItem 上?
实现bug的场景:
1) 单击 ListBoxItem -> 它接收焦点和样式(ok)。
2) 将指针移到此 ListBoxItem -> 它仍然聚焦并接收“MouseOver”的样式(它位于单独的 VisualStateGroup“CommonStates”中,如此处建议:https://msdn.microsoft.com/en-us/library/system.windows.controls.listboxitem%28v=vs.95%29.aspx)(确定)。
3) 使鼠标离开 ListBoxItem 的边界 -> 它失去了 MouseOver(ok) 的样式。
4) 现在,ListBoxItem 处于 'CommonStates' 的 'Normal' 状态并且也是 'Focused',但没有 'Focused' 的样式(这是我的问题)。
5) 尝试单击此 ListBoxItem 无效
能不能给点建议,怎么处理?
我正在使用 Silverlight,所以触发器被禁止 4 我。
这是我的风格:
<Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="ContentControl.Foreground" Value="{StaticResource ViewModeButtonForeground}" />
<Setter Property="Focusable" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="RootElement">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="(ContentControl.Foreground)">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonForeground}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonOuterBorder_MouseOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonBackground_MouseOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="(ContentControl.Foreground)">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonForeground_Focused}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonOuterBorder_Focused}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonBackground_Normal}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused"></VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Border x:Name="CheckOuterBorder" CornerRadius="2" BorderThickness="1" BorderBrush="Transparent" Background="Transparent">
</Border>
<Grid x:Name="GridContent" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Width="16" Height="16" VerticalAlignment="Center" HorizontalAlignment="Left" Source="{Binding ImgSource, Mode=OneWay}"/>
<ContentControl x:Name="Content" Grid.Column="1" Margin="3,0,0,0" Foreground="{TemplateBinding Foreground}" Content="{Binding Title}" ContentTemplate="{TemplateBinding ContentTemplate}" VerticalAlignment="Center" Width="Auto"/>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ViewModeSelectionListBoxStyle" TargetType="ListBox">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="IsTabStop" Value="True" />
<Setter Property="ItemContainerStyle" Value="{StaticResource ListBoxItemStyle1}" />
<Setter Property="Focusable" Value="True" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
如果我将我的 VisualStateGroup 从“FocusStates”更改为 SelectionStates,它的行为是一样的。我希望它与焦点而不是选择相关联,因为如果用户单击某处并且另一个控件也将具有类似的边框,则 UI 中可能存在视觉不匹配。
顺便说一句:我在另一个 msdn 网站上读到: “对于在其 ControlTemplate 中定义的每个 VisualStateGroup,该控件始终处于一种状态,并且仅当它从同一 VisualStateGroup 进入另一种状态时才离开一种状态。” 从我的代码隐藏中,我确保 listBoxItem 仍然是重点。此外,我试图在鼠标离开我的 listBoxItem 后强制进入该状态 - 没有结果(简化:ActiveViewDefinition 是列表框中实际聚焦项目的替代品):
private void It_MouseLeave(object sender, MouseEventArgs e)
{
ListBoxItem lbItem = sender as System.Windows.Controls.ListBoxItem;
if (lbItem != null && lbItem.Content == ActiveViewDefinition)
{
VisualStateManager.GoToState(lbItem, "Focused", true);
}
}
简单。来自不同组的 VisualStates 操作相同元素的相同属性。 您始终需要使用单独的元素(或使用共享元素的单独属性)来指示来自不同组的状态,否则它们自然会相互冲突并弄乱彼此的设置(正如您所观察到的)。
伪xaml中的一个例子:
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"><NoChange/></VisualState>
<VisualState x:Name="MouseOver"><Change What="TheBorder" Color="Red"/></VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"><Change What="TheBorder" Color="Blue"/></VisualState>
</VisualStateGroup>
<Border x:Name="TheBorder" Color="Transparent"/>
最初边框是透明的。 现在,当我们将鼠标移到 Border 上时会发生什么? mouseOver 状态将其更改为红色。现在我们单击我们的控件(假设它被选中并且可以聚焦)。 focusState 将其更改为蓝色。现在我们将鼠标移开它。 normalState 将其更改为初始状态:透明。 focusState 效果丢失。
我们来看看修改后的例子:
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"><NoChange/></VisualState>
<VisualState x:Name="MouseOver"><Change What="TheMouseoverBorder" Color="Red"/></VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"><Change What="TheFocusBorder" Color="Blue"/></VisualState>
</VisualStateGroup>
<Border x:Name="TheFocusBorder" Color="Transparent"/>
<Border x:Name="TheMouseoverBorder" Color="Transparent"/>
最初两个边框都是透明的。 现在当我们将鼠标移到它们上面时会发生什么? mouseOver 状态将 TheMouseoverBorder 更改为红色。现在我们单击我们的控件(假设它被选中并且可以聚焦)。 focusState 将 TheFocusBorder 更改为蓝色。现在我们将鼠标移开它。 normalState 将 TheMouseoverBorder 改回透明。 focusState 仍然可见,因为 TheFocusBorder 不受其他状态更改的影响并且仍然是蓝色。
[编辑]
如果您绝对必须从不同的 VisualStateGroups 修改同一元素的相同 属性,您可以使用某种具有结果输出 属性 的聚合器,或者使用某种 fallthrough机制.
我可以想到几种可能的解决方案来从不同的 VisualStateGroups 修改相同的前景 属性。您可以尝试一下,看看它们是否有效。
掉线
<VisualState x:Name="MouseOver"><Change What="Outer" ForegroundColor="Red"/></VisualState>
...
<VisualState x:Name="Focused"><Change What="Inner" ForegroundColor="Blue"/></VisualState>
<ContentControl x:Name="Outer">
<ContentControl x:Name="Inner">
...
</ContentControl>
</ContentControl>
理论上:如果两个状态都处于活动状态,则内部状态获胜(重点:蓝色),如果只有鼠标悬停状态处于活动状态,则 属性 未在内部明确设置,将从外部继承.未经测试。试试吧。
聚合器
<VisualState x:Name="MouseOver"><Change What="Aggregator" MouseOverColor="Red"/></VisualState>
...
<VisualState x:Name="Focused"><Change What="Aggregator" FocusedColor="Blue"/></VisualState>
<InvisibleAggregator x:Name="Aggregator"/>
<ContentControl Foreground="{Binding ElementName=Aggregator, Path=EffectiveColor}"/>