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

Can't bind DependencyProperties from UserControl code behind to ViewModel

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

我的用户控件视图模型:

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

不要从 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 尽可能多地利用视图模型”有点矛盾。

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