WPF数据绑定:CollectionViewSource和ObjectDataProvider问题

Bor*_*ris 1 data-binding wpf objectdataprovider collectionviewsource

我有一个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}}" />
Run Code Online (Sandbox Code Playgroud)

和它的代码隐藏类,我在其中为详细信息范围定义了过滤器:

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来是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;
}
Run Code Online (Sandbox Code Playgroud)

最后,一个枚举:

public enum DetailScope
{
  Private, 
  Business, 
  Other
}
Run Code Online (Sandbox Code Playgroud)

当我运行我的代码时,我得到一个列有一堆帐户详细信息的列表框,每个列表框都有自己的带有选定范围的组合框和一个具有适当值的文本框.问题是组合框中的所有选定值都与最后输入的详细信息设置的范围相匹配,并且更改任何组合框值会更新所有这些值,就好像它们都绑定到相同的帐户详细信息一样.

当我拿出ObjectDataProviderCollectionViewSourceDetailScopes并直接将其绑定到组合框ItemsSourceDataTemplateAccountDetail,问题就消失了.但是,我确实需要它,CollectionViewSource因为我正在应用一些过滤,我不能应用过滤ObjectDataProvider.

可能有人请解释为什么会出现这种情况和我怎么实际上应该连接CollectionViewSourceObjectDataProvider?谢谢.

Naw*_*waz 5

.

你的代码的问题是每个ComboBox都使用相同的CollectionViewSource实例 ; 这意味着具有键"DetailScopes"的资源由所有 ComboBox 共享,因此每当您从特定ComboBox中选择一个值时,它会自动在所有ComboBox中选择相同的值.这是因为共享的底层集合跟踪所选项目,并且由于它从一个ComboBox中进行选择时发生更改,因此CollectionViewSource会将更改通知给所有ComboBox.

所以解决方案非常简单.所有你需要做DetailScopes资源不能分享.

这是修复:

<!-- 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>
Run Code Online (Sandbox Code Playgroud)

希望它能解决你的问题!

但是,此解决方案将导致另一个问题.让我引用一些来自MSDN的内容,以便您了解x:Shared的作用.

x:共享属性

设置为false时,修改WPF资源检索行为,以便对属性资源的请求为每个请求创建新实例,而不是为所有请求共享同一实例.

由于x:共享导致在您尝试访问它时创建资源的新实例(新副本),这意味着,Filter处理程序方法仅附加到您在代码隐藏中获得的实例,而不是所有实例.

因此,为了正确地处理您的处理程序,您需要从XAML本身附加Handler,如下所示:

<!-- 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>
Run Code Online (Sandbox Code Playgroud)

希望它能解决你所有的问题.如果您仍然面对任何问题,请告诉我.:-)

哦顺便说一下,不再需要以下代码隐藏了.所以请删除它.

    // Attach filter to the collection view source
    detailScopes = this.Resources["DetailScopes"] as CollectionViewSource;
    detailScopes.Filter += new FilterEventHandler(DetailScopesFilter);
Run Code Online (Sandbox Code Playgroud)

.