【发布时间】:2010-07-12 18:02:14
【问题描述】:
VisualStateGroups 有一些基本的东西我不理解。我读过的所有内容都让我相信它们是正交的。也就是说,一个组中的状态更改不会影响其他组。事实上,如果不是这种情况,它们将毫无意义。
但是,为了了解我遇到的一些奇怪行为,我整理了一个简单的示例,说明一个组中的状态变化可以触发另一个组中的动画。我试图了解这是怎么回事。
我只想要一个基于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 的状态变化会导致另一个定义的动画执行?我实现既定目标的最简单、最正确的方法是什么?
谢谢
【问题讨论】:
标签: silverlight silverlight-4.0 visualstatemanager visualstategroup