Click here to Skip to main content
15,886,724 members
Articles / Desktop Programming / WPF

Bindable Converter Parameter

Rate me:
Please Sign up or sign in to vote.
4.81/5 (8 votes)
2 Jul 2013CPOL7 min read 120K   4.5K   28  
A simple technique to achieve Bindable-ConverterParameter in WPF's XAML.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Reflection;
using System.Windows.Markup.Primitives;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace BindableConverterParameterTake2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainViewModel();
        }

        private void TextBox_Initialized(object item, EventArgs e)
        {
        }

        private void Grid_Initialized(object sender, EventArgs e)
        {
            //get all  dependency-objects in logical-tree
            List<DependencyObject> AllLogicalTreeDependencyObjects=new List<DependencyObject>();

            FillAllLogicalTreeDependencyObjects(ref AllLogicalTreeDependencyObjects,sender as DependencyObject );

            //go over them
            foreach (var item in AllLogicalTreeDependencyObjects)
            {
                MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(item);
                IEnumerable<DependencyProperty> MatchingDPs = markupObject.Properties.Select<MarkupProperty, DependencyProperty>
                    (mp => { return mp.DependencyProperty; }).Where
                    (dp =>
                    {
                        return
                            dp != null &&
                            BindingOperations.GetBinding(item, dp) != null &&
                            BindingOperations.GetBinding(item, dp).ConverterParameter != null &&
                            Regex.IsMatch(BindingOperations.GetBinding(item, dp).ConverterParameter.ToString(), "Binding");
                    });

                //for those who have Bindable converterParameter do the following:
                foreach (DependencyProperty dp in MatchingDPs)
                {

                    Match m = Regex.Match(BindingOperations.GetBinding(item, dp).ConverterParameter.ToString(), "Binding Path=(?<path>[A-Za-z0-9]+)");
                    if (m != null)
                    {
                        string sConverterParameterBindingPath = m.Groups["path"].Value;

                        // get original binding
                        Binding bindingOrig = BindingOperations.GetBinding(item, dp);

                        //create set of attached properties to replase this ConverterParameter Binding
                        //give unique name by using dp's name:
                        DependencyProperty apBindingSource=null;

                        //(5.) another attached prop for two-way binding operations
                        DependencyProperty apIsSourceChanged = DependencyProperty.RegisterAttached(dp.Name + "IsSourceChanged", typeof(bool), item.GetType(), new PropertyMetadata(false));


                        // 1. attached-prop for the ConverterParameter-Binding
                        DependencyProperty apConverterParameterBindingSource = DependencyProperty.RegisterAttached(dp.Name + "ConverterParameterBindingSource", typeof(object), item.GetType(), new PropertyMetadata(null));
                        Binding bindingConverterParameterBindingSource = new Binding(sConverterParameterBindingPath);
                        BindingOperations.SetBinding(item, apConverterParameterBindingSource, bindingConverterParameterBindingSource);


                        // 2. attached-prop to hold the Converter Object
                        DependencyProperty apConverter = DependencyProperty.RegisterAttached(dp.Name + "Converter", typeof(IValueConverter), item.GetType(), new PropertyMetadata(null));
                        (item).SetValue(apConverter, bindingOrig.Converter);


                        //3. attached-prop to hold the evaluate result >>> will be binded to the original Binded dp
                        DependencyProperty apEvaluatedResult = DependencyProperty.RegisterAttached(dp.Name + "EvaluatedResult", typeof(object), item.GetType(), new PropertyMetadata(null, (s, edp) =>
                            {
                                if (!(bool)s.GetValue(apIsSourceChanged))
                                {
                                    // change didn't come from source>>> target got changed in two-way binding
                                    // change source via convert back
                                    object ret= (s.GetValue(apConverter) as IValueConverter).
                                        ConvertBack(edp.NewValue, null, s.GetValue(apConverterParameterBindingSource), null);
                                    s.SetValue(apBindingSource, ret);
                                }
                            }));
                        Binding bindingOrigDpToEvaluatedResult = new Binding("(" + item.GetType().Name + "." + dp.Name + "EvaluatedResult)");
                        bindingOrigDpToEvaluatedResult.Source = item;
                        bindingOrigDpToEvaluatedResult.Mode =bindingOrig.Mode;
                        BindingOperations.SetBinding(item, dp, bindingOrigDpToEvaluatedResult);


                        // 4. attached-prop to replace the source binding - bind apBindingSource to the original source (instead of the original dp)
                        apBindingSource = DependencyProperty.RegisterAttached(dp.Name + "BindingSource", typeof(object), item.GetType(), new PropertyMetadata(null, (s, edp) =>
                            {
                                s.SetValue(apIsSourceChanged, true);
                                s.SetValue(apEvaluatedResult, (s.GetValue(apConverter) as IValueConverter).
                                    Convert(edp.NewValue, null, s.GetValue(apConverterParameterBindingSource), null));
                                s.SetValue(apIsSourceChanged, false);
                            }));

                        Binding NewBindingToSource = new Binding(bindingOrig.Path.Path);
                        NewBindingToSource.Mode =bindingOrig.Mode;
                        BindingOperations.SetBinding(item, apBindingSource, NewBindingToSource); // reroute source to apBindingSource


                    }
                }
               
            }
        }

        
        private void FillAllLogicalTreeDependencyObjects(ref List<DependencyObject> list,  DependencyObject depobj)
        {
            List<DependencyObject> ret=new List<DependencyObject>();

                list.Add(depobj);
                var children = LogicalTreeHelper.GetChildren(depobj);
                foreach (var item in children)
	            {
                    if (item is DependencyObject )
                    {
                        FillAllLogicalTreeDependencyObjects(ref list, item as DependencyObject );
                    }
		            
	            }
        }
            
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) self employed
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions