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
是在 Pivot
的 DataTemplate
中生成的,当控件放置在 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;
}
}
MyPivotItem
class是这样的:
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();
}
}
在我的 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
是在 Pivot
的 DataTemplate
中生成的,当控件放置在 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;
}
}
MyPivotItem
class是这样的:
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();
}
}