You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF如何从父视图XAML设置自定义UserControl的ViewModel属性

问题

在MainView的XAML中使用自定义控件MyControl(基于MVVM模式开发),情况如下:

  • MyControl.xaml通过绑定定义布局,已配置ViewModel自动关联;
  • MyControl.xaml.cs仅包含构造函数,且定义了需支持在Styles.xaml资源字典中设置的Number依赖属性;
  • MainView的C#代码未做修改。

但在MainView.xaml中设置MyControl的Number属性时,无法看到该属性。相关代码如下:

MyControl.xaml

<UserControl x:Class="MainWindowModule.Views.MyControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:prism="http://prismlibrary.com/" 
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:viewmodels="clr-namespace:MainWindowModule.ViewModels"
             d:DataContext="{d:DesignInstance Type=viewmodels:MyControlViewModel}"
             prism:ViewModelLocator.AutoWireViewModel="True">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <prism:InvokeCommandAction Command="{Binding LoadedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    
    <Grid>
        <Rectangle x:Name="rect" Width="{Binding Width}" />
    </Grid>
</UserControl>

MyControl.xaml.cs

public partial class MyControl:UserControl {

        public static readonly DependencyProperty NumberProperty =
            DependencyProperty.Register("Number", typeof(int), typeof(MyControl), new PropertyMetadata(0));

        public MyControl() {
            InitializeComponent();
        }
    }

MyControlViewModel.cs

public class MyControlViewModel:BindableBase {

        public int Number {
            get { return _number; }
            set { SetProperty(ref _number, value); }
        }

        public int Width {
            get { return _width; }
            set { SetProperty(ref _width, value);}
        }

        public DelegateCommand LoadedCommand { get; set; }

        private int _number = 0;
        private int _width = 100;

        public MyControlViewModel() {
            LoadedCommand = new DelegateCommand(OnLoad);
        }

        private void OnLoad() {
            //Load resources
        }
    }

MainView.xaml中MyControl的声明

<Grid>
   <local:MyControl x:Name="test" />
</Grid>

请问如何解决这个问题,让我能在MainView.xaml中设置MyControl的Number属性值?


解决方案

1. 为Number依赖属性添加CLR包装器

仅定义NumberProperty依赖属性是不够的,XAML设计器需要对应的CLR属性包装器才能识别该属性。修改MyControl.xaml.cs,添加CLR属性:

public int Number
{
    get { return (int)GetValue(NumberProperty); }
    set { SetValue(NumberProperty, value); }
}

完整修改后的MyControl.xaml.cs:

public partial class MyControl:UserControl {

        public static readonly DependencyProperty NumberProperty =
            DependencyProperty.Register("Number", typeof(int), typeof(MyControl), new PropertyMetadata(0));

        // 添加CLR属性包装器
        public int Number
        {
            get { return (int)GetValue(NumberProperty); }
            set { SetValue(NumberProperty, value); }
        }

        public MyControl() {
            InitializeComponent();
        }
    }

添加完成后,即可在MainView.xaml中直接设置Number属性:

<Grid>
   <local:MyControl x:Name="test" Number="10" />
</Grid>

2. 同步控件依赖属性与ViewModel的Number属性(可选)

若需要让控件的Number属性和ViewModel中的Number属性双向同步,需在依赖属性注册时添加属性变更回调,并监听ViewModel的属性变化:

修改NumberProperty的注册代码,添加PropertyChangedCallback:

public static readonly DependencyProperty NumberProperty =
    DependencyProperty.Register("Number", typeof(int), typeof(MyControl), new PropertyMetadata(0, OnNumberChanged));

private static void OnNumberChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var control = d as MyControl;
    var viewModel = control.DataContext as MyControlViewModel;
    if (viewModel != null && viewModel.Number != (int)e.NewValue)
    {
        viewModel.Number = (int)e.NewValue;
    }
}

在控件构造函数中监听ViewModel的PropertyChanged事件:

public MyControl()
{
    InitializeComponent();
    this.DataContextChanged += MyControl_DataContextChanged;
}

private void MyControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    var oldViewModel = e.OldValue as MyControlViewModel;
    if (oldViewModel != null)
    {
        oldViewModel.PropertyChanged -= ViewModel_PropertyChanged;
    }

    var newViewModel = e.NewValue as MyControlViewModel;
    if (newViewModel != null)
    {
        newViewModel.PropertyChanged += ViewModel_PropertyChanged;
    }
}

private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    if (e.PropertyName == nameof(MyControlViewModel.Number))
    {
        var viewModel = sender as MyControlViewModel;
        if (viewModel != null && this.Number != viewModel.Number)
        {
            this.SetValue(NumberProperty, viewModel.Number);
        }
    }
}

此配置可实现控件属性与ViewModel属性的双向同步,满足MVVM模式下的数据一致性需求。


内容的提问来源于stack exchange,提问作者Aenye_Cerbin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 02:28:13