WPF 数据绑定:CollectionViewSource 和 ObjectDataProvider 问题
我有一个 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}}" />
及其代码隐藏类,我在其中定义了详细范围的过滤器:
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;
}
最后,一个枚举:
public enum DetailScope
{
Private,
Business,
Other
}
当我运行代码时,我获取一个填充了一堆帐户详细信息的列表框,每个详细信息都有自己的组合框(包含选定的范围)和一个包含适当值的文本框。问题是组合框中的所有选定值都与最后输入的详细信息设置的范围相匹配,并且更改任何组合框值都会更新所有这些值,就好像它们都绑定到相同的帐户详细信息一样。
当我从 CollectionViewSource
DetailScopes 中取出 ObjectDataProvider
并将其直接绑定到 DataTemplate
中组合框的 ItemsSource
时AccountDetail,问题就解决了。但是,我确实需要在 CollectionViewSource
中使用它,因为我正在对其应用一些过滤,并且无法对 ObjectDataProvider
应用过滤。
有人可以解释为什么会发生这种情况以及我实际上应该如何连接 CollectionViewSource
和 ObjectDataProvider
吗?谢谢。
I have a MainWindow.xaml file:
<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}}" />
and its code-behind class, where I defined the filter for detail 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;
}
}
}
}
Next, there's the AccountDetail
class:
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;
}
Finally, an enum:
public enum DetailScope
{
Private,
Business,
Other
}
When I run my code, I get a list box populated with a bunch of account details, each having its own combo box with a selected scope and a text box with the appropriate value. The problem is that all the selected values in the combo boxes match the scope set for the last entered detail and changing any of the combo box values updates all of them, as if they are all bound to the same account detail.
When I take out the ObjectDataProvider
from the CollectionViewSource
DetailScopes and bind it directly to the combo box's ItemsSource
in the DataTemplate
AccountDetail, the problem is gone. However, I do need it inside the CollectionViewSource
because I am applying some filtering to it and I cannot apply filtering to ObjectDataProvider
.
Could someone please explain why is this happening and how am I actually supposed to connect CollectionViewSource
and ObjectDataProvider
? Thank you.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
。
您的代码的问题是每个 ComboBox 都使用相同的 CollectionViewSource 实例;这意味着,带有“DetailScopes”键的资源由所有 ComboBox 共享,因此每当您从特定 ComboBox 中选择一个值时,它会自动在所有 ComboBox 中选择相同的值。 这是因为共享的底层集合会跟踪所选项目,并且由于它在从一个 ComboBox 中进行选择时发生变化,因此 CollectionViewSource 会将更改通知给所有 ComboBox。
所以解决办法很简单。使 DetailScopes 资源不可共享所需的一切。
这是修复方法:
希望它能解决您的问题!
然而,这个解决方案会带来另一个问题。让我引用一下 MSDN 中的一些内容,以便您了解 x:Shared 的作用。
由于 x:Shared 会在您尝试访问资源时创建资源的新实例(新副本),这意味着 Filter 处理程序方法仅附加到您在代码隐藏中获得的实例,而不是所有实例。
因此,为了使您的处理程序正常工作,您需要从 XAML 本身附加处理程序,如下所示:
希望它能解决您的所有问题。如果您仍然面临任何问题,请告诉我。:-)
哦,顺便说一句,不再需要以下代码隐藏。所以请删除它。
。
.
The problem with your code is that every ComboBox is using the same instance of CollectionViewSource; that means, the resource with key "DetailScopes" is shared by all ComboBox, so whenever you select one value from a particular ComboBox, it automatically selects the same value in all ComboBox. It's because the underlying collection which is shared, keeps track of the selected item, and since it changes on selecting from one ComboBox, the CollectionViewSource notifies the change to ALL ComboBox.
So the solution is very simple. All you need to make DetailScopes resource unsharable.
Here is the fix:
Hope it solves your problem!
However, this solution will cause another problem. Let me quote something from MSDN, so that you'll understand what x:Shared does.
Since x:Shared causes to create a new instance (a new copy) of the resource whenever you attempt to access it, that means, the Filter handler method is attached only to the instance which you get in the code-behind, not all the instances.
So in order to work your handler properly, you need to attach the Handler from XAML itself, like this:
Hope it solves all your problems. Let me know if you still face any.:-)
Oh by the way, the following code-behind is not needed anymore. So please remove it.
.