c# - Using the same view for different ViewModels in WinUI3 - Stack Overflow

In a WinUI3 project, I have 3 views IconEditorView, ColorEditorView, and ImageEditorView, each with the

In a WinUI3 project, I have 3 views IconEditorView, ColorEditorView, and ImageEditorView, each with their corresponding ViewModel IconEditorViewModel, ColorEditorViewModel, and ImageEditorViewModel. The views are practically the same, the only differences are a few labels that are binded from the ViewModel anyways (the specific UI elements are in independent UserControls within the views).

I was wondering if there is any way to be able to have a generic view like EditorView and attach the corresponding ViewModel. Since they all implement IEditorViewModel they all have everything that would be necessary for EditorView to work. Currently, for each view, I attach the corresponding ViewModel by using a dependency property like so:

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

Silly me tried converting the ViewModel property type to an IEditorViewModel, but that, of course, did not work since I needed data binding for updating a few workflow indicators as the user interacts with the view.

In the future, I will likely add new editors, so this approach would be very convenient to just create the ViewModel implementing IEditorViewModel and attach it to an instance of the existing generic EditorView.

How can I achieve this?

Note: I'm using WinUI3 latest stable version along with CommunityToolkit.MVVM if that's relevant in any way.

Thanks!

In a WinUI3 project, I have 3 views IconEditorView, ColorEditorView, and ImageEditorView, each with their corresponding ViewModel IconEditorViewModel, ColorEditorViewModel, and ImageEditorViewModel. The views are practically the same, the only differences are a few labels that are binded from the ViewModel anyways (the specific UI elements are in independent UserControls within the views).

I was wondering if there is any way to be able to have a generic view like EditorView and attach the corresponding ViewModel. Since they all implement IEditorViewModel they all have everything that would be necessary for EditorView to work. Currently, for each view, I attach the corresponding ViewModel by using a dependency property like so:

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

Silly me tried converting the ViewModel property type to an IEditorViewModel, but that, of course, did not work since I needed data binding for updating a few workflow indicators as the user interacts with the view.

In the future, I will likely add new editors, so this approach would be very convenient to just create the ViewModel implementing IEditorViewModel and attach it to an instance of the existing generic EditorView.

How can I achieve this?

Note: I'm using WinUI3 latest stable version along with CommunityToolkit.MVVM if that's relevant in any way.

Thanks!

Share Improve this question edited Nov 18, 2024 at 2:58 Hector Lazarin asked Nov 16, 2024 at 20:20 Hector LazarinHector Lazarin 826 bronze badges 3
  • For a "view" that changes based on what's in the the (view) "model", I use properties such as "IsVisible" to reflect a particlular aspect of the current "context". They're simply "getters" that say a particluar element should be visible or not visible (or the FontSize is different; etc.); based on some other properties. (e.g. Infantry vs cavalry vs artillery). – Gerry Schmitz Commented Nov 17, 2024 at 19:05
  • You could use something like (model is IEditImage) to show / hide parts of the view. Or just define an abstract base type for the common bits. – Jeremy Lakeman Commented Nov 18, 2024 at 3:22
  • You could try to create a wrapper view model that contains references to the different view models you want to use. I suggest you could refer to the thread: learn.microsoft/en-us/answers/questions/1151482/… – Jeaninez - MSFT Commented Nov 18, 2024 at 6:02
Add a comment  | 

1 Answer 1

Reset to default 1

You could use DataTemplateSelector for this.

Let's say we have these editor classes:

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

then the custom control could be:

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/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft/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>

and the DataTemplateSelector could be something like this:

DataTemplateSelector with Dictionary in XAML

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

finally, we can use the control:

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

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745644217a4637873.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信