【问题标题】:Equivalent to WPF DependencyProperties in Prism ViewModels等效于 Prism ViewModels 中的 WPF DependencyProperties
【发布时间】:2017-09-04 21:26:12
【问题描述】:

我了解 WPF 在 XAML 中公开自定义属性的标准方法是在 View 的代码隐藏中将其定义为 DependencyProperty

但是,这只适用于DependencyObjects,例如UserControl。然而,以干净的 Prism 方式,我的代码隐藏(即,派生自 UserControl 的类)是空的,我处理我的视图模型中的所有逻辑,它派生自 BindableBase,它不是子类DependencyObject.

考虑以下 XAML 片段:

<MyNamespace:MyCustomView MyProperty={Binding} />

MyCustomViewModel的核心是

private string myProperty;
public string MyProperty {
  get { return myProperty; }
  set { SetProperty(ref myProperty, value); }

我对 Prism 还比较陌生。我该怎么做才能公开在我的MyCustomViewModel 中定义的MyProperty,以便我可以在 XAML 中使用与上述类似的标签对其进行设置?


更新

根据@mm8 的回答和我们在相应 cmets 中的讨论,我开发了一个最小(非)工作示例来说明我的想法。先总结一下:

  • 数据模型是一个对象列表。
  • Shell 必须通过此对象类型的自定义用户控件来显示这些对象中的每一个。

A) 外壳

A.1) XAML

XAML 很简单。

<Window x:Class="MyProject.Views.MainWindow"
        Name="MainWindowName"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        xmlns:MyNamespace="clr-namespace:MyProject.Views"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Title="{Binding Title}" Height="350" Width="525">
  <ItemsControl ItemsSource="{Binding StringCollection, ElementName=MainWindowName}">
    <ItemsControl.ItemTemplate>
      <DataTemplate>
        <MyNamespace:MyUserControl MyTargetProperty="{Binding}" />
      </DataTemplate>
    </ItemsControl.ItemTemplate>
  </ItemsControl>
</Window>

A.2) 代码隐藏

代码隐藏包含数据模型定义;实际上,我当然会在 Models 命名空间中定义它。

using System.Collections;
using System.Windows;

namespace MyProject.Views {
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window {
    public MainWindow() {
      InitializeComponent();

      StringCollection = new ArrayList();
      StringCollection.Add("String 1");
      StringCollection.Add("String 2");
      StringCollection.Add("String 3");
    }

    private ArrayList stringCollection;
    public ArrayList StringCollection {
      get { return stringCollection; }
      set { stringCollection = value; }
    }
  }
}

A.3) 查看模型

视图模型是 Prism 代码模板提供的标准视图模型。

using Prism.Mvvm;

namespace MyProject.ViewModels {
  public class MainWindowViewModel : BindableBase {
    private string _title = "Prism Unity Application";
    public string Title {
      get { return _title; }
      set { SetProperty(ref _title, value); }
    }

    public MainWindowViewModel() {
    }
  }
}

B) 自定义用户控件

这就是乐趣的开始。最后,我希望能够访问MyUserControlViewModel 中的MyTargetProperty,因为我想在其上调用复杂的程序逻辑,这取决于数据模型的其他工作,因此不会被放置在代码隐藏。

B.1) XAML

非常天真;只包含一个标签。

<UserControl x:Class="MyProject.Views.MyUserControl"
             Name="UserControlName"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/"             
             prism:ViewModelLocator.AutoWireViewModel="True">
    <Label Content="{Binding MyTargetProperty, ElementName=UserControlName}" Background="AliceBlue"/>
</UserControl>

B.2) 代码隐藏

这是我将 target 属性 声明为DependencyProperty 的地方,正如@mm8 的回答中所建议的那样。

using System.Windows;
using System.Windows.Controls;

namespace MyProject.Views {
  /// <summary>
  /// Interaction logic for MyUserControl
  /// </summary>
  public partial class MyUserControl : UserControl {
    public MyUserControl() {
      InitializeComponent();
    }

    public static readonly DependencyProperty MyTargetPropertyProperty = DependencyProperty.Register("MyTargetProperty", typeof(string), typeof(MyUserControl));
    public string MyTargetProperty {
      get { return (string)GetValue(MyTargetPropertyProperty); }
      set { SetValue(MyTargetPropertyProperty, value); }
    }
  }
}

B.3) 查看模型

视图模型定义了源属性

using Prism.Mvvm;

namespace MyProject.ViewModels {
  public class MyUserControlViewModel : BindableBase {
    public MyUserControlViewModel() {
    }

    private string mySourceProperty;
    public string MySourceProperty {
      get { return mySourceProperty; }
      set { SetProperty(ref mySourceProperty, value); }
    }
  }
}

我这辈子都不知道如何在MyUserControl 的视图模型中访问我在MainWindowItemTemplate 中设置的值。

【问题讨论】:

  • 要使MyProperty={Binding} 工作,MyProperty 必须是依赖属性。您必须在后面的 UserControl 代码中声明它,这并没有错。在 UserControl 中包含“空代码”是没有意义的。更一般地说,UserControl 永远不应该有任何“自己的”视图模型。相反,视图模型实例通常从其父元素(例如 Window)传递给 UserControl 的 DataContext(通过依赖属性值继承)。
  • 感谢@Clemens,这给了我一些可以玩的东西。使用 Prism 的 ViewModelLocator.AutoWireViewModel="True" 确实在内部将 DataContext 设置为每个 View 的单个 ViewModel,但我同意在更通用的方法中不必如此。不知道如何将我的代码隐藏中的DependencyProperty 连接到 ViewModel 的代码,不过……

标签: c# wpf xaml mvvm prism


【解决方案1】:

只有 target(视图)属性必须是依赖属性。因此,为了能够将任何东西绑定到这样的属性,在这种情况下,它必须是像MyProperty 这样的依赖属性:

<MyNamespace:MyCustomView MyProperty="{Binding SourceProperty}" />

视图模型中的 source 属性可能是普通的 CLR 属性:

public string SourceProperty { get; set; }

因此,您的视图模型不必(也不应该!)从 DependencyObject 继承,但视图应该。

【讨论】:

  • 如果我理解正确的话,你所说的就是@Clemens 在上面评论中所写的内容,即在我的视图代码隐藏中将MyProperty 定义为DependencyProperty。不过,我还没有看到如何在不违反 MVVM 原则的情况下使 MyProperty 可用于 ViewModel 中的程序逻辑。对此的任何想法都非常感谢。
  • 我不确定我理解你的意思。您将视图的 MyProperty 的值绑定到视图模型的源属性,并处理视图模型中源属性值与目标值相同的任何逻辑属性,可用。
  • 啊,我明白了——这听起来很可行。但它似乎只适用于用户设置的目标属性,例如TextBoxText。当我想将MyProperty 设置为ItemsControl.ItemTemplate 中的迭代元素时会发生什么?在那种情况下,我不能同时绑定到迭代元素和SourceProperty,从而破坏了视图和视图模型之间的链接,还是我错了?
  • 我还是不明白。您将视图中的属性绑定到视图模型的属性,然后值在两者之间“流动”。 如何 设置目标属性并不重要。您的确切问题是什么?
  • 我添加了一个解释性的基本代码示例来说明我的意思。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多