正确的 MVVM 模式 WPF 命令实现

Posted

技术标签:

【中文标题】正确的 MVVM 模式 WPF 命令实现【英文标题】:Correct MVVM pattern WPF Command implementation 【发布时间】:2017-12-06 17:44:18 【问题描述】:

我正在尝试按照 MVVM 模式实现命令,但我被这个特定场景所困。

在 XAML 中,我将命令绑定到列内的按钮:

<dxg:GridColumn FieldName="Delete" Header="" UnboundType="Object" Width="20" FixedWidth="True">
    <dxg:GridColumn.EditSettings>
        <dxe:ButtonEditSettings AllowDefaultButton="False">
            <dxe:ButtonEditSettings.Buttons>
                <dxe:ButtonInfo GlyphKind="Cancel" Command="Binding DeleteRowCommand" CommandParameter="Binding ElementName=testView"  />
            </dxe:ButtonEditSettings.Buttons>
        </dxe:ButtonEditSettings>
    </dxg:GridColumn.EditSettings>
</dxg:GridColumn>

在我的 ViewModel 中,我声明了一个 DelegateCommand:

Private m_deleteRowCommand As DelegateCommand(Of Object)
Public Property DeleteRowCommand() As DelegateCommand(Of Object)
    Get
        Return m_deleteRowCommand
    End Get
    Private Set(ByVal value As DelegateCommand(Of Object))
        m_deleteRowCommand = value
    End Set
End Property

我在 ViewModel 的构造函数中初始化了命令:

DeleteRowCommand = New DelegateCommand(Of Object)(AddressOf DeleteRowCommandExecute)

最后我执行命令:

Private Sub DeleteRowCommandExecute(ByVal parameter As Object)
    Dim sender As TableView = parameter
    Dim row = sender.DataControl.CurrentItem
    Dim index = sender.FocusedRowHandle
    sender.DeleteRow(index)
End Sub

一切都按预期工作,但据我所知 ViewModel 应该对 View 一无所知,因此删除 ViewModel 内的行是不正确的。

遵循 MVVM 模式的最佳方法是什么?

更新: 从绑定到 Grid 的 ItemsSource 的 ObservableCollection 中删除项目效果很好,但是如果我需要从没有 ItemsSource 的 Grid 中删除像 StackPanel 这样的 UI 元素怎么办?

<Grid>
    <StackPanel>
        <Button Content="Delete" Height="25" Width="100" Command="Binding DeleteItemCommand" 
                CommandParameter="Binding RelativeSource=RelativeSource AncestorType=StackPanel" />
    </StackPanel>
</Grid>

更新 2: 我的目标是拥有一个容器,我可以在其中动态添加项目(UserControl),并且我可以在运行时更改这些项目的顺序。 目前,我使用 Grid 作为容器,每次插入新项目时都会向其中添加新的 RowDefinition。 我使用 Grid.Row 属性来跟踪和更改项目的顺序。 这样我需要在后面的代码中执行所有删除操作,因为我必须从 Grid 中手动删除 RowDefinition。

【问题讨论】:

DataGridItemsSource怎么设置?如果您正在使用例如ObservableCollection 你可以从你的```中删除该条目,你的视图将自动更新。 @MightyBadaboom ItemsSource 确实是一个 ObservableCollection,经过一些调整后,我只需从集合中删除相应的项目即可使其工作。但是,如果我需要从没有 ItemsSource 属性的 Grid 中删除 ui 元素,比如 StackPanel,该怎么办? 干得好,不需要更多帮助了吗? @MightyBadaboom 我更新了问题。 你为什么在Grid 中使用StackPanel,一列一行只是为了放置ButtonGridStackpanel 不可见并且用于定位。当你不得不改变他们的可见性时,我猜你的方法不是最好的。例如,如果你真的需要,你可以使用IValueConverter 【参考方案1】:

从绑定到 Grid 的 ItemsSource 的 ObservableCollection 中删除项目效果很好,但是如果我需要从没有 ItemsSource 的 Grid 中删除 UI 元素(如 StackPanel)怎么办?

由于此代码纯粹是与视图相关的,它应该在视图的代码隐藏中实现(或在应用它的控件本身中)。视图模型不应该知道任何关于任何 UI 元素的信息,因此实现删除这些元素的命令是没有意义的。

MVVM 并不是要从视图中消除与 view 相关的代码。它是关于关注点的分离。因此,请保留与 UIElementsPanels 相关的所有内容。

【讨论】:

实际上,当我单击 StackPanel 内的按钮时,我需要从 Grid 中删除 StackPanel 并从 ViewModel 内的 ObservableCollection 中删除一个项目。 那么您应该使用绑定到源集合的 ItemsControl,然后将 StackPanel 添加到 ItemTemplate。 我更新了我的问题。然后您建议使用 ItemsControl 而不是 Grid?在这种情况下,为了更改项目的顺序,我应该向我的 UserControl 添加自定义索引属性吗? 只需将 ItemsControl 的 ItemsSource 属性绑定到 ObservableCollection,然后从视图模型类中添加、删除或移动 ObservableCollection 中的项目。

以上是关于正确的 MVVM 模式 WPF 命令实现的主要内容,如果未能解决你的问题,请参考以下文章

使用 MVVM 从 WPF 中的 TextBox 进行正确的 DataGrid 搜索

我在使用 MVVM for WPF 实现命令处理器模式时遇到问题

MVVM设计模式和在WPF中的实现 事件绑定

WPF的MVVM模式

WPF 从属性赋值到MVVM模式详解

WPF MVVM 从 ViewModel 触发事件的正确方法