如何在UWP中动态更改itemtemplate?

时间:2017-01-30 18:29:52

标签: c# xaml uwp uwp-xaml

在WPF中,您可以使用Binding动态地使用样式和模板修改控件。我看到如何在UWP中直接在控件中执行此操作,但我想应用一个将根据绑定更改自身的模板。

一个例子是一个按钮。我有一个按钮可以打开和关闭此项目中的灯光。该项目已在WPF中创建并运行,但需要转换为UWP。在WPF版本中,我们为按钮提供LightStyle,根据它的光线类型,我们更改模板以查看和执行该灯光。 (例如:我们可以改变一些灯光的颜色,一些灯光的暗淡,一些灯光只是打开和关闭;但我们对它们使用相同的LightStyle。非常通用,动态,非常有用。)

你如何在UWP中做到这一点?我搜索了一下,发现我会在这里停下来检查,同时继续挖掘。请记住,此项目是纯MVVM,并且不使用任何代码。我不介意解释背后的代码,只要它不是唯一的方法。

提前致谢:)

2 个答案:

答案 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将处于活动状态。