Я конвертирую некоторые представления и модели представлений нашего приложения 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 капот, кажется, что класс Catel UserControl
вводит модель представления как скрытый внутренний DataContext
только внутри визуального дерева, но мой {Binding}
внутри элемента словаря ресурсов может остаться в дураках.
Предполагая, что я прав, какое лучшее лекарство?
Если я прав насчет вышеизложенного, то я могу придумать несколько возможных средств, ни одно из которых не кажется идеальным. Я хотел бы знать, что является общепринятой передовой практикой для исправления этой ситуации.
- Переместите
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}" ...... >