SecurePage
,它继承自UserControl
。 SecurePage
。 Style
的默认SecurePage
中定义一个VisualStateGroup
,其中包含一些VisualState
。 问题是,在派生类中,没有这些VisualState
可用。
var states = VisualStateManager.GetVisualStateGroups(this);
返回一个空列表。
如果我复制XAML
VisualState
定义并将其粘贴到DerivadedFooSecurePage
中,则可以轻松进入状态:
VisualStateManager.GoToState(this, "Blink", false);
与此处描述的问题相同:VisualState in abstract control
SecurePage
[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);
后没有任何反应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);
后,状态已更改! 我只想在XAML Style
的{{1}}定义中定义状态,然后进入任何衍生类的状态!
答案 0 :(得分:1)
经过一番摸索,我终于找到了罪魁祸首-它是UserControl
本身。更准确地说-VisualStateManager.GoToState
方法使用的overridden StateGroupsRoot
property。通常,它返回控件模板的根元素,但是在UserControl
的情况下,它返回UserControl.Content
属性的值。因此,发生的情况是,当您调用GoToState
时,模板中定义的状态将不被考虑。
对此问题至少有两种解决方案。
第一个解决方案是从SecurePage
而不是ContentControl
派生您的基类(UserControl
)。后者没什么不同-默认将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
返回在模板中某个位置设置的值。
关于在控件的构造函数中调用GoToState
的情况-它很可能无法正常工作,因为您的控件仅被实例化,并且很可能其Templtate
为null(并记住您在模板中定义了视觉状态)。最好将逻辑移至OnApplyTemplate
覆盖。
1 因为VisualStateManager.VisualStateGroupsProperty
是只读的,所以通过 set 我的意思是将项目添加到VisualStateManager.GetVisualStateGroups
返回的列表中
答案 1 :(得分:0)
我很累重新生成您所提到的方案,并且能够从基类中获取VisualStateGroup
的列表。这是我实现的示例。
首先,您需要获取当前基类的VisualStateGroup
,然后向其添加自定义创建的组。
基本类
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);
}
}
派生类
派生类的根是基类的类型。请记住要毫无改变地进行更改。 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);
}
}
进一步可以在任何窗口或其他用户控件上实现DerivadedFooSecurePage
。
答案 2 :(得分:0)
我定义了一个enum
,其中每个可用的VisualState
都有一个值。
public enum ESecurePageVisualState
{
Normal,
Blink
}
在DependencyProperty
类中添加了SecurePage
。
/// <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));
并更改了Style
中的SecurePage
:
<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;
};
}
}
如果您找到更好的解决方案,请告诉我!