如何在MVVM模式中创建UserControl?

时间:2015-07-22 07:50:35

标签: c# wpf xaml mvvm datagrid

目前,在实际的应用程序开发中,我正在努力消耗MVVM模式中的自定义UserControl

在我的应用程序中,有一个DataGrid,用户可以在其中选择一个条目。 DataGrid的{​​{1}}为SelectedItem - 绑定到ViewModel设置为TwoWay的字段。当用户选择条目时,该字段被正确更新(测试)。在保存DataContext的{​​{1}}中,字段通过XAML绑定到MVVM模式中设计的自定义Page的{​​{1}}:它将自己的ViewModel裸露出来设为DataGrid。问题是即使DependencyProperty接口正确实现,当字段发生更改时UserControl的{​​{1}}也不会更新(请参阅下一个最小工作示例中与传统控件的比较) )。

此示例由DataContext和bares UserControl构成DependencyPropertyINotifyPropertyChanged消耗Label,并将绑定与之比较一个ViewModelUserControl

文件MainWindow.xaml:

DataContext

代码隐藏的MainWindow.xaml.cs:

UserControl1

MainWindow的ViewModel,文件ViewModelWindow.cs:

Label

文件UserControl1.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>

代码隐藏文件UserControl1.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,文件ViewModelUserControl.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 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> 的{​​{1}}会更新, 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; } } 的{​​{1}}则不会。

我做错了什么?有没有办法让它有效?

非常感谢任何线索。

2 个答案:

答案 0 :(得分:0)

首先,您不需要在UserControl中仅添加XAML中的任何内容。删除UserControl的所有代码并尝试。

让我们解释一下原因:

Content =“{Binding Text}”你在usercontrol xaml中设置它,它绑定到ViewModelWindow。这很有效。并删除

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

好的,但是在其他情况下在用户控件中定义属性是正确的吗?这是正确的,为了做到这一点:

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

在这种情况下,Text是依赖属性,而不是datacontext属性。

尝试第一个选项,然后第二个选项只定义一个依赖项属性,在这种情况下绑定依赖项属性。 还有一个提示,如果依赖属性在可视元素树中,就像你的情况一样,你不需要调用回调。

答案 1 :(得分:0)

感谢Juan的回答,这是在MVVM模式中构思UserControl的解决方案:

我将名称root提供给Grid的{​​{1}}并设置其UserControl1

DataContext

而不是:

root.DataContext = view_model_usercontrol;

一切正常。

快乐结局:)