Обязательство данных WPF: CollectionViewsource и ObjectDataProvider
-
29-09-2019 - |
Вопрос
У меня есть файл mainwindow.xaml:
<Window.Resources>
<CollectionViewSource x:Key="cvs"
Source="{Binding Source={StaticResource ResourceKey=DetailsCollection}}" />
<CollectionViewSource x:Key="DetailScopes">
<CollectionViewSource.Source>
<ObjectDataProvider
MethodName="GetValues"
ObjectType="{x:Type system:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="entities:DetailScope" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</CollectionViewSource.Source>
</CollectionViewSource>
<DataTemplate x:Key="AccountDetail"
DataType="{x:Type entities:AccountDetail}">
<DockPanel>
<ComboBox
DockPanel.Dock="Left"
ItemsSource="{Binding Source={StaticResource ResourceKey=DetailScopes}}"
SelectedItem="{Binding Path=Scope}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Converter={StaticResource DetailScopeConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Text="{Binding Path=Value}" />
</DockPanel>
</DataTemplate>
</Window.Resources>
...
<ListBox
ItemTemplate="{StaticResource ResourceKey=AccountDetail}"
ItemsSource="{Binding Source={StaticResource ResourceKey=cvs}}" />
и его кодовый класс, где я определил фильтр для деталей Scopes:
public class MainWindow
{
public MainWindow()
{
CollectionViewSource detailScopes;
InitializeComponent();
// Attach filter to the collection view source
detailScopes = this.Resources["DetailScopes"] as CollectionViewSource;
detailScopes.Filter += new FilterEventHandler(DetailScopesFilter);
private void DetailScopesFilter(object sender, FilterEventArgs e)
{
DetailScope scope;
scope = (DetailScope)e.Item;
if (scope == DetailScope.Private ||
scope == DetailScope.Business)
{
e.Accepted = true;
}
else
{
e.Accepted = false;
}
}
}
}
Далее есть AccountDetail
класс:
public class AccountDetail
{
public string Value
{
get { return this.value; }
set { this.value = value; }
}
public DetailScope Scope
{
get { return scope; }
set { scope = value; }
}
private string value;
private DetailScope scope;
}
Наконец, Enum:
public enum DetailScope
{
Private,
Business,
Other
}
Когда я запускаю свой код, я получаю список в списке, заполненную кучей сведений с учетной записью, каждый из которых имеет собственную комбинированную коробку с выбранным объемом и текстовым ящиком с соответствующим значением. Проблема в том, что все выбранные значения в поле комбинированы соответствуют установленному набору охвата для последних введенных деталей и изменением любого из значений Combo Box обновляет все их, как если бы они все связаны с тем же деталями аккаунта.
Когда я вынуму ObjectDataProvider
от CollectionViewSource
Подробные кабины и связывают его непосредственно к коробке комбо ItemsSource
в DataTemplate
AccountDetail, проблема ушла. Тем не менее, мне нужно это внутри CollectionViewSource
потому что я применяю его фильтрацию к нему, и я не могу применить фильтрацию к ObjectDataProvider
.
Может кто-нибудь, пожалуйста, объясните, почему это происходит и как я на самом деле должен подключаться CollectionViewSource
и ObjectDataProvider
? Спасибо.
Решение
.
Проблема с вашим кодом в том, что Каждый Combobox использует один и тот же экземпляр CollectionViewsource; Это означает, что ресурс с ключом «Подробные кабины» поделился всем Combobox, поэтому всякий раз, когда вы выбираете одно значение из определенного Combobox, он автоматически выбирает одно и то же значение во всех Combobox. Это связано с тем, что базовая коллекция, которая передается, поддерживает отслеживание выбранного элемента, и поскольку он меняется на выборе из одного COBLOBOX, CollectionViewsource уведомляет изменение для всех Combobox.
Так что решение очень просто. Все, что вам нужно, чтобы сделать детали нерешительный.
Вот исправление:
<!-- Please note this x:Shared="False" just after x:Key="DetailsScopes" --->
<CollectionViewSource x:Key="DetailScopes" x:Shared="False">
<CollectionViewSource.Source>
<ObjectDataProvider
MethodName="GetValues"
ObjectType="{x:Type system:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="entities:DetailScope" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</CollectionViewSource.Source>
</CollectionViewSource>
Надеюсь, это решает вашу проблему!
Однако это решение приведет к другой проблеме. Позвольте мне процитировать что-то от MSDN, чтобы вы поймете, что X: Shared делает.
x: Общий атрибут
При установке в False изменяет поведение ресурсов WPF-Resource, так что запросы на приписываемый ресурс Создайте новый экземпляр для каждого запроса Вместо того, чтобы разделить тот же экземпляр для всех запросов.
Поскольку X: Shared Proseses создать новый экземпляр (новую копию) ресурса, когда вы пытаетесь получить доступ к ней, это означает, что метод обработчика фильтра прикреплен только к экземпляру, который вы попадаете в код, не все экземпляры.
Таким образом, чтобы правильно работать обработчик, вам нужно прикрепить обработчик от сама XAML, как это:
<!-- Now please note Filter="DetailsScopesFilter" --->
<CollectionViewSource x:Key="DetailScopes" x:Shared="False" Filter="DetailScopesFilter">
<CollectionViewSource.Source>
<ObjectDataProvider
MethodName="GetValues"
ObjectType="{x:Type system:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="entities:DetailScope" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</CollectionViewSource.Source>
</CollectionViewSource>
Надеюсь, это решает все ваши проблемы. Дайте мне знать, если вы все еще сталкиваетесь. :-)
О, кстати, следующий код не нужен больше. Поэтому, пожалуйста, удалите его.
// Attach filter to the collection view source
detailScopes = this.Resources["DetailScopes"] as CollectionViewSource;
detailScopes.Filter += new FilterEventHandler(DetailScopesFilter);
.