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

时间:2013-10-03 05:49:28

标签: c# wpf mvvm

我是WPF和MVVM的新手,对以下问题表示感谢。

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

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

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

在我的视图模型中,我将有一个树,其中父项是画布,有一个类型为header的子项,以及3个图表类型的子项。

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

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

3 个答案:

答案 0 :(得分:2)

一种选择是将视图模型添加到集合中,然后将它们绑定到ItemsControl。如果在XAML中提供相应的datatemplate,则视图将自动绑定到数据。 Itemscontrol我看起来像这样:

    <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&lt;&gt ;.只要将ViewModel添加到集合中,它就会根据datatemplate中指定的View在Canvas上呈现。对于定位,您可以使用例如Canvas.Left和Canvas.Top属性(介意对齐!)或rendertransform。

答案 1 :(得分:0)

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

如果我是你,我会对此有所不同。

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

您可以使用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; }
}

希望这有帮助

答案 2 :(得分:0)

如果应用程序专门处理布局和布局信息的更改是您要呈现的数据,那么将布局信息放在视图模型中肯定是合适的。但是,简单的演示文稿信息不属于您的视图模型。

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

这与jQuery如何允许javascript程序员从网页中抓取DOM元素非常相似。