从 Catel WPF UserControl 中的 ResourceDictionary 中绑定
Binding from within a ResourceDictionary in a Catel WPF UserControl
我正在将我们的 WPF 应用程序的一些视图和视图模型转换为 Catel,作为概念验证。
其中一个用户控件在运行时似乎没有正确绑定到视图模型。我想我明白为什么会这样,但想就什么是最好的补救措施获得一些反馈。
密码
我有一个简单的视图,其模型实际上是一个 ObservableCollection
:
PersonTable.xaml
需要注意的关键事项:我正在使用 CollectionViewSource
包装 DataGrid
绑定到的主集合。这样我就可以保持网格自动排序。
<catel:UserControl x:Class="MyApp.PersonTable"
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"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:catel="http://catel.codeplex.com"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="200" d:DataContext="{DynamicResource DesignTimeViewModel}">
<UserControl.Resources>
<ResourceDictionary>
<CollectionViewSource Source="{Binding PersonItems}" x:Key="PersonItemsSource">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="DOB" Direction="Descending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<ui:DesignPersonViewModel x:Key="DesignTimeViewModel" />
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Source={StaticResource PersonItemsSource}}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name, Mode=TwoWay}"
Header="Name" Width="90"
ElementStyle="{StaticResource CellRightAlign}" />
<!-- etc..... -->
</DataGrid.Columns>
</DataGrid>
</Grid>
</catel:UserControl>
PersonTableViewModel.cs
视图模型接受构造函数中的模型:
using Catel.MVVM;
public class PersonTableViewModel : ViewModelBase
{
public PersonTableViewModel(ObservableCollection<Person> personItems)
{
this.PersonItems = personItems
}
public ObservableCollection<Person> PersonItems
{
get { return GetValue<ObservableCollection<Person>>(PersonItemsProperty); }
set { SetValue(PersonItemsProperty, value); }
}
public static PropertyData PersonItemsProperty =
RegisterProperty("PersonItems", typeof(ObservableCollection<Person>), () => new ObservableCollection<PersonItems>());
}
问题
在运行时,网格中没有填充任何项目。尽管在设计时,设计视图模型确实正确地填充了设计视图中的网格。
我对问题的根源是否正确? 我认为绑定到 PersonItems
属性 的控件不属于可视化树,而是嵌入在控件级资源字典中?根据我对文档的阅读,特别是文章 UserControl - Under the hood,Catel UserControl
class 似乎仅将视图模型作为隐藏的内部 DataContext
注入到可视化树中,但我在资源字典项中的 {Binding}
可能会被冷落。
假设我是对的,最好的补救方法是什么?
如果我的上述观点是正确的,那么我可以想到一些可能的补救措施,none 其中看起来很完美。我很想知道补救这种情况的公认最佳做法是什么。
- 将
CollectionViewSource
移至后面的代码;将其作为依赖项公开 属性。我不喜欢这个选项,因为我无法在 XAML 中配置它。
- 将
CollectionViewSource
移动到视图模型。我真的不喜欢这个;将 WPF 组件放入视图模型会破坏 MVVM。
将 CollectionViewSource
绑定到原始 DataContext
(即模型)。问题在于设计时视图模型无法正确绑定。
<CollectionViewSource Source="{Binding}" ..... >
从绑定到视图模型的代码隐藏中公开依赖项 属性。 更新:这在运行时有效,但现在在设计时失败(因为网格不包含测试数据。)
---- PersonTable.xaml.cs ----
[ViewToViewModel(MappingType = ViewToViewModelMappingType.ViewModelToView]
public ObservableCollection<PersonItem> PersonItems { get { ... } }
---- PersonTable.xaml ----
<CollectionViewSource Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type PersonTable}}, Path=PersonItems}" ...... >
你的假设都是正确的。但是还有第四种补救措施。将资源放在网格中,这样您就在 ViewModel 数据上下文中:
<catel:UserControl x:Class="MyApp.PersonTable"
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"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:catel="http://catel.codeplex.com"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="200" d:DataContext="{DynamicResource DesignTimeViewModel}">
<Grid>
<Grid.Resources>
<ResourceDictionary>
<CollectionViewSource Source="{Binding PersonItems}" x:Key="PersonItemsSource">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="DOB" Direction="Descending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<ui:DesignPersonViewModel x:Key="DesignTimeViewModel" />
</ResourceDictionary>
</Grid.Resources>
<DataGrid ItemsSource="{Binding Source={StaticResource PersonItemsSource}}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name, Mode=TwoWay}"
Header="Name" Width="90"
ElementStyle="{StaticResource CellRightAlign}" />
<!-- etc..... -->
</DataGrid.Columns>
</DataGrid>
</Grid>
</catel:UserControl>
我正在将我们的 WPF 应用程序的一些视图和视图模型转换为 Catel,作为概念验证。
其中一个用户控件在运行时似乎没有正确绑定到视图模型。我想我明白为什么会这样,但想就什么是最好的补救措施获得一些反馈。
密码
我有一个简单的视图,其模型实际上是一个 ObservableCollection
:
PersonTable.xaml
需要注意的关键事项:我正在使用 CollectionViewSource
包装 DataGrid
绑定到的主集合。这样我就可以保持网格自动排序。
<catel:UserControl x:Class="MyApp.PersonTable"
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"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:catel="http://catel.codeplex.com"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="200" d:DataContext="{DynamicResource DesignTimeViewModel}">
<UserControl.Resources>
<ResourceDictionary>
<CollectionViewSource Source="{Binding PersonItems}" x:Key="PersonItemsSource">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="DOB" Direction="Descending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<ui:DesignPersonViewModel x:Key="DesignTimeViewModel" />
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Source={StaticResource PersonItemsSource}}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name, Mode=TwoWay}"
Header="Name" Width="90"
ElementStyle="{StaticResource CellRightAlign}" />
<!-- etc..... -->
</DataGrid.Columns>
</DataGrid>
</Grid>
</catel:UserControl>
PersonTableViewModel.cs
视图模型接受构造函数中的模型:
using Catel.MVVM;
public class PersonTableViewModel : ViewModelBase
{
public PersonTableViewModel(ObservableCollection<Person> personItems)
{
this.PersonItems = personItems
}
public ObservableCollection<Person> PersonItems
{
get { return GetValue<ObservableCollection<Person>>(PersonItemsProperty); }
set { SetValue(PersonItemsProperty, value); }
}
public static PropertyData PersonItemsProperty =
RegisterProperty("PersonItems", typeof(ObservableCollection<Person>), () => new ObservableCollection<PersonItems>());
}
问题
在运行时,网格中没有填充任何项目。尽管在设计时,设计视图模型确实正确地填充了设计视图中的网格。
我对问题的根源是否正确? 我认为绑定到 PersonItems
属性 的控件不属于可视化树,而是嵌入在控件级资源字典中?根据我对文档的阅读,特别是文章 UserControl - Under the hood,Catel UserControl
class 似乎仅将视图模型作为隐藏的内部 DataContext
注入到可视化树中,但我在资源字典项中的 {Binding}
可能会被冷落。
假设我是对的,最好的补救方法是什么?
如果我的上述观点是正确的,那么我可以想到一些可能的补救措施,none 其中看起来很完美。我很想知道补救这种情况的公认最佳做法是什么。
- 将
CollectionViewSource
移至后面的代码;将其作为依赖项公开 属性。我不喜欢这个选项,因为我无法在 XAML 中配置它。 - 将
CollectionViewSource
移动到视图模型。我真的不喜欢这个;将 WPF 组件放入视图模型会破坏 MVVM。 将
CollectionViewSource
绑定到原始DataContext
(即模型)。问题在于设计时视图模型无法正确绑定。<CollectionViewSource Source="{Binding}" ..... >
从绑定到视图模型的代码隐藏中公开依赖项 属性。 更新:这在运行时有效,但现在在设计时失败(因为网格不包含测试数据。)
---- PersonTable.xaml.cs ---- [ViewToViewModel(MappingType = ViewToViewModelMappingType.ViewModelToView] public ObservableCollection<PersonItem> PersonItems { get { ... } } ---- PersonTable.xaml ---- <CollectionViewSource Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type PersonTable}}, Path=PersonItems}" ...... >
你的假设都是正确的。但是还有第四种补救措施。将资源放在网格中,这样您就在 ViewModel 数据上下文中:
<catel:UserControl x:Class="MyApp.PersonTable"
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"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:catel="http://catel.codeplex.com"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="200" d:DataContext="{DynamicResource DesignTimeViewModel}">
<Grid>
<Grid.Resources>
<ResourceDictionary>
<CollectionViewSource Source="{Binding PersonItems}" x:Key="PersonItemsSource">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="DOB" Direction="Descending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<ui:DesignPersonViewModel x:Key="DesignTimeViewModel" />
</ResourceDictionary>
</Grid.Resources>
<DataGrid ItemsSource="{Binding Source={StaticResource PersonItemsSource}}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name, Mode=TwoWay}"
Header="Name" Width="90"
ElementStyle="{StaticResource CellRightAlign}" />
<!-- etc..... -->
</DataGrid.Columns>
</DataGrid>
</Grid>
</catel:UserControl>