Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Bindable Converter Parameter

, 2 Jul 2013
A simple technique to achieve Bindable-ConverterParameter in WPF's XAML.
BindableConverterParameter.zip
BindableConverterParameter
BindableConverterParameter
bin
Debug
Release
BindableConverterParameter.csproj.user
Properties
Settings.settings
BindableConverterParameterTake2.zip
BindableConverterParameterTake2
BindableConverterParameterTake2
BindableConverterParameterTake2.csproj.user
Properties
Settings.settings
BindableConverterParameterTake2.suo
BindableConverterParameterTake3.zip
BindableConverterParameterTake3
BindableConverterParameterTake3
BindableConverterParameterTake3.suo
BindableConverterParameterTake3.v11.suo
bin
BindableConverterParameterTake3.csproj.user
Debug
Properties
Settings.settings
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)

Share

About the Author

ntg123
Software Developer (Senior) self employed
Israel Israel
No Biography provided

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 2 Jul 2013
Article Copyright 2012 by ntg123
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid