wpf ViewModel中的DependencyProperty注册

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22295374/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 11:10:01  来源:igfitidea点击:

DependencyProperty Registration in ViewModel

wpfmvvmdependency-properties

提问by joerg

I am finding a lot of discussions about ViewModels and their Properties that compare two approches: implementation of INotifyPropertyChangedor implementation via Dependency Properties.

我发现很多关于 ViewModels 及其属性的讨论比较了两种方法:INotifyPropertyChanged 的实现或通过Dependency Properties 的实现。

While I am doing INotifyPropertyChanged a lot (and it's working) I am having difficulties implementing the DP-approach.

虽然我做了很多 INotifyPropertyChanged(并且它正在工作),但我在实施 DP 方法时遇到了困难。

When I am registering the DP in the ViewModel like this

当我像这样在 ViewModel 中注册 DP 时

    public static readonly DependencyProperty SomePropertyProperty =
        DependencyProperty.Register("SomeProperty", typeof(string), typeof(MyUserControl));

and trying to use it somewhere with:

并尝试在某处使用它:

<myNameSpace:MyUserControl SomeProperty="{Binding ...}"/>

there is an compiler error:

有一个编译器错误:

The property 'SomeProperty' does not exist in XML namespace 'clr-namespace:myNameSpace'.

What am I doing wrong??

我究竟做错了什么??



EDIT1

编辑1

The ViewModel looks like this:

ViewModel 看起来像这样:

public class MyUserControlVM : DependencyObject
{

    public string SomeProperty
    {
        get { return (string)GetValue(SomePropertyProperty); }
        set { SetValue(SomePropertyProperty, value); }
    }

    public static readonly DependencyProperty SomePropertyProperty =
        DependencyProperty.Register("SomeProperty", typeof(string), typeof(MyUserControl));     
}

采纳答案by Marc

Have you implemented the standard property accessors? A complete DP signature looks like this:

您是否实现了标准的属性访问器?完整的 DP 签名如下所示:

public static readonly DependencyProperty PropertyNameProperty =
        DependencyProperty.Register("propertyName", typeof (PropertyType), typeof (MyUserViewModel), new PropertyMetadata(default(PropertyType)));

    public PropertyType PropertyName
    {
        get { return (PropertyType) GetValue(PropertyNameProperty); }
        set { SetValue(PropertyNameProperty  value); }
    }

Then your code should work. One more info regarding DP's vs. INotifyPropertyChanged: For me, the main tradeoff is speed vs. readability. It's a pain littering your ViewModels with dependency property declarations, but you gain about 30% speed in the notification pipeline.

那么你的代码应该可以工作。关于 DP 与 INotifyPropertyChanged 的​​更多信息:对我来说,主要的权衡是速度与可读性。用依赖属性声明乱丢 ViewModel 是一件很痛苦的事情,但您在通知管道中获得了大约 30% 的速度。

EDIT:

编辑:

You register the property on the View's type, it should be the ViewModel's type, i.e.

您在 View 的类型上注册属性,它应该是 ViewModel 的类型,即

public static readonly DependencyProperty PropertyNameProperty =
        DependencyProperty.Register("propertyName", 
        typeof (PropertyType), 
        typeof (MyUserViewModel), 
        new PropertyMetadata(default(PropertyType)));

instead of

代替

public static readonly DependencyProperty PropertyNameProperty =
        DependencyProperty.Register("propertyName", 
        typeof (PropertyType),
        typeof (MyUserControl), 
        new PropertyMetadata(default(PropertyType)));

EDIT 2:

编辑2:

Ok, you're mixing something up here: You can have dependency properties both, on your ViewModel and your View. For the former, you define the DP in the control's codebehind (i.e. MyUserControl.xaml.cs). For the latter you define it in the ViewModel as I have shown it above. The problem with your code lies in the usage:

好的,您在这里混合了一些东西:您可以在 ViewModel 和 View 上都拥有依赖项属性。对于前者,您在控件的代码隐藏(即 MyUserControl.xaml.cs)中定义 DP。对于后者,您可以在 ViewModel 中定义它,如我上面所示。您的代码的问题在于用法:

You are trying to bind some value of your DataContextto a property called SomePropertyon the view:

您正在尝试将您的某些值绑定DataContextSomeProperty视图上调用的属性:

<myNameSpace:MyUserControl SomeProperty="{Binding SomePropertyBindingValue}"/>

As you've defined the dependency property on the view model, there is no property SomePropertyon the view, hence you get the compiler error. To make the above usage work, you need to put the DP in the View'scodebehind and define a normal property SomePropertyBindingValueon the ViewModel.

由于您已在视图模型上定义了依赖项属性,因此视图上没有属性SomeProperty,因此您会收到编译器错误。为了让上面的用法起作用,需要将DP放在View的codebehind中,并SomePropertyBindingValue在ViewModel上定义一个normal属性。

To define the DP on the ViewModel and use it in the view, you need to bind TO this property:

要在 ViewModel 上定义 DP 并在视图中使用它,您需要绑定到此属性:

<myNameSpace:MyUserControl Width="{Binding SomeProperty}"/>

Supposed you've wired up ViewModel and View correctly, this will bind the views width to your ViewModel's property SomeProperty. Now, if SomePropertyis set on the ViewModel, the UI will update, though you haven't implemented INPC.

假设您已正确连接 ViewModel 和 View,这会将视图宽度绑定到您的 ViewModel 的属性SomeProperty。现在,如果SomeProperty在 ViewModel 上设置,UI 将更新,尽管您尚未实现 INPC。

EDIT 3:

编辑 3:

From what I understand your problem is that - to get the desired behavior - you would need to bind one dependency property on the controlto two properties on separate ViewModels: One property on MainWindowVMshould be bound to the UserControland then - from the UserControl- back to another ViewModel(UserControl1VM). There is a bit of twist in the design here and without knowing the exact context, I don't see why you couldn't handle the property synch on ViewModel level:

据我了解,您的问题是 - 要获得所需的行为 - 您需要将控件上的一个依赖属性绑定到单独ViewModel上的两个属性:MainWindowVM上的一个属性应绑定到UserControl,然后 - 从UserControl-回到另一个ViewModel( UserControl1VM)。这里的设计有点扭曲,在不知道确切上下文的情况下,我不明白为什么您不能在 ViewModel 级别处理属性同步:

I let my ViewModels more or less resemble the nested structure of the View:

我让我的 ViewModel 或多或少类似于视图的嵌套结构:

Say you have a view (pseudo-code):

假设您有一个视图(伪代码):

<Window>
    <UserControl1 />
</Window>

Let the data context of the window be MainWM, whereever it comes from, this is not proper XAML(!):

让窗口的数据上下文为MainWM,无论它来自哪里,这都不是正确的 XAML(!):

<Window DataContext="[MainVM]">
    <UserControl1 />
</Window>

Question 1 is, why does the user control need it's own ViewModel? You could simply bind it to MainVMs property 'SomeProperty':

问题 1 是,为什么用户控件需要它自己的ViewModel?您可以简单地将其绑定到MainVM的属性“ SomeProperty”:

<Window DataContext="[MainVM]">
    <UserControl Text="{Binding SomeProperty}" />
</Window>

Ok, say you really have agood reason why you would need a UserControlViewModelwhich has it's own property 'UCSomeProperty':

好吧,说你真的有充分的理由需要一个UserControlViewModel,它有自己的属性“UCSomeProperty”:

public class UserControlVM
{
    public string UCSomeProperty { get; set; } // Let INPC etc. be implemented
}

Add a UserControlVMproperty to MainVM:

UserControlVM属性添加到MainVM

public class MainVM
{
    public UserControlVM UserControlVM { get; set; } // INPC etc.
}

Now, you can set up the binding:

现在,您可以设置绑定:

<Window DataContext="[MainVM]">
    <UserControl DataContext="{Binding UserControlVM}" 
                 Text="{Binding UCSomeProperty}" />
</Window>

Last, again without knowing your specific case and whether it makes sense, but let's say you now want a property on 'MainVM' which is in synch with the property on the user control's ViewModel's property:

最后,同样不知道您的具体情况以及它是否有意义,但假设您现在想要“MainVM”上的属性,该属性与用户控件的 ViewModel 属性上的属性同步:

public class MainVM
{
    public string SomeProperty
    {
         get { return UserControlVM.UCSomeProperty; }
         set { UserControlVM.UCSomeProperty = value; }
    }

    public UserControlVM UserControlVM { get; set; } // INPC etc.

    public MainVM()
    {
         UserControlVM = new UserControlVM();
         UserControlVM.NotifyPropertyChanged += UserControlVM_PropertyChanged;
    }

    private void UserControlVM_PropertyChanged(object sender, BlaArgs e)
    {
         if (e.PropertyName == "UCSomeProperty")
              RaisePropertyChanged("SomeProperty");
    }
 }

You could use the binding like this, for example:

您可以像这样使用绑定,例如:

 <Window DataContext="[MainVM]">
    <UserControl DataContext="{Binding UserControlVM}" 
                 Text="{Binding UCSomeProperty}" />
    <TextBlock Text="{Binding SomeProperty}" />
</Window>

SomeProperty on MainVMand UCSomePropertyon USerControlVMare always the same now and available on both ViewModels. I hope this helps...

MainVM上的SomePropertyUSerControlVM上的UCSomeProperty现在始终相同,并且在两个 ViewModel 上都可用。我希望这有帮助...

回答by Carbine

When you say

当你说

<myNameSpace:MyUserControl SomeProperty="{Binding ...}"/>

You mean create an object of class MyUserControland set its property SomePropertywith the Binding...

您的意思是创建一个类的对象MyUserControlSomeProperty使用 Binding设置其属性...

What you have done wrong - If you look at this line

你做错了什么 - 如果你看这条线

("SomeProperty", typeof(string), typeof(MyUserControl))

Just declaring the typeof(MyUserControl)is not enough to make the property available for MyUserControl

仅声明typeof(MyUserControl)不足以使该属性可用于MyUserControl

MyUserControlclass does not have the property SomePropertyinstead it is within the view Model hence you get the compiler error.

MyUserControl类没有属性,SomeProperty而是在视图模型中,因此您会收到编译器错误。

Ideally DependencyProperty should be declared within code behind of UserControl not in the ViewModel like the code below.

理想情况下,DependencyProperty 应该在 UserControl 后面的代码中声明,而不是像下面的代码那样在 ViewModel 中声明。

public class MyUserControl
{
    public string SomeProperty
    {
        get { return (string)GetValue(SomePropertyProperty); }
        set { SetValue(SomePropertyProperty, value); }
    }

    public static readonly DependencyProperty SomePropertyProperty =
        DependencyProperty.Register("SomeProperty", typeof(string), 
        typeof(MyUserControl));     
}

public class MyUserControlVM
{
  public string SomePropertyBindingValue{get;set;}
}

Then you can bind the Dependency property with the viewModel property. like this

然后您可以将 Dependency 属性与 viewModel 属性绑定。像这样

<myNameSpace:MyUserControl SomeProperty="{Binding SomePropertyBindingValue}"/>

Edit - Positing your question from your comment

编辑 - 根据您的评论提出您的问题

I have MainWindow and its DataContext MainWindowVM and a UserControl1 and its DataContext UserControl1VM in this MainWindow. I want to bind a (normal) property in MainWindowVM to a DP on UserControl1, that is again bound to a (normal) property in UserControl1VM. The connection that I am missing right now is the one between the DP in UserControl1 and the property in the UserControl1VM.

我在这个 MainWindow 中有 MainWindow 和它的 DataContext MainWindowVM 和一个 UserControl1 和它的 DataContext UserControl1VM。我想将 MainWindowVM 中的(正常)属性绑定到 UserControl1 上的 DP,然后再次绑定到 UserControl1VM 中的(正常)属性。我现在缺少的连接是 UserControl1 中的 DP 和 UserControl1VM 中的属性之间的连接。

I would suggest you to rethink your requirement, may be this is not the best approach to solve your problem. The reason is as follows:

我建议您重新考虑您的要求,这可能不是解决问题的最佳方法。原因如下:

You want a property to be common to both MainWindowVM and UserControl1VM and you are trying to achieve this by binding ViewModel to the View's Dependency property and passing it from the View back to the Child ViewModel. This breaks the purpose of MVVM pattern - separation of responsibility between View and its ViewModel.

您希望 MainWindowVM 和 UserControl1VM 都有一个属性,并且您试图通过将 ViewModel 绑定到 View 的 Dependency 属性并将其从 View 传递回 Child ViewModel 来实现这一点。这打破了 MVVM 模式的目的 - View 与其 ViewModel 之间的责任分离。

It would be easier if you pass it as a constructor parameter to UserControl1VM from MainWIndowVM. That way you can bind them to the View wherever you like rather than passing it via the View.

如果您将它作为构造函数参数从 MainWIndowVM 传递给 UserControl1VM 会更容易。这样你就可以将它们绑定到你喜欢的任何地方,而不是通过视图传递它。

回答by H?kan Fahlstedt

Besides from this:

除此之外:

public static readonly DependencyProperty SomePropertyProperty =
        DependencyProperty.Register("SomeProperty", typeof(string), typeof(MyUserControl));

You also need this:

你还需要这个:

public string SomeProperty 
{
    get 
    {
        return (string)GetValue(SomePropertyProperty);
    }
    set
    {
        SetValue(SomePropertyProperty, value);
    }
}