c# - XAML 样式/基类中 VisualStateGroup/VisualState 的继承

标签 c# wpf xaml expression-blend visualstatemanager

  • 我有一个继承自UserControl的基类SecurePage
  • 应用内的每个“页面”都继承自 SecurePage
  • 我想在 SecurePage 的默认 Style 中定义一个带有一些 VisualStateVisualStateGroup

问题是,在派生类中,这些 VisualState 都不可用。

var states = VisualStateManager.GetVisualStateGroups(this);

返回一个空列表。

如果我复制 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();
        }
    }
}

信息.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>

实时调试

enter image description here

  • 状态 = 0
  • 调用 VisualStateManager.GoToState(this, "Blink", false); 后没有任何反应

enter image description here

  • 状态 = 0
  • 调用 VisualStateManager.GoToState(this, "Blink", false); 后没有任何反应

将 VisualState 复制到派生类中

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 >

enter image description here

  • 状态 = 0
  • 调用 VisualStateManager.GoToState(this, "Blink", false); 后状态发生更改!!

我只想在 SecurePageXAML 样式 定义中定义状态,并转到任何派生类中的状态!

最佳答案

诊断

经过一番摸索,我终于找到了罪魁祸首 - 它是 UserControl本身。更准确地说 - overridden StateGroupsRoot property ,由 VisualStateManager.GoToState 使用方法。通常,它返回控件模板的根元素,但在 UserControl 的情况下它返回值 UserControl.Content属性(property)。那么当你调用 GoToState 时会发生什么? ,不考虑模板中定义的状态。

解决方案

这个问题至少有两种解决方案。

第一个解决方案是从 SecurePage 派生基类 ( ContentControl )而不是UserControl 。后者没有太大不同 - 它默认 FocusableIsTabStop属性为false ,和HorizontanContentAlignmentVerticalContentAlignmentStretch 。另外,除了提到的重写 StateGroupsRoot属性,它提供自己的AutomationPeer (a 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覆盖。


1VisualStateManager.VisualStateGroupsProperty是只读的,通过 set 我的意思是将项目添加到 VisualStateManager.GetVisualStateGroups 返回的列表中

关于c# - XAML 样式/基类中 VisualStateGroup/VisualState 的继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51246313/

相关文章:

c# - 具有类约束的字段

c# - 简单的 C# Excel 加载项功能区按钮为单元格着色

c# - Task.ContinueWith 方法需要任务参数吗?

c# - 传递用于访问属性的 Lambda 表达式列表

c# - DataGrid 的意外行为

c# - 输入验证 Silverlight

c# - 在文本 block 中使用具有某些属性的 <Run Text={Binding ...}/> 时应用程序崩溃

wpf - 使用 VisualStateManager 动画 MVVM ViewModel 转换——动画未运行

wpf - 以编程方式更改标签属性

wpf - 在WPF中创建一个不错的GUI