XAML 样式/基础 class 中的 VisualStateGroup/VisualState 的继承
Inheritance of VisualStateGroup / VisualState in a XAML Style / base class
- 我有一个继承自
UserControl
的基础 class SecurePage
。
- 应用程序中的每个 'page' 都继承自
SecurePage
。
- 我想在
SecurePage
的默认 Style
中定义一个带有一些 VisualState
的 VisualStateGroup
。
问题是,在派生的 class 中,有 none 个 VisualState
可用。
var states = VisualStateManager.GetVisualStateGroups(this);
Returns 一个空列表。
如果我复制我的 XAML
VisualState
定义并将其粘贴到我的 DerivadedFooSecurePage
中,我可以轻松地进入状态:
VisualStateManager.GoToState(this, "Blink", false);
与此处描述的问题相同:VisualState in abstract control
更多细节
安全页面
[TemplateVisualState(GroupName = "State", Name = "Normal")]
[TemplateVisualState(GroupName = "State", Name = "Blink")]
public class SecurePage: UserControl
{
public SecurePage()
{
DefaultStyleKey = typeof(HtSecurePage);
}
}
<Style TargetType="basic:SecurePage">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="basic:SecurePage">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Signals">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Blink">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0:0:0.4" Value="#FF3AFF00">
<EasingColorKeyFrame.EasingFunction>
<BounceEase EasingMode="EaseIn" Bounciness="3" Bounces="4"/>
</EasingColorKeyFrame.EasingFunction>
</EasingColorKeyFrame>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter Content="{TemplateBinding Content}"/>
<Border
x:Name="border"
BorderThickness="5"
BorderBrush="Transparent"
IsHitTestVisible="False"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
信息页
Info.xaml.cs
namespace Views.General
{
public partial class Info
{
public Info()
{
InitializeComponent();
}
}
}
Info.xaml
<basic:SecurePage
x:Class="Views.General.Info"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:basic="clr-namespace:Foo.PlcFramework.Controls.Basic;assembly=Foo"
FontSize="14">
<Grid>
<TextBlock Text="HelloWorld"/>
</Grid>
</basic:SecurePage>
实时调试
states = 0
- 调用后没有任何反应
VisualStateManager.GoToState(this, "Blink", false);
states = 0
- 调用后没有任何反应
VisualStateManager.GoToState(this, "Blink", false);
将 VisualState 复制到派生的 class
namespace Views.General
{
[TemplateVisualState(GroupName = "State", Name = "Normal")]
[TemplateVisualState(GroupName = "State", Name = "Blink")]
public partial class Info
{
public Info()
{
InitializeComponent();
var states = VisualStateManager.GetVisualStateGroups(this);
VisualStateManager.GoToState(this, "Blink", false);
}
}
}
<basic:SecurePage
x:Class="Views.General.Info"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:basic="clr-namespace:Foo.PlcFramework.Controls.Basic;assembly=Foo"
FontSize="14">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Signals">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Blink">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0:0:0.4" Value="#FF3AFF00">
<EasingColorKeyFrame.EasingFunction>
<BounceEase EasingMode="EaseIn" Bounciness="3" Bounces="4"/>
</EasingColorKeyFrame.EasingFunction>
</EasingColorKeyFrame>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock Text="HelloWorld"/>
<Border
x:Name="border"
BorderThickness="5"
BorderBrush="Transparent"
IsHitTestVisible="False"/>
</Grid>
</basic:SecurePage >
states = 0
- 调用后
VisualStateManager.GoToState(this, "Blink", false);
状态改变了!!
我只是想在SecurePage
的XAML Style
定义中定义状态,然后去任何派生的状态class!
我厌倦了重新生成你提到的场景,我能够从基础 class 中获得 VisualStateGroup
的列表。这是我实现的示例。
首先,您需要获取当前基础 class 的 VisualStateGroup
,然后将自定义创建的组添加到其中。
基础Class
public partial class SecurePage : UserControl
{
public VisualStateGroup vsGroup = new VisualStateGroup();
public SecurePage()
{
System.Collections.IList groups = VisualStateManager.GetVisualStateGroups(this);
VisualState state1 = new VisualState() { Name = "State1" };
Storyboard sb = new Storyboard();
DoubleAnimation anim = new DoubleAnimation(1, 0, TimeSpan.FromSeconds(1.0));
Storyboard.SetTargetProperty(anim, new PropertyPath(FrameworkElement.OpacityProperty));
sb.AutoReverse = true;
sb.Children.Add(anim);
state1.Storyboard = sb;
vsGroup.States.Add(state1);
groups.Add(vsGroup);
}
}
派生Class
派生的根 class 是基 class 的类型。请记住一定要改变它。 XAML 文件的第一行。检查下面。
<UserControl x:Class="WPFTest.SecurePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Rectangle x:Name="rect" Height="100" Width="100" Fill="Red" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</UserControl>
public partial class DerivadedFooSecurePage : SecurePage
{
public DerivadedFooSecurePage()
{
InitializeComponent();
var states = VisualStateManager.GetVisualStateGroups(this);
//VisualStateManager.GoToElementState(this.rect, "State1", false);
}
}
进一步可以在任何 window 或其他用户控件上实现 DerivadedFooSecurePage
。
解决方案
我定义了一个 enum
,每个可用的 VisualState
都有一个值。
public enum ESecurePageVisualState
{
Normal,
Blink
}
在 SecurePage
class 中添加了一个 DependencyProperty
。
/// <summary>
/// Set the visual State of the page
/// </summary>
public ESecurePageVisualState VisualState
{
get => (ESecurePageVisualState)GetValue(VisualStateProperty);
set => SetValue(VisualStateProperty, value);
}
/// <summary>
/// The <see cref="VisualState"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty VisualStateProperty = DependencyProperty.Register("VisualState", typeof(ESecurePageVisualState), typeof(SecurePage), new PropertyMetadata(ESecurePageVisualState.Normal));
并更改了SecurePage
的Style
:
<Style TargetType="basic:SecurePage">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="basic:SecurePage">
<Grid x:Name="Grid">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Signals">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Blink">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0:0:0.4" Value="#FF3AFF00">
<EasingColorKeyFrame.EasingFunction>
<BounceEase EasingMode="EaseIn" Bounciness="3" Bounces="4"/>
</EasingColorKeyFrame.EasingFunction>
</EasingColorKeyFrame>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter Content="{TemplateBinding Content}"/>
<Border
x:Name="border"
BorderThickness="5"
BorderBrush="Transparent"
IsHitTestVisible="False"/>
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=basic:SecurePage}, Path=VisualState}" Value="{x:Static basic:ESecurePageVisualState.Normal}">
<ei:GoToStateAction StateName="Normal" TargetName="Grid" TargetObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}"/>
</ei:DataTrigger>
<ei:DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=basic:SecurePage}, Path=VisualState}" Value="{x:Static basic:ESecurePageVisualState.Blink}">
<ei:GoToStateAction StateName="Blink" TargetName="Grid" TargetObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}"/>
</ei:DataTrigger>
</i:Interaction.Triggers>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
现在我可以通过 属性 更改 VisualState
。
public partial class Info
{
public Info()
{
InitializeComponent();
Loaded += (sender, args) =>
{
VisualState = ESecurePageVisualState.Blink;
};
}
}
如果您找到更好的解决方案,请告诉我!
诊断
经过一番摸索,我终于找到了罪魁祸首 - 它是 UserControl
本身。更准确地说 - overridden StateGroupsRoot
property,它被 VisualStateManager.GoToState
方法使用。通常,它 return 是控件模板的根元素,但在 UserControl
的情况下,它 return 是 UserControl.Content
属性 的值。因此,当您调用 GoToState
时,模板中定义的状态不会被考虑在内。
解决方案
这个问题至少有两个解决方案。
第一个解决方案 是从ContentControl
而不是UserControl
派生基数class (SecurePage
)。后者并没有太大不同——它将 Focusable
和 IsTabStop
属性默认为 false
,而 HorizontanContentAlignment
和 VerticalContentAlignment
属性默认为 Stretch
。此外,除了覆盖提到的 StateGroupsRoot
属性 之外,它还提供了自己的 AutomationPeer
(UserControlAutomationPeer
),但我认为您不必为此担心。
第二种解决方案 是在模板根目录上使用VisualStateManager.GoToElementState
。例如:
public class SecurePage : UserControl
{
//Your code here...
private FrameworkElement TemplateRoot { get; set; }
public override void OnApplyTemplate()
{
if (Template != null)
TemplateRoot = GetVisualChild(0) as FrameworkElement;
else
TemplateRoot = null;
}
public bool GoToVisualState(string name, bool useTransitions)
{
if (TemplateRoot is null)
return false;
return VisualStateManager.GoToElementState(TemplateRoot, name, useTransitions);
}
}
其他注意事项
在你的控件上调用 VisualStateManager.GetVisualStateGroups
会产生一个空列表,因为它只是一个普通的附加依赖 属性 访问器,而你没有设置 1 属性 由您控制。要掌握您在模板中定义的组,您应该将模板根作为参数传递给它。根据同样的原则,您不希望 Grid.GetColumn
调用您的控件来 return 您在模板中某处设置的值。
关于在您的控件的构造函数中调用 GoToState
- 它很可能不会起作用,因为您的控件只是 被实例化 ,并且很可能是它的 Templtate
为 null(请记住,您在模板中定义了视觉状态)。最好将该逻辑移动到 OnApplyTemplate
覆盖。
1 由于 VisualStateManager.VisualStateGroupsProperty
是只读的,通过 set 我的意思是将项目添加到列表 return由VisualStateManager.GetVisualStateGroups
编辑
- 我有一个继承自
UserControl
的基础 classSecurePage
。 - 应用程序中的每个 'page' 都继承自
SecurePage
。 - 我想在
SecurePage
的默认Style
中定义一个带有一些VisualState
的VisualStateGroup
。
问题是,在派生的 class 中,有 none 个 VisualState
可用。
var states = VisualStateManager.GetVisualStateGroups(this);
Returns 一个空列表。
如果我复制我的 XAML
VisualState
定义并将其粘贴到我的 DerivadedFooSecurePage
中,我可以轻松地进入状态:
VisualStateManager.GoToState(this, "Blink", false);
与此处描述的问题相同:VisualState in abstract control
更多细节
安全页面
[TemplateVisualState(GroupName = "State", Name = "Normal")]
[TemplateVisualState(GroupName = "State", Name = "Blink")]
public class SecurePage: UserControl
{
public SecurePage()
{
DefaultStyleKey = typeof(HtSecurePage);
}
}
<Style TargetType="basic:SecurePage">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="basic:SecurePage">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Signals">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Blink">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0:0:0.4" Value="#FF3AFF00">
<EasingColorKeyFrame.EasingFunction>
<BounceEase EasingMode="EaseIn" Bounciness="3" Bounces="4"/>
</EasingColorKeyFrame.EasingFunction>
</EasingColorKeyFrame>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter Content="{TemplateBinding Content}"/>
<Border
x:Name="border"
BorderThickness="5"
BorderBrush="Transparent"
IsHitTestVisible="False"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
信息页
Info.xaml.cs
namespace Views.General
{
public partial class Info
{
public Info()
{
InitializeComponent();
}
}
}
Info.xaml
<basic:SecurePage
x:Class="Views.General.Info"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:basic="clr-namespace:Foo.PlcFramework.Controls.Basic;assembly=Foo"
FontSize="14">
<Grid>
<TextBlock Text="HelloWorld"/>
</Grid>
</basic:SecurePage>
实时调试
states = 0
- 调用后没有任何反应
VisualStateManager.GoToState(this, "Blink", false);
states = 0
- 调用后没有任何反应
VisualStateManager.GoToState(this, "Blink", false);
将 VisualState 复制到派生的 class
namespace Views.General
{
[TemplateVisualState(GroupName = "State", Name = "Normal")]
[TemplateVisualState(GroupName = "State", Name = "Blink")]
public partial class Info
{
public Info()
{
InitializeComponent();
var states = VisualStateManager.GetVisualStateGroups(this);
VisualStateManager.GoToState(this, "Blink", false);
}
}
}
<basic:SecurePage
x:Class="Views.General.Info"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:basic="clr-namespace:Foo.PlcFramework.Controls.Basic;assembly=Foo"
FontSize="14">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Signals">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Blink">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0:0:0.4" Value="#FF3AFF00">
<EasingColorKeyFrame.EasingFunction>
<BounceEase EasingMode="EaseIn" Bounciness="3" Bounces="4"/>
</EasingColorKeyFrame.EasingFunction>
</EasingColorKeyFrame>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock Text="HelloWorld"/>
<Border
x:Name="border"
BorderThickness="5"
BorderBrush="Transparent"
IsHitTestVisible="False"/>
</Grid>
</basic:SecurePage >
states = 0
- 调用后
VisualStateManager.GoToState(this, "Blink", false);
状态改变了!!
我只是想在SecurePage
的XAML Style
定义中定义状态,然后去任何派生的状态class!
我厌倦了重新生成你提到的场景,我能够从基础 class 中获得 VisualStateGroup
的列表。这是我实现的示例。
首先,您需要获取当前基础 class 的 VisualStateGroup
,然后将自定义创建的组添加到其中。
基础Class
public partial class SecurePage : UserControl
{
public VisualStateGroup vsGroup = new VisualStateGroup();
public SecurePage()
{
System.Collections.IList groups = VisualStateManager.GetVisualStateGroups(this);
VisualState state1 = new VisualState() { Name = "State1" };
Storyboard sb = new Storyboard();
DoubleAnimation anim = new DoubleAnimation(1, 0, TimeSpan.FromSeconds(1.0));
Storyboard.SetTargetProperty(anim, new PropertyPath(FrameworkElement.OpacityProperty));
sb.AutoReverse = true;
sb.Children.Add(anim);
state1.Storyboard = sb;
vsGroup.States.Add(state1);
groups.Add(vsGroup);
}
}
派生Class
派生的根 class 是基 class 的类型。请记住一定要改变它。 XAML 文件的第一行。检查下面。
<UserControl x:Class="WPFTest.SecurePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Rectangle x:Name="rect" Height="100" Width="100" Fill="Red" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</UserControl>
public partial class DerivadedFooSecurePage : SecurePage
{
public DerivadedFooSecurePage()
{
InitializeComponent();
var states = VisualStateManager.GetVisualStateGroups(this);
//VisualStateManager.GoToElementState(this.rect, "State1", false);
}
}
进一步可以在任何 window 或其他用户控件上实现 DerivadedFooSecurePage
。
解决方案
我定义了一个 enum
,每个可用的 VisualState
都有一个值。
public enum ESecurePageVisualState
{
Normal,
Blink
}
在 SecurePage
class 中添加了一个 DependencyProperty
。
/// <summary>
/// Set the visual State of the page
/// </summary>
public ESecurePageVisualState VisualState
{
get => (ESecurePageVisualState)GetValue(VisualStateProperty);
set => SetValue(VisualStateProperty, value);
}
/// <summary>
/// The <see cref="VisualState"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty VisualStateProperty = DependencyProperty.Register("VisualState", typeof(ESecurePageVisualState), typeof(SecurePage), new PropertyMetadata(ESecurePageVisualState.Normal));
并更改了SecurePage
的Style
:
<Style TargetType="basic:SecurePage">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="basic:SecurePage">
<Grid x:Name="Grid">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Signals">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Blink">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0:0:0.4" Value="#FF3AFF00">
<EasingColorKeyFrame.EasingFunction>
<BounceEase EasingMode="EaseIn" Bounciness="3" Bounces="4"/>
</EasingColorKeyFrame.EasingFunction>
</EasingColorKeyFrame>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter Content="{TemplateBinding Content}"/>
<Border
x:Name="border"
BorderThickness="5"
BorderBrush="Transparent"
IsHitTestVisible="False"/>
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=basic:SecurePage}, Path=VisualState}" Value="{x:Static basic:ESecurePageVisualState.Normal}">
<ei:GoToStateAction StateName="Normal" TargetName="Grid" TargetObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}"/>
</ei:DataTrigger>
<ei:DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=basic:SecurePage}, Path=VisualState}" Value="{x:Static basic:ESecurePageVisualState.Blink}">
<ei:GoToStateAction StateName="Blink" TargetName="Grid" TargetObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}"/>
</ei:DataTrigger>
</i:Interaction.Triggers>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
现在我可以通过 属性 更改 VisualState
。
public partial class Info
{
public Info()
{
InitializeComponent();
Loaded += (sender, args) =>
{
VisualState = ESecurePageVisualState.Blink;
};
}
}
如果您找到更好的解决方案,请告诉我!
诊断
经过一番摸索,我终于找到了罪魁祸首 - 它是 UserControl
本身。更准确地说 - overridden StateGroupsRoot
property,它被 VisualStateManager.GoToState
方法使用。通常,它 return 是控件模板的根元素,但在 UserControl
的情况下,它 return 是 UserControl.Content
属性 的值。因此,当您调用 GoToState
时,模板中定义的状态不会被考虑在内。
解决方案
这个问题至少有两个解决方案。
第一个解决方案 是从ContentControl
而不是UserControl
派生基数class (SecurePage
)。后者并没有太大不同——它将 Focusable
和 IsTabStop
属性默认为 false
,而 HorizontanContentAlignment
和 VerticalContentAlignment
属性默认为 Stretch
。此外,除了覆盖提到的 StateGroupsRoot
属性 之外,它还提供了自己的 AutomationPeer
(UserControlAutomationPeer
),但我认为您不必为此担心。
第二种解决方案 是在模板根目录上使用VisualStateManager.GoToElementState
。例如:
public class SecurePage : UserControl
{
//Your code here...
private FrameworkElement TemplateRoot { get; set; }
public override void OnApplyTemplate()
{
if (Template != null)
TemplateRoot = GetVisualChild(0) as FrameworkElement;
else
TemplateRoot = null;
}
public bool GoToVisualState(string name, bool useTransitions)
{
if (TemplateRoot is null)
return false;
return VisualStateManager.GoToElementState(TemplateRoot, name, useTransitions);
}
}
其他注意事项
在你的控件上调用 VisualStateManager.GetVisualStateGroups
会产生一个空列表,因为它只是一个普通的附加依赖 属性 访问器,而你没有设置 1 属性 由您控制。要掌握您在模板中定义的组,您应该将模板根作为参数传递给它。根据同样的原则,您不希望 Grid.GetColumn
调用您的控件来 return 您在模板中某处设置的值。
关于在您的控件的构造函数中调用 GoToState
- 它很可能不会起作用,因为您的控件只是 被实例化 ,并且很可能是它的 Templtate
为 null(请记住,您在模板中定义了视觉状态)。最好将该逻辑移动到 OnApplyTemplate
覆盖。
1 由于 VisualStateManager.VisualStateGroupsProperty
是只读的,通过 set 我的意思是将项目添加到列表 return由VisualStateManager.GetVisualStateGroups
编辑