tags:

views:

489

answers:

3
A: 

make a user control. add a SourceList dep. Property. Add a delegate property for your callback, add a delegate property for your "is in list" filter

use two ListBox controls ( a left and a right ), use CollectionViewSource to set the source of the two lists, using the filter delegate to determine membership.

Muad'Dib
Ok, thanks.. I think the keyword is ** delegate property ** Will look into this and post if I make noticeable progress...
Christian
you can check out the CollectionViewSource class to see what your delegate should look like
Muad'Dib
+1  A: 

How would I do it?

  • Create a control called PickList that subclasses ItemsControl and includes commands for picking a single item, picking all items, unpicking all items etcetera
  • Create a class called PickListItem that has an IsPicked property
  • Define the control template for PickList to include two ListBoxes and a bunch of buttons for picking one, all etcetera. The template would include a couple of CollectionViewSources to segregate those items that are picked (which would be on the right) from those that are not (which would be on the left)

You would then use this control just like any other ItemsControl, and reuse it for any data type you might have:

<PickList ItemsSource="{Binding People}">
    <PickList.ItemContainerStyle>
        <Style TargetType="{x:Type PickListItem}">
            <Setter Property="IsPicked" Value="{Binding IsRich}"/>
        </Style>
    </PickList.ItemContainerStyle>
</PickList>

HTH, Kent

Kent Boogaart
What class would PickListItem inherit from? DependencyObject, ContentControl, ...?
jpierson
ContentControl - just like ListBoxItem, for example.
Kent Boogaart
Ok, I'm trying out your method as it seems to be the one suggested which is most consistent with how WPF was designed to work. I'm having trouble however and any clarification or extra details on how the rest of this would be implemented would be helpful.
jpierson
+1  A: 

I think I understand what you are after, this should get you started.

I am assuming that your usercontrol has two listviews, one intended for the true items named "TrueList", the other for the false items named "FalseList".

Extend your usercontrol from ItemsCollection and bind the ItemsSource property of each listview to the ItemsSource of the parent usercontrol.

Add a TrueFilter and a FalseFilter property to your usercontrol:

   Predicate<object> trueFilter;
   public Predicate<object> TrueFilter
   {
        get
        {
             return trueFilter;
        }
        set 
        {
             if (trueFilter!= null && this.TrueList.Items != null)
                 this.TrueList.Items.Filter -= trueFilter;

             trueFilter = value;

             if (trueFilter!= null && this.TrueList.Items != null)
                 this.TrueList.Items.Filter += trueFilter;
        }
    }

   Predicate<object> falseFilter;
   public Predicate<object> FalseFilter
   {
        get
        {
             return falseFilter;
        }
        set 
        {
             if (falseFilter!= null && this.FalseList.Items != null)
                 this.FalseList.Items.Filter -= falseFilter;

             filter = value;

             if (falseFilter!= null && this.FalseList.Items != null)
                 this.FalseList.Items.Filter += falseFilter;
        }
    }

Then create an "IToggle" (or some other more meaningful name) interface:

public interface IToggle
{
   Predicate<object> TrueFilter { get; }
   Predicate<object> FalseFilter { get; }
}

Then, extend ObservableCollection for each of your custom classes, implementing the "IToggle" interface:

public class Cars : ObservableCollection<Car>, IToggle
{
    Predicate<object> trueFilter;
    public Predicate<object> TrueFilter
    {
        get
        {
            if (trueFilter == null)
               trueFilter = new Predicate<object>(this.TrueFilterPredicate);
            return trueFilter;
        }
    }

    private bool TrueFilterPredicate(object value)
    {
       Car car = (Car)value;
       return car.IsFast;
    }

    Predicate<object> falseFilter;
    public Predicate<object> FalseFilter
    {
        get
        {
            if (falseFilter == null)
               falseFilter = new Predicate<object>(this.FalseFilterPredicate);
            return falseFilter;
        }
    }

    private bool FalseFilterPredicate(object value)
    {
       Car car = (Car)value;
       return !car.IsFast;
    }

Next, override the ItemsSource property on your user control:

public new IEnumerable ItemsSource
{
   get { return base.ItemsSource; }
   set
   {
        if (value != null && !(value is IToggle))
           throw new Exception("You may only bind this control to collections that implement IToggle.");

        base.ItemsSource = value;

        this.TrueFilter = base.ItemsSource == null ? null : (base.ItemsSource as IToggle).TrueFilter;
        this.FalseFilter = base.ItemsSource == null ? null : (base.ItemsSource as IToggle).FalseFilter;
   }
}

Finally, call TrueList.Items.Refresh() and FalseList.Items.Refresh() on your event callbacks to refresh the item views whenever you switch an item from true to false, and vice versa.

This solution still requires writing some implementation code for each custom class (the true and false filters), but it should keep the extra code to a minimum.

Alternatively, it would be a much simpler solution if you gave each of your custom classes a common interface, something like:

public interface Valid
{
   bool IsValid { get; set; }
}

Then you could use a single set of filters (or style setters, or databinding with converters) to work against the "Valid" interface. Instead of "Car.IsFast" and "Fruit.ILikeIt" you would use "Car.IsValid" and "Fruit.IsValid".

Josh
Thanks A LOT, I think this is the way to go.. I will implement it the way you described and check if there is anything unclear and come back with feedback..
Christian