【问题标题】:UWP/WinRT: How to use VisualState Triggers to change the styling of all controls of a certain type?UWP/WinRT:如何使用 VisualState 触发器更改某种类型的所有控件的样式?
【发布时间】:2016-05-19 07:44:48
【问题描述】:

在我的 UWP 应用程序中,我有一系列 AppBarButtons,由 AppBarSeparators 分隔。当窗口大小下降到一定数量以下时,我想隐藏AppBarSeparators 以节省空间。

我尝试了类似的方法,但没有成功:

<VisualState.Setters>
   <Setter Target="AppBarSeparator" Value="Collapsed"/>
</VisualState.Setters>

我知道不可能给每个 AppBarSeparators 标签,所以我可以直接定位它们,因为它们是作为绑定的一部分动态生成的。

那么当我的窗口缩小到一定大小以下时,如何隐藏所有 AppBarSeparators?

编辑:这是我的 XAML 的精简版本,用于显示 AppBarSeparators 是如何生成的:

<Pivot x:Name="docPivot"
       ItemsSource="{Binding}">

    <Pivot.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                </Grid.RowDefinitions>

                <StackPanel Orientation="Horizontal"
                            Grid.Row="0">
                    <AppBarButton/>
                    <AppBarSeparator/>
                    <AppBarButton/>
                    <AppBarButton/>
                    <AppBarSeparator/>
                </StackPanel>

                <StackPanel Orientation="Horizontal"
                            Grid.Row="1">
                </StackPanel>
            </Grid>
        </DataTemplate>
    </Pivot.ItemTemplate>

</Pivot>

【问题讨论】:

  • “它们是作为绑定的一部分动态生成的。”您的AppBarSeparators 是在后面的代码中生成的,您没有在 xaml 中设计它们?
  • @GraceFeng-MSFT 不,它们在 xaml 中,作为 Pivot 绑定的一部分生成
  • 能否请您显示一些 xaml 代码?
  • @GraceFeng-MSFT 用 XAML 更新了我的帖子
  • 好的,我现在明白了,你的AppBarSeparatorsDataTemplate里面,它们没有暴露出来,里面的控件实际上变成了你的数据对象的可视化结构,所以这里使用visualstate trigger不能解决问题,但您可以在此处使用带有转换器的数据绑定。如果我在这里写一个演示作为答案,可以吗?

标签: responsive-design windows-runtime uwp


【解决方案1】:

你可以在你的页面中定义一个依赖属性:

sealed partial class Page1: Page {
     public static readonly DependencyProperty SeparatorVisibilityProperty = 
                            DependencyProperty.RegisterAttached("SeparatorVisibility",
                                           typeof(Visibility),
                                           typeof(Page1),
                                           new PropertyMetadata(Visibility.Visible)
                            );
    public bool SeparatorVisibility {
         get {
            return (Visibility)this.GetValue(SeparatorVisibilityProperty);
         }
         set {
             this.SetValue(SeparatorVisibilityProperty , value);
         }
    }
    ..
    ..

然后将 AppBarSeparators 的 Visibility 属性绑定到该属性:

<Page ...
      ...
      x:Name="page">
    .. 
    ..
    <AppBarSeparator Visibility="{Binding ElementName=page, Path=SeparatorVisibility}"/>
    ..

然后在可视状态下改变页面的 SeparatorVisibility 属性:

<VisualState.Setters>
   <Setter Target="page.SeparatorVisibility" Value="Collapsed"/>
</VisualState.Setters>

视觉状态会改变页面属性,它会改变 AppBarSeparators 的可见性,因为它们的 Visibility 属性绑定到页面的 SeparatorVisibility 属性。不确定这是否是最好的解决方案,这正是我现在想到的。

【讨论】:

  • 第一句话是什么意思?你是说可以给他们起名字并通过这种机制联系到他们吗?
  • @WilliamJones 我不确定这是否可能,但事实并非如此。我删除了那句话。
【解决方案2】:

正如我们在评论中所讨论的,您的AppBarSeparators 是在PivotDataTemplate 中生成的,当控件放置在DateTemplate 中时,它们将成为您数据对象的可视结构,但是VisualState 目标是控件,所以在这里不能用吗。

您可以使用带有Converter 的DataBinding 来执行此操作,如果在运行时窗口的大小是可变的,您可能还需要使用INotifyPropertyChanged Interface 完成您的数据源类。

例如这里:

<Page.Resources>
    <local:BoolVisibleConverter x:Key="cvt" />
</Page.Resources>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Pivot x:Name="docPivot" ItemsSource="{x:Bind pivotlist}" SizeChanged="docPivot_SizeChanged">
        <Pivot.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="auto" />
                        <RowDefinition Height="auto" />
                    </Grid.RowDefinitions>
                    <StackPanel Orientation="Horizontal" Grid.Row="0">
                        <AppBarButton Icon="Accept" Label="Accept" />
                        <AppBarSeparator Visibility="{Binding WindowWidth, Converter={StaticResource cvt}}" />
                        <AppBarButton Icon="Cancel" Label="Cancel" />
                        <AppBarButton Icon="Add" Label="Add" />
                        <AppBarSeparator Visibility="{Binding WindowWidth, Converter={StaticResource cvt}}" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal"
                        Grid.Row="1">
                    </StackPanel>
                </Grid>
            </DataTemplate>
        </Pivot.ItemTemplate>
    </Pivot>
</Grid>

后面的代码,我在运行时使用FrameworkElement.SizeChanged event来获取窗口的宽度,如果你只是想让布局在第一次加载布局时适合移动或PC,那么不需要这个事件,INotifyPropertyChanged 也不是:

private ObservableCollection<MyPivotItem> pivotlist = new ObservableCollection<MyPivotItem>();

public MainPage()
{
    this.InitializeComponent();
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    pivotlist.Clear();
    pivotlist.Add(new MyPivotItem { });
    pivotlist.Add(new MyPivotItem { });
    pivotlist.Add(new MyPivotItem { });
    pivotlist.Add(new MyPivotItem { });
}

private void docPivot_SizeChanged(object sender, SizeChangedEventArgs e)
{
    foreach (var item in docPivot.Items)
    {
        var pivotitem = item as MyPivotItem;
        pivotitem.WindowWidth = Window.Current.Bounds.Width;
    }
}

MyPivotItem 类是这样的:

public class MyPivotItem : INotifyPropertyChanged
{
    public MyPivotItem()
    {
        _windowwidth = Window.Current.Bounds.Width;
    }

    private double _windowwidth;

    public double WindowWidth
    {
        get { return _windowwidth; }
        set
        {
            if (value != _windowwidth)
            {
                _windowwidth = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (this.PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

BoolVisibleConverter 在这里非常简单:

public class BoolVisibleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        double? width = (double?)value;
        if (width <= 700)
            return Visibility.Collapsed;
        return Visibility.Visible;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

【讨论】:

  • 刚刚发现,如果你不想在运行时改变它,使用ItemTemplateSelector可能更容易做到这一点。
  • 所以你是在涉及与视图相关的东西的视图模型?我认为“窗口大小”是一个纯粹的视图问题,不应该暴露给视图模型。我通常在视图模型层中没有对 Windows.UI 的单一引用。
  • @MehrzadChehraz,由于OP没有提到他使用MVVM模式,我这里没有涉及MVVM,但是你是对的,如果这是一个MVVM项目,windows size不应该是暴露于视图模型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-11
  • 1970-01-01
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 2016-02-12
相关资源
最近更新 更多