ICommand依赖属性

10

我有一个包含按钮的用户控件(UserControl)。这个按钮需要添加一些项目到用户控件里面的网格(Grid)中。我知道可以使用Click事件来实现这个功能。

但问题在于,我正在使用MVVM模式,直接更改数据不在相应的ViewModel中,这会破坏格式(这是我的意思)。

是否有一种方法可以创建一个ICommand依赖属性(Dependency Property),以便将该DP绑定到按钮,并在我的ViewModel中具有向Grid添加项目的功能?(我已经在我的UC和ViewModel中都有List,并且它们正常工作)

谢谢。


请提供您的问题和相关代码,以便我们能够为您提供支持。 - safi
7
问题在于我没有任何支持我的问题的代码,因为我不知道从哪里开始,而且我也不知道问题可以变得更简单多少。另一方面,我设法找到了一个解决方案。它已经发布在下面。谢谢。 - Xanagandr
3个回答

26

找到了一种解决方法,就是我一直在尝试的方式。 在这里留下答案,以便人们可以使用:

1)在您的用户控件的代码后台中,创建一个依赖属性。 我选择了ICommand,因为在我的ViewModel中将其设置为DelegateCommand:

public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register(
        "Command",
        typeof(ICommand),
        typeof(UserControl));

    public ICommand Command
    {
        get 
        { 
            return (ICommand)GetValue(CommandProperty); 
        }

        set 
        { 
            SetValue(CommandProperty, value); 
        }
    }

2) 在您的UserControl的XAML代码中,将此依赖属性(在本例中为按钮)进行绑定:

<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">

    <Button Command="{Binding Command}" />

</Grid>

3) 接下来,在您的ViewModel上声明一个Command属性,并进行相应的配置:

3) Next, 在您的ViewModel上声明一个Command属性,并进行相应的配置:

public ICommand ViewModelCommand { get; set; }

public ViewModelConstructor()
{
    ViewModelCommand = new DelegateCommand(ViewModelCommandExecute);
}

private void ViewModelCommandExecute()
{
    // Do something
}

4)最后,在嵌套了UserControl的View中,我们声明绑定:

<UserControls:UserControl Command={Binding ViewModelCommand}/>

通过这种方式,绑定将会发生,您可以将任何用户控件按钮的命令绑定到您的ViewModels上,而不会破坏MVVM。


3
我收到了以下错误信息:WPF错误40,BindingExpression路径错误:对象上未找到属性。第2条解决了我的问题。我漏掉了DataContext。 - MUG4N
我创建了一个简单的应用程序,展示了所描述代码的工作版本。示例 - Pawel Wujczyk
1
你如何将参数从用户控件传递到命令中,就像DataGridView使用其SelectedItem一样? - Robert Harvey

0

基本的方法是创建一个实现ICommand接口的对象(例如MyCommand),并将其嵌套在ViewModel中。在MyCommand内部,您无法访问ViewModel。您可以通过解决方法(例如在MyCommand构造函数中传递对ViewModel的引用)来解决此问题,但最终会产生太多代码(对于像这样的简单事情)。我认为几乎没有人真正这样做。

大多数人使用DelegateCommand来解决(大部分)以上问题。

最后,只需使用事件处理程序。
如果您像这样简单地编写它们:

void Grid_MouseMove(object sender, MouseEventArgs e)
{ viewModel.SaveMousePosition(e.GetPosition()); }

你没有违反任何MVVM规则。
而且你不能使用命令来处理上述事件。
大多数事件都没有MouseMove命令(也没有大多数事件的命令),而且你不能在命令中传递事件参数。

你可以使用Interaction.Triggers来处理每个事件,就像this
但是你仍然无法处理事件参数(并添加丑陋的XAML)。

对我来说,在WPF支持在事件处理程序中进行数据绑定之前,就像

Grid MouseMove="{Binding SaveMousePosition(e)}"

代码后台仍然是处理事件最有效的方式。


谢谢你的回答。问题是我试图避免使用事件处理程序,因为我也在使用MEF。我已经找到了我的问题的答案,但还是谢谢。 - Xanagandr
2
只是想指出,DelegateCommand 有时也被称为 RelayCommand,对于那些想要了解更多的人来说,这可能会有所帮助。 - Aleksandr Albert

0

我遇到了类似的问题,这个问题/答案对我帮助很大;所以我会在这里发布我的解决方案,以防其他人以后搜索。使用mvvm light制作。

我有一个自定义的winforms控件作为模型,一个WPF控件作为视图。因此,视图的xaml(我为我的视图创建了一个用户控件,没有app.xaml):

<UserControl.Resources>
    <ResourceDictionary>
        <viewModel:ViewModelLocator x:Key="Locator"  />
    </ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
    <Binding Path = "Main" Source="{StaticResource Locator}"></Binding>
</UserControl.DataContext>
<Grid>
      <Button Command="{Binding Zoom, ElementName=Wrapper}"></Button>

      <viewModel:ProfileWrapper x:Name="Wrapper"  >                   
      </viewModel:ProfileWrapper>
</Grid>

点击按钮会被路由到一个RelayCommand缩放ProfileWrapper(这是我的模型实现的地方)

然后,ProfileWrapper的XAML很简单:

<Grid>
    <WindowsFormsHost>
        <local:ManualControl x:Name="abc" ></local:ManualControl>
    </WindowsFormsHost>
</Grid>

ProfileWrapper 的代码后台:

public partial class ProfileWrapper : UserControl
{
    public ProfileWrapper()
    {
        InitializeComponent();
        test = abc;           
        Command = new RelayCommand(() => test.bZoomIn());
    }
    public ManualControl test;
    public RelayCommand Zoom { get; set; } 
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register(
            "Zoom",
            typeof(ICommand),
            typeof(ProfileWrapper));

    public ICommand Command
    {
        get
        {
            return (ICommand)GetValue(CommandProperty);
        }
        set
        {
            SetValue(CommandProperty, value);
        }
    }

}

我的MainViewModel类是空的,所有功能都在ProfileWrapper类中实现,这可能不太好,但至少它能工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接