【问题标题】:How to translate ViewModel to Canvas in MVVM and WPF如何在 MVVM 和 WPF 中将 ViewModel 转换为 Canvas
【发布时间】:2013-10-03 19:09:54
【问题描述】:

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

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

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

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

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

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

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

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    一种选择是将视图模型添加到集合中,然后将它们绑定到 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。

    【讨论】:

    • 在上面的例子中,要在 Canvas 中绘制的形状的几何形状是在哪里决定的?假设你想将数据渲染成圆形,或者椭圆形,或者矩形......你在哪里指定这个形状几何?谢谢
    • 这将在您的视图中。如果您想要一个椭圆,请执行 ,但如果视图更复杂,我通常把它做成一个单独的 UserControl,然后像在代码片段中一样将 datacontext 绑定到 viewmodel。
    【解决方案2】:

    您不应在 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; }
    }
    

    希望对你有帮助

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2012-05-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-31
        • 2011-02-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多