0

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

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

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

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

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

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

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

4

3 回答 3

2

一种选择是将视图模型添加到集合中,然后将它们绑定到 ItemsControl。如果您在 XAML 中提供适当的数据模板,则视图会自动绑定到数据。我拥有的 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<>。一旦 ViewModel 添加到集合中,它就会根据数据模板中指定的 View 在 Canvas 上呈现。对于定位,您可以使用例如 Canvas.Left 和 Canvas.Top 属性(注意对齐!),或 rendertransform。

于 2013-10-03T11:09:54.093 回答
0

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

如果我是你,我会以不同的方式处理这个问题。

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

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

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

当您需要它以供以后使用时,使用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; }
}

希望这可以帮助

于 2013-10-03T07:08:47.580 回答
0

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

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

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

于 2013-10-03T07:24:56.643 回答