将WPF UserControl绑定到View Model和Code Behind

时间:2015-06-25 10:06:57

标签: wpf mvvm binding viewmodel dependency-properties

我正在尝试理解连接自定义控件以使用依赖项属性和视图模型的最佳方法。实现依赖项属性以公开可在XAML中使用的属性,以初始化视图模型中的属性。例如,在自定义控件的后面代码中,我可以定义以下依赖项属性:

public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
         "MyProperty", typeof(string), typeof(MyControl), new PropertyMetadata(null));

public string MyProperty
{
  get { return (string)GetValue(MyPropertyProperty); }
  set { SetValue(MyPropertyProperty, value); }
}

其中视图模型定义为

public class MyControlViewModel : INotifyPropertyChanged
   {
      public MyControlViewModel()
      {
         _myProperty = "Default View Model string";
      }

      private string _myProperty;

      public string MyProperty
      {
         get
         {
            return _myProperty;
         }
         set
         {
            _myProperty = value;
            OnPropertyChanged("MyProperty");
         }
      }

      public event PropertyChangedEventHandler PropertyChanged;

      protected virtual void OnPropertyChanged(string propertyName = null)
      {
         var handler = PropertyChanged;
         if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));

      }
   }

,自定义控件绑定到View Model MyProperty,如下所示

<UserControl x:Class="MyProject.MyControlView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:MyProject">

    <UserControl.DataContext>
        <local:MyControlViewModel/>
    </UserControl.DataContext>

    <Grid>
        <TextBlock Text="{Binding MyProperty}"/>                
    </Grid>

</UserControl>

现在我已经在自定义控件的代码中定义了依赖属性MyProperty,我希望能够使用它在ViewModel中初始化MyProperty。像这样的东西

 <Window x:Class="MyProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyProject"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:MyControlView MyProperty="This was set in XAML"/>
    </Grid>
</Window>

运行上面的内容将显示在View Model的构造函数中设置的字符串“Default View Model string”。如何连接Dependency Property值以便正确初始化View Model中的字符串?即它应显示“这是在XAML中设置的”。

更新

我可以在后面的代码中设置属性更改回调并在View Model中设置值,即

public static readonly DependencyProperty MyPropertyProperty =
          DependencyProperty.Register("MyProperty", typeof(string), typeof(MyControlView), new PropertyMetadata("Default", OnMyPropertyChanged));

      private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
         var view = d as MyControlView;
         if (view != null)
         {
            var viewModel = view.DataContext as MyControlViewModel;
            if (viewModel != null)
            {
               viewModel.MyProperty = e.NewValue as string;
            }
         }
      }

这是正确的还是闻到了?

2 个答案:

答案 0 :(得分:1)

您已创建了很好的依赖项属性。但是,通过实现视图模型并将其属性用于后面的代码,您已经紧密耦合。错误的方式。

你应该从&#34; Control&#34;继承你的班级。并且对于绑定值并在DP中创建一个custom control class,如下所示:

public static readonly DependencyProperty myValueProperty = DependencyProperty.Register(
            "MyProperty", typeof(object), typeof(FieldControl), new FrameworkPropertyMetadata(null, myValueChanged));

然后,

private static void myValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var fc = dependencyObject as FieldControl;
            if (fc != null)
            {
                fc.SetValue(BindingExpression1Key, fc.GetBindingExpression(ValueProperty));
                fc.RaiseValueChangedEvent(dependencyPropertyChangedEventArgs);
            }
        }

现在,使用它并绑定您的属性,如下所示:

 <local:MyControlView MyProperty="{Binding MyProperty, Mode=TwoWay}"/>

答案 1 :(得分:0)

您可以使用混合行为更新ViewModel(INPC)属性,该属性基于View的依赖属性(DP)。此解决方案避免了必须在代码隐藏中添加属性更改的回调:

public override bool Equals(object obj) {
    if (obj as Line == null) { return false; }
    var second = (Line)obj;
    if (this.X1 != second.X1 && this.X1 != second.X2) { return false; }
    if (this.Y1 != second.Y1 && this.Y1 != second.Y2) { return false; }
    if (this.X2 != second.X2 && this.X2 != second.X1) { return false; }
    if (this.Y2 != second.Y2 && this.Y2 != second.Y1) { return false; }
    return true;
}

您必须向项目添加对System.Windows.Interactivity和Microsoft.Expression.Interactivity.Core程序集的引用才能使用混合行为(请注意<UserControl x:Class="MyProject.MyControlView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions" xmlns:local="clr-namespace:MyProject" x:Name="MyControl"> <UserControl.DataContext> <local:MyControlViewModel/> </UserControl.DataContext> <i:Interaction.Triggers> <i:EventTrigger EventName="Loaded"> <ic:ChangePropertyAction TargetObject="{Binding}" PropertyName="MyProperty" Value="{Binding ElementName=MyControl, Path=MyProperty}" /> </i:EventTrigger> </i:Interaction.Triggers> <Grid> <TextBlock Text="{Binding MyProperty}"/> </Grid> i名称空间我补充说。)

在这种情况下,我使用调用ic的{​​{1}}挂钩到UserControl Loaded事件。通过为EventTrigger指定ChangePropertyAction,我告知行为使用绑定{Binding}中的视图模型。 TargetObject引用视图模型上的属性。 DataContext指的是视图上的DP。我给你的UserControl一个名字,这样我就可以在查询PropertyName的值时轻松引用它。

以这种方式执行此操作的好处意味着您可以在Value中交换视图模型(只要新模型也具有MyProperty属性),而无需更新代码 - 后面。