简体   繁体   English

如何在MVVM模式中创建UserControl?

[英]How to create a UserControl in the MVVM pattern?

Currently, within a real-world application development, I am struggling with the consumption of a custom UserControl in the MVVM pattern. 当前,在一个实际的应用程序开发中,我正在努力使用MVVM模式中的自定义UserControl

In my application, there is a DataGrid where the user can select an entry. 在我的应用程序中,有一个DataGrid ,用户可以在其中选择一个条目。 The DataGrid 's SelectedItem is TwoWay -bound to a field of the ViewModel set as DataContext . DataGridSelectedItem TwoWay DataGrid到ViewModel的设置为DataContext的字段。 When the user selects an entry, the field is properly updated (tested). 当用户选择一个条目时,该字段将正确更新(测试)。 In the Page where holds the DataGrid , the field is bound through XAML to a DependencyProperty of a custom UserControl devised in the MVVM pattern : it bares its own ViewModel which is set as DataContext . 在保存DataGridPage中,该字段通过XAML绑定到以MVVM模式设计的自定义UserControlDependencyProperty :它裸露了自己的ViewModel,并将其设置为DataContext The trouble is that the UserControl 's DependencyProperty is not updated when the field changes even though the INotifyPropertyChanged interface is correctly implemented (see the comparison with a traditional control in the next minimal working example). 问题在于,即使正确实现了INotifyPropertyChanged接口,当字段更改时UserControlDependencyProperty也不会更新(请参见下一个最小的工作示例中与传统控件的比较)。

This example is constituted of a Label and bares ViewModelUserControl as a DataContext , UserControl1 is consumed by the MainWindow and the binding is compared to that of a Label . 本实施例中由一的Label和剥开ViewModelUserControl作为DataContextUserControl1由消耗MainWindow和结合相比较,一个的Label

The file MainWindow.xaml: 文件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>

The code-behind MainWindow.xaml.cs: 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;
        }
    }

The ViewModel of the MainWindow , the file ViewModelWindow.cs : 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";
}

The file UserControl1.xaml: 文件UserControl1.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>

The code-behind file UserControl1.xaml.cs: 代码隐藏文件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;
        }
    }

The ViewModel of UserControl1 , the file ViewModelUserControl.cs: 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 = "";
}

As you can see when executing this code, the MainWindow 's Label gets updated while the UserControl1 's Label doesn't. 正如您在执行此代码时看到的那样, MainWindowLabel被更新,而UserControl1Label不被更新。

What am I doing wrong? 我究竟做错了什么? Is there a way to makes this works? 有没有办法使它可行?

Many thanks in advance for any clue. 非常感谢您提供任何线索。

first you do not need to add anything in the UserControl just the XAML. 首先,您不需要在UserControl中添加任何内容,只需添加XAML。 Remove all the code of the UserControl and try. 删除所有的UserControl代码,然后尝试。

Let's explain why: 让我们解释一下原因:

Content="{Binding Text}" you set this in the usercontrol xaml, it's binded to the ViewModelWindow. 您在用户控件xaml中设置的Content =“ {Binding Text}”,它已绑定到ViewModelWindow。 and that works. 那行得通。 and remove in 并删除

  <Local:UserControl1 => Text="{Binding Text, Mode=OneWay}"

Ok, but it is correct to define a property in the user control in case of other situation?, that's right, in order to do that: 好的,但是在其他情况下在用户控件中定义属性是正确的吗?是的,这样做是正确的:

<UserControl x:Name="UserControlInstance"...>
<Label Content="{Binding Text, ElementName=UserControlInstance}" ...>

Where in this case Text is the dependency property and not the datacontext property. 在这种情况下,Text是依赖项属性,而不是datacontext属性。

Try the first option, and then the second defining just a Dependency Property and in this case bind the dependency property as you did. 尝试第一个选项,然后第二个仅定义依赖项属性,在这种情况下,像您一样绑定依赖项属性。 And a tip, if a dependency property is in the visual element tree like in your case you do not need to call the callback. 还有一个提示,如果依赖项属性位于可视元素树中(如您的情况),则无需调用回调。

Thank you Juan for your answer, here is the solution for conceiving the UserControl in the MVVM pattern: 感谢Juan的回答,这是在MVVM模式中构思UserControl的解决方案:

I gave the name root to the Grid of UserControl1 and set its DataContext : 我将root命名为UserControl1Grid并设置其DataContext

root.DataContext = view_model_usercontrol;

instead of: 代替:

DataContext = view_model_usercontrol;

Everything works fine. 一切正常。

Happy ending :) 美好的结局 :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM