如何在MVVM模式中创建UserControl
本文关键字:创建 UserControl 模式 MVVM | 更新日期: 2023-09-27 18:04:02
目前,在实际的应用程序开发中,我正在努力解决在MVVM模式中使用自定义UserControl
的问题。
在我的应用程序中,有一个DataGrid
,用户可以在其中选择条目。DataGrid
的SelectedItem
是TwoWay
绑定到ViewModel集的字段DataContext
。当用户选择一个条目时,该字段被适当地更新(测试)。在保存DataGrid
的Page
中,该字段通过XAML绑定到MVVM模式中设计的自定义UserControl
的DependencyProperty
:它暴露了自己的ViewModel,设置为DataContext
。问题是,即使INotifyPropertyChanged
接口被正确实现,当字段发生变化时,UserControl
的DependencyProperty
也没有更新(参见下一个最小工作示例中与传统控件的比较)。
本例由Label
组成,并将ViewModelUserControl
作为DataContext
, UserControl1
被MainWindow
消耗,并与Label
的绑定进行比较。
文件MainWindow.xaml:
<Window x:Class="UserControlWithinUserControlDataContext.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Local="clr-namespace:UserControlWithinUserControlDataContext"
Title="MainWindow"
Height="350" Width="525"
>
<StackPanel Orientation="Horizontal"
>
<ListBox SelectedItem="{Binding Text, Mode=TwoWay}"
x:Name="listbox"
Height="150"
>
</ListBox>
<Local:UserControl1 Text="{Binding Text, Mode=OneWay}"
Height="50" Width="150"
/>
<Label Content="{Binding Text, Mode=OneWay}"
/>
</StackPanel>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public ViewModelWindow view_model_window
{
get { return _view_model; }
}
private ViewModelWindow _view_model = new ViewModelWindow();
public MainWindow()
{
InitializeComponent();
DataContext = view_model_window;
IList<String> list = new List<String>();
list.Add("A");
list.Add("B");
list.Add("C");
listbox.ItemsSource = list;
}
}
MainWindow
的ViewModel,文件ViewModelWindow.cs:
public class ViewModelWindow : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public String Text
{
get { return text; }
set
{
if (text != value)
{
text = value;
NotifyPropertyChanged("Text");
}
}
}
private String text = "Bli";
}
文件usercontrol .xaml:
<UserControl x:Class="UserControlWithinUserControlDataContext.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Label Content="{Binding Text}"
Background="Magenta"
HorizontalAlignment="Stretch"
/>
</Grid>
</UserControl>
代码隐藏文件UserControl1.xaml.cs:
public partial class UserControl1 : UserControl
{
public ViewModelUserControl view_model_usercontrol
{
get { return _view_model; }
}
private ViewModelUserControl _view_model = new ViewModelUserControl();
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(UserControl1),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(TextPropertyChangedCallback)));
private static void TextPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControl1 user_control = d as UserControl1;
if(user_control != null)
{
user_control.view_model_usercontrol.Text = user_control.Text;
}
}
public UserControl1()
{
InitializeComponent();
DataContext = view_model_usercontrol;
}
}
UserControl1
的ViewModel,文件ViewModelUserControl.cs:
public class ViewModelUserControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public String Text
{
get { return text; }
set
{
if (text != value)
{
text = value;
NotifyPropertyChanged("Text");
}
}
}
private String text = "";
}
正如你在执行这段代码时看到的,MainWindow
的Label
被更新了,而UserControl1
的Label
没有。
我做错了什么?有办法让它起作用吗?
首先,您不需要在UserControl中添加任何内容,只需添加XAML。删除UserControl的所有代码并尝试。
让我们解释一下原因:
Content="{Binding Text}"你在usercontrol xaml中设置了这个,它被绑定到ViewModelWindow。这是可行的。然后删除
<Local:UserControl1 => Text="{Binding Text, Mode=OneWay}"
Ok,但是在其他情况下,在用户控件中定义属性是正确的吗?,这是正确的,为了这样做:
<UserControl x:Name="UserControlInstance"...>
<Label Content="{Binding Text, ElementName=UserControlInstance}" ...>
这里的Text是依赖属性,而不是数据上下文属性。
尝试第一个选项,然后第二个选项只定义一个依赖属性,在这种情况下,像你一样绑定依赖属性。提示一下,如果依赖属性在可视元素树中,就像你的例子一样,你不需要调用回调。
感谢Juan的回答,以下是在MVVM模式中构思UserControl
的解决方案:
我将UserControl1
的Grid
命名为root
,并设置其DataContext
:
root.DataContext = view_model_usercontrol;
代替:
DataContext = view_model_usercontrol;
一切正常
大团圆结局