将Action注入UserControl

时间:2014-01-22 09:21:57

标签: c# wpf mvvm command dependency-properties

我的观点包含ButtonUserControlUserControl还包含Button。我视图中的Button1和UserControl中的Button2都应该在ViewModel中调用相同的方法。

ViewViewModelUserControl

因此,我需要RelayCommandUserControl“注入”(不知道如何调用它)。我认为依赖属性会很容易,但我无法让它工作。

我尝试了什么:

UserControl包含依赖项属性,该属性在包含UserControl的视图的XAML代码中设置:

UserControl代码:

public const string LoadDataCommandPropertyName = "LoadDataCommand";
public Action LoadDataCommand
{
    get
    {
        return (Action)GetValue(LoadDataCommandProperty);
    }
    set
    {
        SetValue(LoadDataCommandProperty, value);
    }
}

public static readonly DependencyProperty LoadDataCommandProperty = DependencyProperty.Register(
    LoadDataCommandPropertyName,
    typeof(Action),
    typeof(GridViewPersistenceController),
    new UIPropertyMetadata());

查看Xaml代码:(在UserControl中使用dp)

<customControls:MyUserControl Grid.Column="1" 
                              x:Name="RadGridViewSettingsPersistenceControl" 
                              GridControl="{Binding ElementName=RadGridView}"
                              LoadDataCommand="{Binding ActionTest}"
                              />

UserControl的LoadDataCommand绑定到ViewModel中的此属性

private const string ActionTestPropertyName = "ActionTest";
private Action actionTest;
public Action ActionTest
{
    get
    {
        return this.actionTest;
    }

    set
    {
        this.RaisePropertyChanging(ActionTestPropertyName);
        this.actionTest = value;
        this.RaisePropertyChanged(ActionTestPropertyName);
    }
}

ActionTest属性在ViewModel

的构造函数中初始化
public MyViewModel()
        {
            this.ActionTest = new Action(this.LoadData);
        }

visual studio输出窗口也给了我一个绑定错误(我不明白)

  

System.Windows.Data错误:40:BindingExpression路径错误:   'object'''MyUserControl'上找不到'ActionTest'属性   (名称= 'RadGridViewSettingsPersistenceControl')”。   BindingExpression:路径= ActionTest;的DataItem = '的MyUserControl'   (名称= 'RadGridViewSettingsPersistenceControl');目标元素是   'MyUserControl'(Name ='RadGridViewSettingsPersistenceControl');目标   property是'LoadDataCommand'(输入'Action')

我发现了一种有效的解决方法,但我不喜欢它。我在View codebehind的LoadedEvent中设置了LoadDataCommand。它看起来很麻烦,我觉得我错过了一些重要的概念。

//TODO: Dirty coding, fix me
    private void MyView_OnLoaded(object sender, RoutedEventArgs e)
    {
        this.RadGridViewSettingsPersistenceControl.LoadDataCommand = new Action((this.DataContext as MyViewModel).LoadData);
    }

问题:

  • 如何使用View中的xaml代码将ViewModel中定义的Command / Action传递给UserControl?
  • 为什么我目前使用绑定的方法不起作用?
  • 我错过了一些基本概念吗? (我可以通过代表更轻松地实现这一目标吗?(我试过......))

2 个答案:

答案 0 :(得分:5)

从错误中可以看出:

  在'对象'''MyUserControl'上找不到'ActionTest'属性   (名称= 'RadGridViewSettingsPersistenceControl')'

绑定引擎正在搜索控件中的属性,而不是在ViewModel中搜索。 (默认情况下,它在DataContext控件中搜索属性,我怀疑你已经在代码中的某个地方设置了控件的DataContext)

使用 RelativeSource获取UserControl的DataContext ,这将是您的ViewModel。

LoadDataCommand="{Binding DataContext.ActionTest,
                   RelativeSource={RelativeSource Mode=FindAncestor,
                                           AncestorType=UserControl}}"

此外,不是创建Action类型的DP,而是使用ICommand并在ViewModel中创建ICommand并绑定到它。

答案 1 :(得分:2)

作为替代方案,您可以使用附加依赖项属性。以下是一个例子:

AttachedDependencyProperty

public static class UserControlExtension
{
    public static readonly DependencyProperty ActionProperty;

    public static void SetAction(DependencyObject DepObject, ICommand value)
    {
        DepObject.SetValue(ActionProperty, value);
    }

    public static ICommand GetAction(DependencyObject DepObject)
    {
        return (ICommand)DepObject.GetValue(ActionProperty);
    }

    static UserControlExtension()
    {
        ActionProperty = DependencyProperty.RegisterAttached("Action",
                                                            typeof(ICommand),
                                                            typeof(UserControlExtension));
    }
}

TestViewModel

public class TestViewModel
{
    private ICommand _testButtonCommand = null;

    public ICommand TestButtonCommand
    {
        get
        {
            if (_testButtonCommand == null)
            {
                _testButtonCommand = new RelayCommand(param => this.TestButton(), null);
            }

            return _testButtonCommand;
        }
    }

    private void TestButton() 
    {
        MessageBox.Show("Test command execute");
    }
}

MainWindowView

<Window.Resources>
    <local:TestViewModel x:Key="TestVM" />
</Window.Resources>

<Grid DataContext="{StaticResource TestVM}">
    <local:TestUserControl x:Name="TestUserControl"
                           AttachedProperties:UserControlExtension.Action="{Binding TestButtonCommand}" />
</Grid>

UserControl

<Grid>
    <Button Name="TestButton"
            HorizontalAlignment="Center" 
            VerticalAlignment="Center"
            Content="TestContent"
            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, 
                              Path=(AttachedProperties:UserControlExtension.Action)}" />        
</Grid>

示例项目可用here