MVVM绑定模型到后面的自定义控件代码



我正在用Win2D画布构建WinUI应用程序。

介绍信息:

  • 我正在使用CommunityToolkit。Mvvm
  • 我正在从DI注入vm
  • 我想"尽可能多地"做这件事。即不直接绑定到模型——保持M-V-VM的分离清晰(即使这意味着过度工程的代码)

我已经创建了自定义高级CanvasControl,它将绑定到ObservableCollection<Polygon>,它的代码后面照顾渲染,如:

var geometry = CanvasGeometry.CreatePolygon(session, polygon.Points);
foreach (var cutout in polygon.Cutouts)
{
var cutoutGeometry = CanvasGeometry.CreatePolygon(session, cutout);
geometry = geometry.CombineWith(cutoutGeometry, Matrix3x2.Identity, CanvasGeometryCombine.Exclude);
}
session.DrawGeometry(geometry, lineColor, PolygonLineThickness);

我的Polygon模型是这样的:

public class Polygon
{
public string Name { get; set; }
public List<Vector2> Points { get; init; }
public List<List<Vector2>> Cutouts { get; init; }
void Rotate(double degrees) { ... }
...
}

我的问题是,我如何在自定义画布代码后面获得多边形对象,所以我没有直接从视图引用模型,从而违反MVVM?这个绑定应该是双向的,所以当我拖拽多边形时,它将在画布代码中被处理并反射回源集合。

我的想法是:

CreateObservableCollection<PolygonViewModel>并在我的ViewModel做一些像polygons.Select(p => new PolygonViewModel(p));和CustomCanvas将绑定到这-然而,因为自定义控件不应该绑定到MVVM(就像我们没有东西像TextBoxViewModel),这似乎不像正确的方式。我也不认为从DI获取一些虚拟机和手动创建一些虚拟机是干净的(?)

在视图层创建一些PolygonItem对象这似乎是正确的方式,因为控件将保持独立。这将绑定到ObservableCollection<Polygon>,并使用两者之间的值转换器。然而,我不认为这是可能的。如果转换器位于VM层,则无法看到视图层中的PolygonItem。如果它在视图层,它就看不到模型层中的Polygon

我接近这个了吗?

首先,你需要问自己,

多边形需要生活在我的自定义CanvasControl?

,如果答案是NO,那么将所有内容保留在自定义CanvasControl中,即代码隐藏。

但是如果答案是YES,这里有一个示例代码:

AwesomeCanvas.cs

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace AwesomeCanvasControlSample;
public sealed class AwesomeCanvas : Control
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
nameof(ItemsSource),
typeof(object),
typeof(AwesomeCanvas),
new PropertyMetadata(default));
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
nameof(SelectedItem),
typeof(object),
typeof(AwesomeCanvas),
new PropertyMetadata(default));
public AwesomeCanvas()
{
this.DefaultStyleKey = typeof(AwesomeCanvas);
}
public object ItemsSource
{
get => (object)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public object SelectedItem
{
get => (object)GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
}

MainPage.xaml

<Page
x:Class="AwesomeCanvasControlSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:AwesomeCanvasControlSample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid RowDefinitions="Auto,*">
<StackPanel
Grid.Row="0"
Orientation="Horizontal">
<Button
Command="{x:Bind ViewModel.AddPolygonCommand}"
Content="Add polygon" />
<Button
Command="{x:Bind ViewModel.RemovePolygonCommand}"
CommandParameter="{x:Bind AwesomeCanvasControl.SelectedItem, Mode=OneWay}"
Content="Remove polygon" />
</StackPanel>
<local:AwesomeCanvas
x:Name="AwesomeCanvasControl"
ItemsSource="{x:Bind ViewModel.Polygons, Mode=OneWay}" />
</Grid>
</Page>

MainPageViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
namespace AwesomeCanvasControlSample;
public class Polygon
{
public int Id { get; set; }
}
public partial class MainPageViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<Polygon> polygons = new();
[RelayCommand]
private void AddPolygon()
{
Polygons.Add(new Polygon() { Id = Polygons.Count + 1 });
}
[RelayCommand]
private void RemovePolygon(Polygon polygon)
{
Polygons.Remove(polygon);
}
}

有不同的意见,但我认为,MVVM不是关于View | ViewModel | Model的分离,更多的是关于依赖的方向View ->ViewModel→模型:

模型
  • 不应该知道ViewModels
  • 视图模型可以了解模型但从来不讨论视图

所以,在这种情况下,没有错误的传递多边形(模型)到视图没有包装它在PolygonViewModel。

最新更新