【问题标题】:Can't bind DependencyProperties from UserControl code behind to ViewModel无法将后面的 UserControl 代码中的 DependencyProperties 绑定到 ViewModel
【发布时间】:2021-06-16 00:58:03
【问题描述】:

我正在尝试在我的 WPF 项目中创建一个 UserControl,我希望它应该有一个可以在父级中绑定的 DependencyProperty。该项目是用 MVVM 编写的,我使用的是 Caliburn micro。

我真的很想使用 MVVM 编写干净且可维护的代码,因此我希望我的 UserControls 尽可能多地利用视图模型并尽可能少地使用代码。

问题是我无法让父视图模型和 UserControl 视图模型之间的绑定正常工作。

我的用户控件:

    public partial class MyUserControlView : UserControl
    {
        public MyUserControlView()
        {
            InitializeComponent();
            // If no Datacontext is set, binding between parent property and textbox text works - one way only (set from parent)!.
            // -
    
            // If Datacontext is set to this, bindings with properties in MyUserControlView code behind works.
            //DataContext = this;
    
            // If Datacontext is set to MyUserControlViewModel, binding between MyUserControlViewModel and MyUserControlView works, but not with parent.
            DataContext = new MyUserControlViewModel();
        }
    
        public string ProjectNumber
        {
            get { return (string)GetValue(MyUserControlValueProperty); }
            set { SetValue(MyUserControlValueProperty, value); }
        }
    
        public static readonly DependencyProperty MyUserControlValueProperty =
            DependencyProperty.Register("ProjectNumber", typeof(string), typeof(MyUserControlView), new PropertyMetadata(null, new PropertyChangedCallback(OnProjectNumberUpdate)));
    
        private static void OnProjectNumberUpdate(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var view = d as MyUserControlView;
            view.ProjectNumberText.Text = e.NewValue as string;
        }
    }

MyUserControl 后面的代码:

<StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="In MyUserControl: " />
        <TextBlock Text="{Binding ProjectNumber}" />
    </StackPanel>
    <TextBox Name="ProjectNumberText" Text="{Binding ProjectNumber, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</StackPanel>

MyUserControl 视图模型:

public class MyUserControlViewModel : Screen
{
    private string _projectNumber;

    public string ProjectNumber
    {
        get { return _projectNumber; }
        set
        {
            _projectNumber = value;
            NotifyOfPropertyChange(() => ProjectNumber);
        }
    }
}

父视图:

<StackPanel>
    <local:MyUserControlView ProjectNumber="{Binding ParentProjectNumber}" />

    <StackPanel Orientation="Horizontal">
        <TextBlock Text="In parent: "/>
        <TextBlock Text="{Binding ParentProjectNumber}" />
    </StackPanel>
</StackPanel>

父视图模型:

public class ShellViewModel : Screen
{
    public ShellViewModel()
    {
        ParentProjectNumber = "Hello from parent!";
    }

    private string _parentProjectNumber;

    public string ParentProjectNumber
    {
        get { return _parentProjectNumber; }
        set
        {
            _parentProjectNumber = value;
            NotifyOfPropertyChange(() => ParentProjectNumber);
        }
    }
}

我知道我可能离这里很远,但我不知道该怎么做才能让绑定正常工作。

有没有更好的方法来绑定 DependencyProperty 和视图模型?我可以以某种方式将 DP 放入视图模型中吗?

这里是整个项目解决方案:https://github.com/ottosson/DependencyPropertyTest

【问题讨论】:

    标签: wpf mvvm data-binding dependency-properties


    【解决方案1】:

    不要从 UserControl 内部更改 UserControl.DataContext。它可以并且稍后产生问题。

    为 DP 使用正确的名称(ProjectNumberProperty 和相应的 ProjectNumber)并将 BindsTwoWayByDefault 添加到元数据:

    public partial class MyUserControlView : UserControl
    {
        public MyUserControlView()
        {
            InitializeComponent();
        }
    
        public string ProjectNumber
        {
            get { return (string)GetValue(ProjectNumberProperty); }
            set { SetValue(ProjectNumberProperty, value); }
        }
    
        public static readonly DependencyProperty ProjectNumberProperty = DependencyProperty.Register
        (
            "ProjectNumber", 
            typeof(string), 
            typeof(MyUserControlView), 
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
        );
    }
    

    修复 xaml 中的绑定:

    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="In MyUserControl: " />
            <TextBlock Text="{Binding Path=ProjectNumber, RelativeSource={RelativeSource AncestorType=UserControl}}" />
        </StackPanel>
        <TextBox Text="{Binding Path=ProjectNumber, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}" />
    </StackPanel>
    

    应该这样做。

    顺便说一句,“使用 MVVM 的干净和可维护的代码”和“希望我的 UserControls 尽可能地利用视图模型”有点矛盾。

    用户控件中的代码隐藏也没有错,只要该代码只处理视图功能。例如:DataGrid source code 包含 8000+ LoC

    【讨论】:

    • 谢谢。我同意一切都不必在 ViewModel 中。该示例已针对该问题进行了简化。然而,我错过了什么吗?我的问题是关于如何在视图模型和依赖项属性之间进行绑定,而在您的回答中我没有看到与视图模型的任何连接?
    • 与 vm 的连接在这里:&lt;local:MyUserControlView ProjectNumber="{Binding ParentProjectNumber}" /&gt;。它保持不变。您需要修复 MyUserControlView 才能正常工作
    • 也许我的问题并不清楚,但我想要一个用于我的用户控件的视图模型。我需要做一堆输入验证,我认为这很适合子 VM 而不是父 VM。
    • 然后在父 VM 类中公开子 VM。 class ParentVM { public ChildVM ChildVMInstance {get;set;} }。更改绑定local:MyUserControlView ProjectNumber="{Binding ChildVMInstance.ProjectNumber}" /&gt;。那是您用于用户控制的视图模型。在用户控件中创建视图模型会破坏 mvvm。视图层次结构(嵌套视图)需要视图模型层次结构(嵌套视图模型)
    猜你喜欢
    • 2020-02-19
    • 2018-10-29
    • 1970-01-01
    • 1970-01-01
    • 2016-07-11
    • 2021-11-05
    • 1970-01-01
    • 1970-01-01
    • 2011-07-19
    相关资源
    最近更新 更多