UWP/WinRT: 如何使用VisualState Triggers改变某一类所有控件的样式?

UWP/WinRT: How to use VisualState Triggers to change the styling of all controls of a certain type?

在我的 UWP 应用中,我有一系列由 AppBarSeparators 分隔的 AppBarButtons。当 window 尺寸下降到一定数量以下时,我想隐藏 AppBarSeparators 以节省 space。

我试过类似的方法,但没有用:

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

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

那么,当我的 window 缩小到特定大小以下时,如何隐藏所有 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>

您可以在您的页面中定义依赖项属性:

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 的可见性 属性 绑定到此 属性:

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

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

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

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

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

您可以将 DataBinding 与 Converter to do this, and if the size of the window is changeable during the run-time, you may also need complete your data source class with 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>

后面的代码,如果您只是想让布局适合移动设备或 PC,我在 运行 期间使用 FrameworkElement.SizeChanged event 获取 window 的宽度第一次加载布局,则不需要此事件,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;
    }
}

MyPivotItemclass是这样的:

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();
    }
}