Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
644 views
in Technique[技术] by (71.8m points)

wpf - Why is CompositeCollection not Freezable?

I am writing an application using the MVVM pattern. I am providing data to my view by setting my view's DataContext property to an instance of my ViewModel. Generally I just use Binding from there and go about my way.

Recently, I tried to implement a ComboBox with an "extra" element beyond the collection my ViewModel provides that says "Select Item".

<ComboBox>    
    <ComboBox.ItemsSource>    
        <CompositeCollection>
           <ComboBoxItem IsEnabled="False">Select Item</ComboBoxItem>
           <CollectionContainer Collection="{Binding MyItemsCollection}" />    
        </CompositeCollection>
    </ComboBox.ItemsSource>
</ComboBox>

The problem is, CompositeCollection is not a Freezable: Freezable Objects Overview. This causes only the static ComboBoxItem to appear and none of the results from my binding expression.

My initial reaction to the problem was to just implement my own version of CompositeCollection that was Freezable. This, though, begs the following question:

Why isn't CompositeCollection a Freezable in the first place?

My concern is that generally these decisions are made for a reason and I don't feel I know enough about Freezable to say why they didn't inherit from it. I know I can implement this collection, but I'm concerned there will be a measurable difference in performance if I do.

Any help would be appreciated. Thanks!

Also: please note that I realize I can insert a Null or some other special value and provide and template or valueconverter to do what I want. This is not the question I'm interested in... only the question in bold above.

Update:

After some further research brought on by ArsenMkrt's comment, I'm led to believe this was actually an oversight. The evidence is this:

  1. There is a collection that is freezable called FreezableCollection<T>. It does not produce CollectionViews, which makes it inappropriate for my needs directly.
  2. Sam Bent of MSFT says as much in the above link. I cannot find contact information for him yet, but I plan on discussing this with him if I get the chance.

My current plan to get around this problem is to create a new collection with the properties of CompositeCollection and FreezableCollection<T>. I don't know if it'll work yet, but I'm thinking about something like this:

public class BindableCompositeCollection : FreezableCollection<object>, ICollectionViewFactory

If anyone has a better option, I'd like to hear it!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I just tried this tonight:

public class State
{
    public string Code { get; set; }
    public string Name { get; set; }
}

public class MyWindowViewModel
{
    ObservableCollection<State> _states = new ObservableCollection<State>
    {
        new State { Code = "FL", Name = "Florida" },
        new State { Code = "CA", Name = "California" },
    };

    public ObservableCollection<State> States
    {
        get
        {
            return _states;
        }
    }
}
<Window x:Class="WpfApplication1.MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:app="clr-namespace:WpfApplication1"
        Title="Window1"
        Height="300"
        Width="300">

  <Window.Resources>
    <app:ServiceLocator x:Key="Locator" />
  </Window.Resources>

  <StackPanel>
    <ComboBox x:Name="TestCombo" SelectedIndex="0" DisplayMemberPath="Name" SelectedValuePath="Code">
      <ComboBox.ItemsSource>
        <CompositeCollection>
          <app:State Code="" Name="Select a state..." />
          <app:State Code="TX" Name="Texas" />
          <CollectionContainer Collection="{Binding Source={StaticResource Locator}, Path=MyWindowViewModel.States}" />
        </CompositeCollection>
      </ComboBox.ItemsSource>
    </ComboBox>
  </StackPanel>
</Window>

The key here is to create an instance of your service locator as a static resource then go through it to get to your viewmodel. The service locator can wire up to instances of the ViewModel using Unity or whatever DI you want.

Edit:

Actually in my silverlight app I create the service locator as a static resoure in the App.xaml and then bind my other UserControls/Windows/Pages DataContext to a ViewModel property of the service locator. It should still work the same way for the combo boxes though even if the service locator is instantiated in the App.xaml's resources. I wish there was a silverlight version of CompositeCollection that I could use. This would work great for the app I'm working on. :(


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...