在Prism ViewModels中与WPF DependencyProperties相当的是什么?

3
我知道在WPF中,将自定义属性暴露给XAML的标准方式是将其定义为视图代码后台中的“DependencyProperty”。但这仅适用于如“UserControl”等“DependencyObject”。然而,在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的答案和我们在相应评论中的讨论,我开发了一个最小(非)工作示例,展示我所想要的内容。首先是一个摘要:

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

A) Shell

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) 代码后置

这里我将目标属性声明为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); }
    }
  }
}

我真的无法弄清楚如何访问我在MainWindowItemTemplate中设置的值,以及如何在MyUserControl的视图模型中使用它们。


2
为了使 MyProperty={Binding} 起作用,MyProperty 必须是一个依赖属性。您必须在 UserControl 的代码后台中声明它,这没有任何问题。在 UserControl 中拥有“空代码后台”是毫无意义的。更一般地说,UserControl 不应该有任何“自己的”视图模型。相反,通常会将视图模型实例通过依赖属性值继承从其父元素(例如 Window)传递到 UserControl 的 DataContext 中。 - Clemens
谢谢@Clemens,这让我有了一些东西可以玩耍。使用Prism的ViewModelLocator.AutoWireViewModel="True"会在内部将DataContext设置为每个View的单个ViewModel,但我认为在更一般的方法中,这并非必要。不过我还不确定如何将代码后台中的DependencyProperty与ViewModel的代码连接起来... - Informagic
1个回答

1

仅目标视图属性必须是依赖属性。因此,为了使您能够将任何内容绑定到此类属性,它必须是像此处的MyProperty一样的依赖属性:

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

在视图模型中,source属性可以是一个普通的CLR属性:
public string SourceProperty { get; set; }

所以你的视图模型不需要(也不应该!)继承自DependencyObject,但是视图需要。


如果我理解正确的话,你所说的与@Clemens在上面评论中写的类似,即在我的视图的代码后台中将MyProperty定义为DependencyProperty。然而,我还没有看到如何在不违反MVVM原则的情况下使MyProperty可用于我在ViewModel中拥有的程序逻辑。对此的任何想法都会受到高度赞赏。 - Informagic
我不确定我理解你的意思。你需要将视图中MyProperty的值与视图模型的源属性绑定,并在视图模型中处理任何逻辑,其中源属性的值与目标属性的值相同。 - mm8
啊,我明白了——这听起来是可行的。但它似乎仅适用于用户设置的目标属性,例如 TextBoxText。当我想将 MyProperty 设置为 ItemsControl.ItemTemplate 中的迭代元素时会发生什么呢?在这种情况下,我无法同时绑定迭代元素和 SourceProperty,从而破坏视图和视图模型之间的链接,还是我错了? - Informagic
我还是不明白。你将视图中的属性绑定到视图模型的属性上,然后值就在两者之间“流动”。目标属性的设置方式并不重要。你的确切问题是什么? - mm8
所以,在一次事故后,我又回来了。由于不能用手打字,这不可避免地让我有些时间来思考。:-/ 你是对的 - 我确实在看问题上有一个重大的怪癖。此外,你的答案当然是完全正确的。感谢你在面对我像砖头一样愚笨时的耐心! - Informagic
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接