【问题标题】:Triggering Commands from the ViewModel in WPF with MVVM使用 MVVM 从 WPF 中的 ViewModel 触发命令
【发布时间】:2012-05-02 12:32:51
【问题描述】:

我创建了一些具有可绑定“ClearCommand”ICommand 依赖属性的自定义控件(不是用户控件)。这个属性将完全按照它的意思做:它将清除控件中的所有值(文本框等)。我还将(一些)相同的属性绑定到我在下面描述的 VM。

现在我被困在尝试在以下 MVVM 场景中触发这些控件中的 ClearCommand:

我在视图中添加了一些这样的控件。该视图还包括一个“保存”按钮,该按钮绑定到我的 ViewModel 的 SaveCommand DelegateCommand 属性。

我需要做的是,在成功保存后,VM 应该在 View 中找到的那些控件上触发 ClearCommand

更新

我在下面添加了代码示例。我有一些类似于 ExampleCustomControl 的控件。另外,请注意,如果它完全关闭,我愿意重组其中的一些。

示例控件 sn-p:

public class ExampleCustomControl : Control {

    public string SearchTextBox { get; set; }
    public IEnumerable<CustomObject> ResultList { get; set; }

    public ExampleCustomControl() {
        ClearCommand = new DelegateCommand(Clear);
    }

    /// <summary>
    /// Dependency Property for Datagrid ItemSource.
    /// </summary>
    public static DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem",
        typeof(CustomObject), typeof(ExampleCustomControl), new PropertyMetadata(default(CustomObject)));

    public CustomObject SelectedItem {
        get { return (CustomObject)GetValue(SelectedCustomObjectProperty); }
        set { SetValue(SelectedCustomObjectProperty, value); }
    }

    public static DependencyProperty ClearCommandProperty = DependencyProperty.Register("ClearCommand", typeof(ICommand),
            typeof(ExampleCustomControl), new PropertyMetadata(default(ICommand)));

    /// <summary>
    /// Dependency Property for resetting the control
    /// </summary>
    [Description("The command that clears the control"), Category("Common Properties")]
    public ICommand ClearCommand {
        get { return (ICommand)GetValue(ClearCommandProperty); }
        set { SetValue(ClearCommandProperty, value); }
    }

    public void Clear(object o) {
        SearchTextBox = string.Empty;
        SelectedItem = null;
        ResultList = null;
    }
}

示例视图 sn-p:

<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="30"/>
    </Grid.RowDefinitions>
    <control:ExampleCustomControl Grid.Row="0"
        SelectedItem="{Binding Selection, UpdateSourceTrigger=PropertyChanged}" />
    <Button Grid.Row="1" x:Name="ResetButton" Command="{Binding SaveCommand}">
        Save
    </Button>
</Grid>

示例视图模型:

public class TestViewModel : WorkspaceTask {
    public TestViewModel() {
        View = new TestView { Model = this };
        SaveCommand = new DelegateCommand(Save);
    }

    private CustomObject _selection;
    public CustomObject Selection {
        get { return _selection; }
        set {
            _selection = value;
            OnPropertyChanged("Selection");
        }
    }

    public DelegateCommand SaveCommand { get; private set; }
    private void Save(object o) {
        // perform save
        // clear controls
    }
}

【问题讨论】:

  • 通常,我的命令在我的 ViewModel 上。在 ViewModel 中,您可以调用 MyCommand.Execute();。如果这不是您的项目的结构,请发布一些代码以澄清。
  • 作为一种快速解决方法,您可以在注册 SelectedIOtemProperty 时在 PropertyMetadata 中设置 PropertyChangedCallback,并在回调中清除您想要的所有内容。但是,我建议您考虑重组您的设置并将清除与所有绑定属性一起放入 VM。
  • 我的问题是将 ExampleCustomControl 的所有属性绑定到视图模型的问题是重用。我希望搜索逻辑/等位于自定义控件中,并且我希望能够在几个不同的视图中重用相同的控件。

标签: wpf mvvm user-controls


【解决方案1】:

正如其他人所说,VM 不应该直接在 MVVM 中知道视图,因此 VM 在您的自定义控件上触发某些内容以清除所有内容是没有意义的。

我会将自定义控件的DataContext 设置为具有您要清除的所有属性的对象,这些属性都绑定(双向)到您的文本框等。然后在 Save() 方法中您可以设置一个新对象(自定义控件DataContext 绑定到该对象),所有属性将为您清除(假设您已在该对象上实现INotifyPropertyChanged)。

更新:

根据我的评论,请参阅当前设置的解决方法示例(顺便说一句未经测试):

    public static DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem",
        typeof(CustomObject), typeof(ExampleCustomControl), new PropertyMetadata(default(CustomObject), OnSelectedItemChanged));


    private static void OnSelectedItemChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        var cont = source as ExampleCustomControl;

            //do all the clearing of txtboxes etc here....
        cont.SearchTextBox = string.Empty;
    }

但我仍然会尝试将所有这些移到 VM 中。即有一个明确的命令,就像您使用 save 命令所做的那样,并将文本框文本等绑定到 VM 中的属性,当调用该命令时,它会清除所有内容,然后您也可以轻松地从 VM 中的 Save 方法调用它。但显然我不知道您从长远来看要实现什么目标,也不知道 selectedItem 和文本框等是如何相关的,所以我猜(一如既往)取决于。

【讨论】:

  • 建议的解决方案不起作用,因为 SearchTextBox 不是静态类型的。
  • 如果您使用 DependencyObject 并转换回您的 ExampleCustomControl,请查看我的更新答案
  • 如何将 ExampleCustomControl 实现为 View/ViewModel,以便我可以轻松地重用控件(以及包含的搜索功能)并将其绑定到我的 TestViewModel 中的 SelectedItem?
  • 这可能真的取决于您希望所有这些重用控件如何与主视图一起使用。但我会说你需要设置 VM 和视图,将视图绑定到它,然后简单地在其他视图中重新使用控件。然后,您需要让 TestViewModel 知道正在使用哪个其他 VM(我猜是绑定到一个新属性),以便每当调用 Save 时,它​​都可以简单地提醒引用的 VM。简而言之:从 TestViewModel 中保存对当前 ExampleCustomControlViewModel 的引用,以便您可以访问 Clear 方法。希望这是有道理的。
  • 已经说了所有这些(并且在更好地了解您想要达到的目标之后),您最好使用我提到的回调在控件本身内完成所有这些操作。当它所做的只是清除一些属性时,它可能会增加太多的复杂性,必须从 VM 中引用 VM。细线,但如果控件只有这个基本功能,那么最好保持原样。希望这没有混淆问题。
【解决方案2】:

听起来你想错了。在 MVVM 中,ViewModel 永远不应该知道有关自定义控件的任何信息(因此您在使用这个 Clear 功能时遇到了问题)。

您的要求有点模糊,但您考虑过吗:

1) 如果属性是从虚拟机绑定的,控件是否无法检测到这些属性何时更改?

2) 如果您确实需要从 XAML 层调用 Clear 并希望将其保持为纯 MVVM,请考虑类似 Expression Blend SDK 的 CallMethodAction

【讨论】:

  • 关于#1:我已经考虑过这种情况。自定义控件包含一个搜索文本框和一个数据网格来显示结果。所有要搜索的逻辑都包含在控制实现本身中。控件数据网格的选定值实际上是我在 VM 中绑定到的依赖属性。虽然我可以在我的 VM 中重置所选值,但如何触发自定义控件中其余控件的重置?
  • Alex-我认为您需要发布一些精简的示例代码以获得进一步的帮助。没有什么比代码示例更好的了。
  • 接下来让我说明虽然我可以从 DependencyProperty 触发事件,但该事件必须包含在静态范围内。这对重置控件的非静态范围属性不起作用。
  • 我看到了——你有没有考虑过使用强制值回调——它引用了 DependencyObject?
  • 我已经用实际代码 sn-ps 更新了这个问题。如前所述,如果这意味着解决这个问题,我愿意对其进行重组。谢谢。
【解决方案3】:

作为我评论的后续行动。我怀疑您的命令是针对视图并直接清除TextBoxes。相反,让您的命令以 ViewModel 为目标并清除 View 绑定到的属性。然后,您可以将该命令作为 ViewModel 上的一个属性,并在需要时调用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-05
    • 1970-01-01
    • 2013-05-04
    • 2010-10-22
    • 1970-01-01
    • 2018-09-20
    • 1970-01-01
    • 2012-05-29
    相关资源
    最近更新 更多