无法将 DependencyProperties 从后面的 UserControl 代码绑定到 ViewModel

时间:2021-03-18 15:35:42

标签: wpf mvvm data-binding dependency-properties

我正在尝试在我的 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

1 个答案:

答案 0 :(得分: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 的干净和可维护的代码”和“希望我的用户控件尽可能多地利用视图模型”相互矛盾。

UserControl 中的代码隐藏也没有问题,只要该代码仅处理视图功能即可。例如:DataGrid source code 包含 8000+ LoC

相关问题