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