在WPF中,您可以使用Binding动态地使用样式和模板修改控件。我看到如何在UWP中直接在控件中执行此操作,但我想应用一个将根据绑定更改自身的模板。
一个例子是一个按钮。我有一个按钮可以打开和关闭此项目中的灯光。该项目已在WPF中创建并运行,但需要转换为UWP。在WPF版本中,我们为按钮提供LightStyle,根据它的光线类型,我们更改模板以查看和执行该灯光。 (例如:我们可以改变一些灯光的颜色,一些灯光的暗淡,一些灯光只是打开和关闭;但我们对它们使用相同的LightStyle。非常通用,动态,非常有用。)
你如何在UWP中做到这一点?我搜索了一下,发现我会在这里停下来检查,同时继续挖掘。请记住,此项目是纯MVVM,并且不使用任何代码。我不介意解释背后的代码,只要它不是唯一的方法。
提前致谢:)
答案 0 :(得分:1)
Here is a sample I've made - XAML:
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Orientation="Horizontal">
<StackPanel.Resources>
<local:MySelector x:Key="MySelector">
<local:MySelector.GreenTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Foreground="Green"/>
</DataTemplate>
</local:MySelector.GreenTemplate>
<local:MySelector.RedTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Foreground="Red"/>
</DataTemplate>
</local:MySelector.RedTemplate>
</local:MySelector>
</StackPanel.Resources>
<ListView x:Name="ListOfItems" Width="100" ItemTemplateSelector="{StaticResource MySelector}"/>
<StackPanel>
<ToggleSwitch OnContent="GREEN" OffContent="RED" Margin="10" IsOn="{x:Bind IsSwitched, Mode=TwoWay}"/>
<Button Content="Add item" Click="AddClick" Margin="10"/>
</StackPanel>
</StackPanel>
和背后的代码:
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private bool isSwitched = false;
public bool IsSwitched
{
get { return isSwitched; }
set { isSwitched = value; RaiseProperty(nameof(IsSwitched)); }
}
public MainPage() { this.InitializeComponent(); }
private void AddClick(object sender, RoutedEventArgs e)
{
ListOfItems.Items.Add(new ItemClass { Type = isSwitched ? ItemType.Greed : ItemType.Red, Text = "NEW ITEM" });
}
}
public enum ItemType { Red, Greed };
public class ItemClass
{
public ItemType Type { get; set; }
public string Text { get; set; }
}
public class MySelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
switch ((item as ItemClass).Type)
{
case ItemType.Greed:
return GreenTemplate;
case ItemType.Red:
default:
return RedTemplate;
}
}
public DataTemplate GreenTemplate { get; set; }
public DataTemplate RedTemplate { get; set; }
}
通常您可以为选择器选择各种开关,这取决于您的需求。在上面的示例中,我根据项目的属性切换模板,here is a good example如何切换项目的类型。
答案 1 :(得分:0)
以下是我使用的答案,适用于我的特定情况。基本上你必须使用VisualStateTrigger并通过代码手动创建触发器。你可以使用各种各样的触发器,并且内置了许多触发器,但是对于这种情况,我必须,或者至少我认为必须手动编写一个。
这是触发器代码。
public class StringComparisonTrigger : StateTriggerBase
{
private const string NotEqual = "NotEqual";
private const string Equal = "Equal";
public string DataValue
{
get { return (string)GetValue(DataValueProperty); }
set { SetValue(DataValueProperty, value); }
}
public static readonly DependencyProperty DataValueProperty =
DependencyProperty.Register(nameof(DataValue), typeof(string), typeof(StringComparisonTrigger), new PropertyMetadata(Equal, (s, e) =>
{
var stringComparisonTrigger = s as StringComparisonTrigger;
TriggerStateCheck(stringComparisonTrigger, stringComparisonTrigger.TriggerValue, (string)e.NewValue);
}));
public string TriggerValue
{
get { return (string)GetValue(TriggerValueProperty); }
set { SetValue(TriggerValueProperty, value); }
}
public static readonly DependencyProperty TriggerValueProperty =
DependencyProperty.Register(nameof(TriggerValue), typeof(string), typeof(StringComparisonTrigger), new PropertyMetadata(NotEqual, (s, e) =>
{
var stringComparisonTrigger = s as StringComparisonTrigger;
TriggerStateCheck(stringComparisonTrigger, stringComparisonTrigger.DataValue, (string)e.NewValue);
}));
private static void TriggerStateCheck(StringComparisonTrigger elementTypeTrigger, string dataValue, string triggerValue)
=> elementTypeTrigger.SetActive(dataValue == triggerValue);
}
这个,因为继承自StateTriggerBase可以在VisualStateTriggers组中使用,我将在下面发布。我不知道的是,您编写的任何依赖属性都可以在XAML中使用,并且触发器中没有任何接口或任何东西可以使其工作。触发触发器的唯一代码行是&#39; SetActive(bool值)&#39;您希望状态发生变化时必须打电话。通过在XAML中创建依赖项属性和绑定,您可以在属性更改时触发SetActive,从而修改可视状态。
DataTemplate在下面。
<DataTemplate x:Key="LightsButtonTemplate">
<UserControl>
<StackPanel Name="panel">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<DataTriggers:StringComparisonTrigger DataValue="{Binding Type}"
TriggerValue="READ" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="panel.(UIElement.Background)"
Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock Text="{Binding Type}" />
<TextBlock Text="{Binding LightStateViewModel.On}" />
</StackPanel>
</UserControl>
</DataTemplate>
最后使用你可以在任何地方使用DataTemplate,但我在一个绑定到LightViewModel列表的ItemsControl中使用它。
<ScrollViewer Grid.Row="1">
<ItemsControl ItemsSource="{Binding LightViewModels}"
ItemTemplate="{StaticResource LightsButtonTemplate}" />
</ScrollViewer>
显然,这不是我想要的灯光按钮的模板设计,但这是我完成的所有工作,以便理解并现在实现动态模板。希望这有助于其他人来自WPF。
从StateTriggerBase派生的自定义触发器类可以执行并绑定您想要的任何内容,只要您希望更新该触发器,您需要做的就是调用SetActive(true)或SetActive(false)。当它为真时,使用该触发器的VisualState将处于活动状态。