WPF数据绑定:CollectionViewSource和ObjectDataProvider问题

时间:2010-12-01 09:04:31

标签: wpf data-binding 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
}

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

当我从ObjectDataProvider DetailScopes中取出CollectionViewSource并将其直接绑定到ItemsSource AccountDetail中的组合框DataTemplate时,问题就消失了。但是,我确实在CollectionViewSource中需要它,因为我对它应用了一些过滤,我无法对ObjectDataProvider应用过滤。

有人可以解释为什么会发生这种情况以及我实际上应该如何连接CollectionViewSourceObjectDataProvider?谢谢。

1 个答案:

答案 0 :(得分: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>

希望它能解决你的问题!

但是,此解决方案将导致另一个问题。让我引用一些来自MSDN的内容,这样你就可以理解x:共享的内容了。

  

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>

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

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

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