Click here to Skip to main content
12,290,514 members (65,981 online)
Click here to Skip to main content

Tagged as

Stats

57.2K views
2.6K downloads
27 bookmarked
Posted

Bindable Converter Parameter

, 2 Jul 2013 CPOL
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)

Share

About the Author

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

You may also be interested in...

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