关于VisualStateGroup
有一些基本的东西我不理解。我读过的所有东西都让我相信它们是正交的。也就是说,一个群体的状态变化不会影响其他群体。事实上,如果情况并非如此,那么他们就会毫无意义。VisualStateGroup在另一个VisualStateGroup中触发动画
但是,为了试图理解我遇到的一些奇怪的行为,我将一个简单的例子说明了一个组中的状态变化可以触发另一个组中的动画。我试图理解这可能是怎么回事。
我要的是一个基于ToggleButton
控制以具有一个外观切换时(IsChecked == true
),无论其是否已经聚焦或鼠标是否已经结束它,例如。
首先,我有一个非常简单的控制,在自定义组自定义的状态之间的转换:
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
[TemplateVisualState(Name = normalState, GroupName = activationGroupName)]
[TemplateVisualState(Name = hoverState, GroupName = activationGroupName)]
[TemplateVisualState(Name = activatedState, GroupName = activationGroupName)]
public class CustomControl : ToggleButton
{
private const string activationGroupName = "Activation";
private const string normalState = "Normal";
private const string hoverState = "Hover";
private const string activatedState = "Activated";
public CustomControl()
{
this.DefaultStyleKey = typeof(CustomControl);
this.Checked += delegate
{
this.UpdateStates();
};
this.Unchecked += delegate
{
this.UpdateStates();
};
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.UpdateStates();
}
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
this.UpdateStates();
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
this.UpdateStates();
}
private void UpdateStates()
{
var state = normalState;
if (this.IsChecked.HasValue && this.IsChecked.Value)
{
state = activatedState;
}
else if (this.IsMouseOver)
{
state = hoverState;
}
VisualStateManager.GoToState(this, state, true);
}
}
其次,我对这个控制,改变基于当前状态的背景颜色的模板:
<Style TargetType="local:CustomControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomControl">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Activation">
<VisualStateGroup.Transitions>
<VisualTransition To="Normal" GeneratedDuration="00:00:0.2"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="Hover">
<Storyboard>
<ColorAnimation Duration="00:00:0.2" To="Red" Storyboard.TargetName="grid" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Activated">
<Storyboard>
<ColorAnimation Duration="00:00:0.2" To="Blue" Storyboard.TargetName="grid" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="grid" Background="White">
<TextBlock>ToggleButton that should be white normally, red on hover, and blue when checked</TextBlock>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
它的大部分工作原理,但是当控件失去焦点时,它将转换回正常状态。
我能解决这个问题的明确处理焦点的变化在我的控制,并制定一个状态更新:
public CustomControl()
{
this.DefaultStyleKey = typeof(CustomControl);
this.Checked += delegate
{
this.UpdateStates();
};
this.Unchecked += delegate
{
this.UpdateStates();
};
// explicitly handle focus changes
this.GotFocus += delegate
{
this.UpdateStates();
};
this.LostFocus += delegate
{
this.UpdateStates();
};
}
然而,这是没有意义的我,为什么我必须这样做。
为什么一个VisualStateGroup
中的状态改变会导致由另一个定义的动画执行?对我来说,实现既定目标的最简单,最正确的方法是什么?
感谢
感谢安东尼。我实际上尝试使用一个独特的国家名称 - 我称之为'Normal2' - 在发布我的问题后,可以确认它的工作。在我看来,事情仍然“破碎”,因为我不能使用内置状态,因为底层控件(在这种情况下为“ToggleButton”)正在切换状态,而没有任何拦截这些更改的能力。因此,试图使用内置状态会导致视觉异常,因为底层控制开关在我不想要时会显示状态。 – 2010-07-13 18:19:02
这是一种令Silverlight在样式中不允许触发器的痛苦。这样就不需要VSM了。肯特,我有完全相同的目标来实现与ToggleButton,并想知道你是否最终实现了你的目标,因为你的上一篇文章? – Houman 2011-05-05 06:46:40
@Kave:也许我偏于WPF之前在Silverlight中开发,但我更喜欢VSM模型来触发自己。它提供了一个抽象层,比Xaml中描述的用户界面与特定事件紧密集成更有意义。不过,我可以看到触发器对WPF开发者的灵活性的损失会令人沮丧。 – AnthonyWJones 2011-05-05 07:21:31