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
相关产品推荐
相关产品推荐

