在WinUI3中对不同的ViewModel使用相同的视图

问题描述 投票:0回答:1

在 WinUI3 项目中,我有 3 个视图

IconEditorView
ColorEditorView
ImageEditorView
,每个视图都有相应的 ViewModel
IconEditorViewModel
ColorEditorViewModel
ImageEditorViewModel
。视图实际上是相同的,唯一的区别是无论如何都从 ViewModel 绑定的一些标签(特定的 UI 元素位于视图内的独立 UserControl 中)。

我想知道是否有任何方法能够拥有像

EditorView
这样的通用视图并附加相应的ViewModel。因为他们都实现了
IEditorViewModel
,所以他们都拥有
EditorView
工作所需的一切。目前,对于每个视图,我使用依赖属性附加相应的 ViewModel,如下所示:

public sealed partial class ImageEditorView : UserControl
{
    public ImageEditorViewModel ImageEditorViewModel
    {
        get => (ImageEditorViewModel)GetValue(ImageEditorViewModelProperty);
        set => SetValue(ImageEditorViewModelProperty, value);
    }

    public static readonly DependencyProperty ImageEditorViewModelProperty =
    DependencyProperty.Register(
        nameof(ImageEditorViewModel),
        typeof(ImageEditorViewModel),
        typeof(ImageEditorView),
        new PropertyMetadata(default));

    public ImageEditorView(ImageEditorViewModel imageEditorViewModel)
    {
        this.InitializeComponent();
        ImageEditorViewModel = imageEditorViewModel;
    } 
}

愚蠢的我尝试将 ViewModel 属性类型转换为

IEditorViewModel
,但这当然不起作用,因为我需要数据绑定来在用户与视图交互时更新一些工作流指示器。

将来,我可能会添加新的编辑器,因此这种方法非常方便,只需创建实现

IEditorViewModel
的 ViewModel 并将其附加到现有通用
EditorView
的实例。

我怎样才能实现这个目标?

注意: 我正在使用 WinUI3 最新稳定版本以及 CommunityToolkit.MVVM(如果有任何相关性)。

谢谢!

c# xaml mvvm winui-3 windows-app-sdk
1个回答
0
投票

您可以使用 DataTemplateSelector 来实现此目的。

假设我们有这些编辑器类:

public interface IEditor
{
    string Name { get; }
}

public class IconEditor : IEditor
{
    public string Name { get; } = nameof(IconEditor);

    public Symbol Icon { get; set; }
}

public class ColorEditor : IEditor
{
    public string Name { get; } = nameof(ColorEditor);

    public Brush Color { get; set; } = new SolidColorBrush(Colors.Transparent);
}

那么自定义控件可以是:

EditorView.cs

public class EditorView : Control
{
    public static readonly DependencyProperty EditorProperty =
        DependencyProperty.Register(nameof(Editor),
            typeof(IEditor),
            typeof(EditorView),
            new PropertyMetadata(default));

    public static readonly DependencyProperty EditorTemplateSelectorProperty =
        DependencyProperty.Register(
            nameof(EditorTemplateSelector),
            typeof(DataTemplateSelector),
            typeof(EditorView),
            new PropertyMetadata(default));

    public EditorView()
    {
        DefaultStyleKey = typeof(EditorView);
    }

    public ContentPresenter? EditorPresenter { get; set; }

    public IEditor Editor
    {
        get => (IEditor)GetValue(EditorProperty);
        set => SetValue(EditorProperty, value);
    }
    public DataTemplateSelector EditorTemplateSelector
    {
        get => (DataTemplateSelector)GetValue(EditorTemplateSelectorProperty);
        set => SetValue(EditorTemplateSelectorProperty, value);
    }
}

Generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1">

    <Style TargetType="local:EditorView">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:EditorView">
                    <Border
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                        <ContentControl
                            Content="{TemplateBinding Editor}"
                            ContentTemplateSelector="{TemplateBinding EditorTemplateSelector}" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

DataTemplateSelector
可能是这样的:

XAML 中带有字典的 DataTemplateSelector

public class StringToDataTemplateDictionary : Dictionary<string, DataTemplate>
{
}

public class EditorTemplateSelector : DataTemplateSelector
{
    public DataTemplate DefaultTemplate { get; set; } = new();

    public StringToDataTemplateDictionary DataTemplates { get; set; } = [];

    protected override DataTemplate SelectTemplateCore(object item)
    {
        return base.SelectTemplateCore(item);
    }

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        return item is IEditor editor
            ? DataTemplates.TryGetValue(editor.Name, out var template) is true
                ? template
                : DefaultTemplate
            : DefaultTemplate;
    }
}

最后,我们可以使用控件了:

<Page.Resources>
    <local:EditorTemplateSelector x:Key="EditorTemplateSelector">
        <local:EditorTemplateSelector.DataTemplates>
            <DataTemplate
                x:Key="IconEditor"
                x:DataType="local:IconEditor">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{x:Bind Name}" />
                    <SymbolIcon Symbol="{x:Bind Icon}" />
                </StackPanel>
            </DataTemplate>
            <DataTemplate
                x:Key="ColorEditor"
                x:DataType="local:ColorEditor">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{x:Bind Name}" />
                    <Rectangle
                        Width="32"
                        Height="32"
                        Fill="{x:Bind Color}" />
                </StackPanel>
            </DataTemplate>
        </local:EditorTemplateSelector.DataTemplates>
    </local:EditorTemplateSelector>
</Page.Resources>

<StackPanel>
    <Button
        Click="ToggleEditorButton_Click"
        Content="Toggle Editor" />
    <local:EditorView
        x:Name="EditorViewControl"
        EditorTemplateSelector="{StaticResource EditorTemplateSelector}" />
</StackPanel>
private void ToggleEditorButton_Click(object sender, RoutedEventArgs e)
{
    EditorViewControl.Editor = EditorViewControl.Editor is IconEditor
        ? new ColorEditor() { Color = new SolidColorBrush(Colors.SkyBlue) }
        : new IconEditor() { Icon = Symbol.Home };
}
© www.soinside.com 2019 - 2024. All rights reserved.