문제

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.

도움이 되었습니까?

해결책

.

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:

<!-- 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>

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.

x:Shared Attribute

When set to false, modifies WPF resource-retrieval behavior so that requests for the attributed resource create a new instance for each request instead of sharing the same instance for all requests.

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:

<!-- 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>

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.

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

.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top