使用MVVM从WPF中的ViewModel触发命令

使用MVVM从WPF中的ViewModel触发命令,wpf,mvvm,user-controls,Wpf,Mvvm,User Controls,我已经创建了一些具有可绑定的“ClearCommand”ICommand依赖属性的自定义控件(不是UserControls)。此属性将完全执行其听起来的操作:它将清除控件中的所有值(文本框等)。我还将(一些)相同的属性绑定到下面描述的VM 现在,在以下MVVM场景中,我一直在尝试触发这些控件中的ClearCommand: 我在视图中添加了一些这样的控件。该视图还包括一个“保存”按钮,该按钮绑定到我的ViewModel的SaveCommandDelegateCommand属性 我需要做的是,成功保

我已经创建了一些具有可绑定的“ClearCommand”ICommand依赖属性的自定义控件(不是UserControls)。此属性将完全执行其听起来的操作:它将清除控件中的所有值(文本框等)。我还将(一些)相同的属性绑定到下面描述的VM

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

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

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

更新

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

示例控制代码段:

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;
    }
}

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

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

1) 如果属性是从VM绑定的,控件不能检测到这些属性何时更改吗


2)如果您真的需要从XAML层调用“清除”,并希望保持它是纯MVVM,那么请考虑“表达式混合SDK”。

作为我的评论的后续内容。我怀疑您的命令是针对视图并直接清除

TextBox
es。相反,让命令以ViewModel为目标,并清除视图绑定到的属性。然后,您可以将该命令作为ViewModel上的一个属性,并在需要时调用它。

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

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

更新:

根据我的评论,请参阅当前设置的解决方案示例(未经测试的btw):


但我仍然会尝试将所有这些移动到虚拟机中。i、 e.像使用save命令一样,使用clear命令,并将textbox文本等绑定到VM中的属性,当调用该命令时,它会清除所有内容,然后您也可以从VM中的save方法轻松调用这些内容。但很明显,我不知道您要在长期内实现什么,也不知道selectedItem和文本框等如何关联,所以我想这取决于(一如既往)我的猜测。

通常,我的命令都在我的ViewModel上。在ViewModel中,您可以调用
MyCommand.Execute()。如果您的项目不是这样组织的,请发布一些代码进行说明。作为一种快速解决方法,您可以在注册SelectedItemProperty时在PropertyMetadata中设置PropertyChangedCallback,并在回调中清除所有需要的内容。不过,我建议您考虑重新构建您的设置,并将清除和所有绑定属性一起放入VM中。将ExampleCustomControl的所有属性绑定到viewmodel的问题是重用。我希望搜索逻辑/etc位于自定义控件中,并且我希望能够在几个不同的视图中重用相同的控件。自定义控件包含一个搜索文本框和一个显示结果的数据网格。所有要搜索的逻辑都包含在控件实现本身中。控件datagrid的选定值实际上是我在VM中绑定到的dependencyproperty。虽然我可以从VM中重置所选的值,但如何触发自定义控件中其余控件的重置?Alex-我认为您需要发布一些精简的示例代码以获得进一步的帮助。没有什么能比得上代码示例。让我接着说,虽然我可以从DependencyProperty触发事件,但该事件必须包含在静态范围中。这对重置控件的非静态作用域属性不起作用。我知道-您考虑过使用强制值回调吗-它引用DependencyObject?我已经用实际的代码片段更新了这个问题。如前所述,如果这意味着解决这个问题,我愿意对其进行全面重组。谢谢。建议的解决方案无法工作,因为SearchTextBox不是静态类型的。如果您使用DependencyObject并回溯到ExampleCustomControl,请参阅我的更新答案如何将ExampleCustomControl实现为视图/视图模型,以便可以重用该控件(以及包含的搜索功能)从我的TestViewModel中轻松地将其绑定到SelectedItem?这可能真的取决于您希望所有这些重用控件如何与主视图一起工作。但是我想说,您需要设置VM和视图,将视图绑定到它,然后在其他视图中简单地重用控件。然后,您需要让TestViewModel知道正在使用另一个VM中的哪一个(我猜是绑定到一个新属性),这样每当调用Save时,它就可以简单地通知被引用的VM。简而言之:从TestViewModel中保留对当前ExampleCustomControlViewModel的引用,以便可以访问Clear方法。希望这是有意义的。说了所有这些之后(并且在更好地了解了你想要达到的目标之后),你可能会更好地在控制范围内完成这一切
<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
    }
}
    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;
    }