代码之家  ›  专栏  ›  技术社区  ›  Dominic Jonas

在XAML样式/基类中继承VisualStateGroup/VisualState

  •  2
  • Dominic Jonas  · 技术社区  · 7 年前
    • 我有一个基础班 SecurePage 继承自 UserControl .
    • 应用程序中的每个“页面”都继承自 安全页面 .
    • 我想在默认值中定义 Style 属于 安全页面 VisualStateGroup 有一些 VisualState S.

    问题是,在派生类中,这些都不是 视觉状态 S可用。

    var states = VisualStateManager.GetVisualStateGroups(this);

    返回空列表。

    如果我复制我的 XAML 视觉状态 定义并粘贴到我的 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>
    

    信息页

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

    • states = 0
    • 打电话后什么都没发生 VisualStateManager.GoToState(this, "Blink", false);

    enter image description here

    • 状态=0
    • 打电话后什么都没发生 visualstatemanager.gotostate(这是“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(这是“blink”,false); 状态已更改!!

    我只想定义 XAML Style 定义 安全页面 并在任何派生类中进入状态!

    3 回复  |  直到 6 年前
        1
  •  1
  •   Grx70    6 年前

    诊断

    经过一番周旋,我终于找到了罪魁祸首——是 UserControl 本身。更准确地说- overridden StateGroupsRoot property ,由 VisualStateManager.GoToState 方法。通常,它返回控件模板的根元素,但在 用户控件 它返回 UserControl.Content 属性。所以当你打电话的时候 GoToState ,不考虑模板中定义的状态。

    解决方案

    这个问题至少有两种解决办法。

    第一个解决方案 是派生基类( SecurePage ContentControl 而不是 用户控件 . 后者没什么不同——它是默认的 Focusable IsTabStop 属性到 false HorizontanContentAlignment VerticalContentAlignment Stretch . 此外,除了提到的覆盖 状态组根目录 属性,它提供自己的 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 在控件上生成一个空列表,因为它只是一个普通的附加依赖属性访问器,而您没有设置 你控制的财产。若要保留在模板中定义的组,应将模板根作为参数传递给它。按照同样的原则,你不会期望 Grid.GetColumn 调用控件以返回在模板中某处设置的值。

    关于呼叫 哥斯达特 在控件的构造函数中-它很可能不会工作,因为控件 正在实例化 最有可能的是 Templtate 为空(请记住您在模板内定义了可视状态)。最好把逻辑转移到 OnApplyTemplate 覆盖。


    自从 VisualStateManager.VisualStateGroupsProperty 是只读的,由 设置 我的意思是在返回的列表中添加项目 VisualStateManager.GetVisualStateGroups

        2
  •  0
  •   Gaurang Dave    7 年前

    我厌倦了重新生成你提到的场景,我能够得到 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 在任何窗口或其他用户控件上。

        3
  •  0
  •   Dominic Jonas    7 年前

    解决方案

    我定义了一个 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 安全页面 :

    <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>
    

    现在我可以改变 视觉状态 通过财产。

    public partial class Info
    {
        public Info()
        {
            InitializeComponent();
            Loaded += (sender, args) =>
            {
                VisualState = ESecurePageVisualState.Blink;
            };
        }       
    }
    

    如果你找到更好的解决方案,请告诉我!