如何在 MVVM 和 WPF 中将 ViewModel 转换为画布

本文关键字:转换 ViewModel 中将 WPF MVVM | 更新日期: 2023-09-27 18:33:53

我是WPF和MVVM的新手,希望能帮助解决以下问题。

我想创建一个应用程序,其中用户通过对话框指定他希望如何在页面上布局 N 个图表对象,应用程序在画布上向他显示此布局。 当对他在画布中看到的布局感到满意时,用户会保留它以供以后使用。

所有图表对象都可以可视化为矩形。 用户还可以定义一个标题,它也是一个矩形。

典型的布局可以是页面顶部的标题,下面是并排的三个图表。用户可以在对话框中指定此布局以及每个子项的尺寸和位置,然后点击"应用"按钮,希望在画布上以图形形式查看此规范。

在我的视图模型中,我会有一个树,其中父级是画布,有一个标题类型的子项和 3 个图表类型的子项。

用户可能不喜欢他所看到的内容,并在对话框中进行更改,然后影响视图模型中的更改。

我有点理解对话框和视图模型之间的视图-视图模型交互。 但不知道如何实现画布-视图模型交互。这意味着当用户在对话框中请求在给定坐标处说出给定大小的标题矩形时,我知道如何在视图模型的树中添加该标题对象,但我不知道如何从 ViewModel 的树中更新画布。如何绘制画布以反映视图模型中的对象树,然后在每次视图模型更改时重新绘制(由于用户与对话框的交互(?

如何在 MVVM 和 WPF 中将 ViewModel 转换为画布

一种选择是将视图模型添加到集合,然后将其绑定到 ItemsControl。如果在 XAML 中提供适当的数据模板,则视图将自动绑定到数据。我拥有的项目控件如下所示:

    <ItemsControl x:Name="WorksetPresenter"
                  ItemsSource="{Binding ElementName=RootWindow, Path=TableauItems}"
                  >
        <ItemsControl.Resources>
            <DataTemplate DataType="{x:Type viewModels:AnalysisViewModel}">
                 <wg:AnalysisView DataContext="{Binding DescriptiveAnalysis}"/>
            </DataTemplate>
        <!-- more datatemplates for more view/viewmodel pairs --> 
        </ItemsControl.Resources>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>  

TableauItems 是一个 ObservableCollection<>。将 ViewModel 添加到集合后,它将根据数据模板中指定的视图在画布上呈现。对于定位,您可以使用例如 Canvas.Left 和 Canvas.Top 属性(注意对齐!(,或渲染转换。

不应在 viewModel 中存储图形设置,例如控件的大小和坐标。

如果我是你,我会以不同的方式来形容这一点。

View 中,使用画布上的拖放操作让用户更改图表和标题的位置。

您可以使用GridSplitter使它们可调整用户大小

然后,当用户点击Apply时,使用 XamlWriter.Save 方法保存画布对象

当您需要它供以后使用时,请使用 XamlReader.Load 方法加载它

在您的ViewModel中,有一个命令将画布作为参数并处理Save操作。

视图:

<Canvas x:Name="mainCanvas">
        <StackPanel>
            <TextBlock Text="My Header ..."/>
            <!-- Charts goes here .... -->
            <Button Content="Apply">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <i:InvokeCommandAction Command="{Binding ApplyCommand}" CommandParameter="{Binding ElementName=mainCanvas}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>
        </StackPanel>
</Canvas>

查看型号:

public class MainWindowViewModel
{
    public MainWindowViewModel()
    {
        ApplyCommand = new DelegateCommand<Canvas>(canvas =>
            {
                string userLayout = XamlWriter.Save(canvas);
                // save userLayout for later use ...
            });
    }
    public DelegateCommand<Canvas> ApplyCommand { get; set; }
}

希望这有帮助

如果应用程序专门处理布局的更改,并且布局信息是您呈现的数据,那么将布局信息放在视图模型中当然是合适的。但是,简单的表示法信息不属于您的视图模型。

为此,您需要一个不同的解决方案。考虑一下。如果我需要在屏幕上定位视图模型模板位置,我该怎么做?我的视图模型无法了解可视化树!虫子。为了解决此问题,我使用附加属性标记元素,并使用自定义布局行为或控件来查询附加属性。

这与jQuery允许javascript程序员从网页中获取DOM元素的方式非常相似。