将情节提要的目标设置为 DataTemplate WPF 中的按钮

Posted

技术标签:

【中文标题】将情节提要的目标设置为 DataTemplate WPF 中的按钮【英文标题】:Set Target of Storyboard to Button inside a DataTemplate WPF 【发布时间】:2021-05-24 14:48:35 【问题描述】:

我们正在尝试根据窗口大小设置按钮的高度和宽度。我们希望使用视觉状态来实现这一点。我们想将 Storyboard Target 设置为 DataTemplate 内的按钮。由于 DataTemplate 的名称范围,无法使用 StoryBoard.TargetName 直接按名称设置目标。在 XAML 中有什么方法可以做到这一点。

参考-

XAML:

<UserControl x:Class="ResizeSampleApp.Controls.ResizeUserControl"
             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:ResizeSampleApp.Controls"
             mc:Ignorable="d" 
             SizeChanged="CurrentWindow_SizeChanged"
             x:Name="DashBoard"
             >


    <Grid x:Name="grid">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="ResizeStates">
                <VisualState x:Name="FirstHorizontalBreakpoint">
                    <Storyboard >
                        <DoubleAnimation To="116" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height"></DoubleAnimation>
                        <DoubleAnimation To="182" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width"></DoubleAnimation>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="MinSize">
                    <Storyboard>
                        <DoubleAnimation To="100" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height"></DoubleAnimation>
                        <DoubleAnimation To="197" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width"></DoubleAnimation>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <StackPanel Orientation="Vertical">

            <Grid Margin="10">
                <ItemsControl Name="icTodoList">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Button x:Name="TargetBtn" Content="Binding Title" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Grid>
        </StackPanel>
    </Grid>
</UserControl>

C#:

public class TodoItem
    
        public string Title  get; set; 
       
    
    public partial class ResizeUserControl : UserControl
    
        public ResizeUserControl()
        
            InitializeComponent();

            List<TodoItem> items = new List<TodoItem>();
            items.Add(new TodoItem()  Title = "Testing 1 " );
            items.Add(new TodoItem()  Title = "Testing 2" );
            items.Add(new TodoItem()  Title = "Testing 3" );

            icTodoList.ItemsSource = items;


        
        public void CurrentWindow_SizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
        
            

            if (this.ActualWidth > 847)
             
                VisualStateManager.GoToState(this.DashBoard, "FirstHorizontalBreakpoint", false);
            
            else
            
                VisualStateManager.GoToState(this.DashBoard, "MinSize", false);
            
        

提前致谢。

【问题讨论】:

【参考方案1】:

不能使用视觉状态动画来为DataTemplate 中的属性设置动画。 DataTemplate 只是一个模板,它不是可视化树的一部分,在定义故事板的那一刻。将为每个模板化目标复制模板,例如每个项目容器。这意味着将有多个名称为 TargetBtn 的按钮 - 从模板范围之外查看时。因此,您必须将动画移动到 DataTemplate 范围内,这意味着在模板内部。

除了控件的视觉状态定义之外,您还应该添加属性以将视觉状态反映为对象状态。 Microsoft Docs 建议对每个状态执行此操作是最佳做法:添加属性如 IsMouseOver 以指示 MouseOver 状态或 IsFocusedFocused 状态串联,仅举一些框架示例. 这允许您的控件将视觉状态表达为真实的实例状态,可以在控件的直接内容的上下文之外使用 - 视觉状态只能由 VisualStateManager 使用。

然后您可以使用这个通常是公共的只读依赖属性来触发,例如一个DataTrigger,您在DataTemplate 中定义它来为模板的内容设置动画:

ResizeUserControl.xaml.cs

public partial class ResizeUserControl : UserControl

  #region IsExpandedViewEnabled read-only dependency property

  protected static readonly DependencyPropertyKey IsExpandedViewEnabledPropertyKey = DependencyProperty.RegisterReadOnly(
    "IsExpandedViewEnabled",
    typeof(bool),
    typeof(ResizeUserControl),
    new PropertyMetadata(default(bool)));

  public static readonly DependencyProperty IsExpandedViewEnabledProperty = MyControl.IsExpandedViewEnabledPropertyKey.DependencyProperty;

  public bool IsExpandedViewEnabled
  
    get => (bool) GetValue(MyControl.IsExpandedViewEnabledProperty);
    private set => SetValue(MyControl.IsExpandedViewEnabledPropertyKey, value);
  

  #endregion IsExpandedViewEnabled read-only dependency property

  public ResizeUserControl()
  
    InitializeComponent();
  

  #region Overrides of FrameworkElement

  protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
  
    base.OnRenderSizeChanged(sizeInfo);
    if (this.ActualWidth > 847)
    
      VisualStateManager.GoToState(this, "FirstHorizontalBreakpoint", false);
      this.IsExpandedViewEnabled = true;
    
    else
    
      VisualStateManager.GoToState(this, "MinSize", false);
      this.IsExpandedViewEnabled = false;
    
  

ResizeUserControl.xaml

<UserControl>
  <Grid x:Name="grid">
    <StackPanel Orientation="Vertical">
      <Grid Margin="10">
        <ItemsControl Name="icTodoList"
                      ItemsSource="Binding DataItems">
          <ItemsControl.ItemTemplate>
            <DataTemplate>
              <Button x:Name="TargetBtn" Height="60"
                      Content="Binding Title" />

              <DataTemplate.Triggers>
                <DataTrigger Binding="Binding RelativeSource=RelativeSource AncestorType=UserControl, Path=IsExpandedViewEnabled"
                             Value="True">
                  <DataTrigger.EnterActions>
                    <BeginStoryboard>
                      <Storyboard>
                        <DoubleAnimation To="116" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height" />
                        <DoubleAnimation To="182" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width" />
                      </Storyboard>
                    </BeginStoryboard>
                  </DataTrigger.EnterActions>

                  <DataTrigger.ExitActions>
                    <BeginStoryboard>
                      <Storyboard>
                        <DoubleAnimation To="100" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height" />
                        <DoubleAnimation To="197" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width" />
                      </Storyboard>
                    </BeginStoryboard>
                  </DataTrigger.ExitActions>
                </DataTrigger>
              </DataTemplate.Triggers>
            </DataTemplate>
          </ItemsControl.ItemTemplate>
        </ItemsControl>
      </Grid>
    </StackPanel>
  </Grid>
</UserControl>

【讨论】:

非常感谢!这是有效的。主要应用程序遵循 MVVM 模式。我之前确实尝试通过绑定 ViewModel 的属性来使用 DataTrigger。但这没有用。

以上是关于将情节提要的目标设置为 DataTemplate WPF 中的按钮的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 在更改时为每个应用程序目标编译情节提要

一起使用笔尖和情节提要:将笔尖的按钮目标/动作连接到情节提要中的控制器

无法将 Action 设置为在情节提要上创建的 UIBarButtonItem

如何在情节提要 Xcode 11 中的视图之间将边距设置为零

如何在情节提要中为 iPad 横向和纵​​向模式将不同的框架设置为相同的视图?

如何在情节提要 XCode6 (Swift) 中将所有视图控制器设置为风景