Skip navigation

"Chloe, in order to prevent the suspected terrorist from escaping, we need the ability to check multiple CheckBoxes at once in our WPF application."

"I’m looking at the Brownie Points blog now, Jack. Hopefully he’ll post the rest of the solution in time. It would take too long to code it by hand. The other solution using an AttachedProperty works but it results in an iterative search through the Visual Tree, from the looks of it, Brownie takes a different approach by creating bindings from a MultiSelectionSource to each checkbox. If he puts it up in time, we can prevent Anwar Shakur from escaping."

"Chloe, why do you always explain information in detail as if I don’t know already what you’re talking about?"

"I don’t know, Jack, I think the writers needed some way to help the viewers have a quick update of what’s going on, so they created me as a person with absolutely no social skills but always in the know so that I could ask the dumb questions that the viewer would know if they had watched last week’s show."

The following takes place between 12:00 PM and 1:00 PM

Where were we? Let’s look at our Scenario:

  1. In our XAML we declare a MultiSelectionSource in the Window/UserControl/Listbox.Resources.
  2. The MultiSelectionSource holds a List that it uses to track bindings with MultiSelectionTargets
  3. We declare MultiSelectionTargets on our checkboxes that point back to our MultiSelectionSource
  4. The Bindings are generated and added to the MultiSelectionSource, allowing it to (un)check a group of checkboxes at once

1 and 2 are both clear, we need to enable 3 and 4.

Let’s declare an Attached Property that is a MultiSelectionTarget. It might be confusing on the MultiSelectionTarget or MultiSelectionSource class, so let’s make a utility class, PowerSelector, to hold the property. First we declare the property

    public static class PowerSelector
    { 
        public static readonly DependencyProperty MultiSelectorTargetProperty = 
            DependencyProperty.RegisterAttached( 
                "MultiSelectorTarget"
                typeof(MultiSelectionTarget), 
                typeof(PowerSelector), 
                new PropertyMetadata(new PropertyChangedCallback(TargetAdded))); 

        public static MultiSelectionTarget GetMultiSelectorTarget(DependencyObject obj) 
        { 
            return (MultiSelectionTarget)obj.GetValue(MultiSelectorTargetProperty); 
        } 

        public static void SetMultiSelectorTarget(DependencyObject obj, 
          MultiSelectionTarget value) 
        { 
            obj.SetValue(MultiSelectorTargetProperty, value); 
        }  

 It’s a very non-assuming Attached Property, except it has a PropertyChangedCallback. It’s time for our Plover bird to earn its keep

        private static void TargetAdded(DependencyObject sender, 
          DependencyPropertyChangedEventArgs e) 
        { 
            MultiSelectionTarget tmpTarget = e.NewValue as MultiSelectionTarget
            if (tmpTarget != null
            { 
                Binding tmpbinding = new Binding(tmpTarget.Path); 
                tmpbinding.Source = sender; 
                tmpbinding.Mode = BindingMode.TwoWay; 
                tmpTarget.Source.AddBinding(tmpbinding); 
            } 
        }

So it’s quite simple…when the PowerSelector.MultiSelectorTarget property (of type MultiSelectionTarget) is set on a DependencyObject, the handler creates a Binding with the sender as the Source and the MultiSelectionTarget.Path as the path. The Binding is given a BindingMode of TwoWay, and we call AddBinding on the MultiSelectionSource passing the new Binding we just created. Soo…I guess we should look at how AddBinding works.

        internal void AddBinding(Binding newBinding) 
        { 
            bindings.Add(newBinding); 
            MultiBinding tmpBindings = new MultiBinding(); 
            foreach (BindingBase binding in bindings) 
            { 
                tmpBindings.Bindings.Add(binding); 
            } 
            tmpBindings.Mode = BindingMode.TwoWay; 
            tmpBindings.Converter = new MultiSelectConverter(); 
            tmpBindings.ConverterParameter = this
            BindingOperations.SetBinding(this, MultiSelectionSource.IsSelectedProperty,
              tmpBindings); 
        } 

        private class MultiSelectConverter : IMultiValueConverter 
        { 

            public object Convert(object[] values, Type targetType, object parameter,
              System.Globalization.CultureInfo culture) 
            { 
                MultiSelectionSource source = (MultiSelectionSource)parameter; 
                Boolean tmpBool = (Boolean)values[0];
                if (values.Length == 1) 
                    return tmpBool; 
                else 
                    for (int i = 1; i < values.Length; i++) 
                    { 
                        if ((bool)values[i] != tmpBool) 
                            return source.IsSelected; 
                    } 
                return tmpBool; 
            } 

            public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
              System.Globalization.CultureInfo culture) 
            { 
                Boolean tmpVal; 
                try 
                { 
                    tmpVal = (bool)value; 
                } 
                catch (Exception e) 
                { 
                    throw new ArgumentException(
                      "This method expects a boolean as its first parameter", "value", e); 
                } 
                Object[] retVal = new Object[targetTypes.Length]; 
                for (int i = 0; i < retVal.Length; i++) 
                { 
                    retVal[i] = tmpVal; 
                } 
                return retVal; 
            } 
        } 
    }

Remember bindings is a private IList that MultiSelectionSource declared so the Binding created by the AddTarget handler is added to that list. A new MultiBinding is created and all of the bindings stored in …umm… bindings are added to it. The MultiBinding is set to TwoWay and a MultiSelectConverter is created and assigned as its converter. (A MultiBinding REQUIRES an IMultivalueConverter to be set on it.) The MultiSelectConverter has pretty simple logic ConvertBack sets all of the Sources to the new Value of the Target…In this case the Sources are MultiSelectionTargets and the target is the MultiSelectionSource. Convert sets the MultiSelectionSource only if all the MultiSelectionTargets agree, otherwise it keeps the same value.

Finally, in order to simplify life for the end user — in this case the developer — I created a markup extension that allows you to create a MultiSelectionTarget using attribute syntax rather than requiring property syntax….which is much more verbose. Markup Extensions aren’t as frightening to develop as they first sound. Basically you extend MarkupExtension, put properties on your class and override the ProvideValue method. It’s much easier to visualize in code…But I’m out of time for now. I’ll wrap it all up later and make the code available for download somehow…I wish spaces allowed you to upload files.

beep…beep…beep…beep

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: