📜 ⬆️ ⬇️

BindableConverter for WPF

Problem: WPF cool technology, but sometimes unfinished. For example, I’ll spit out such code that I don’t remember exactly what Exception is, since ConverterParameter is not a successor of DependencyObject:

<...Text={Binding SourceProperty, Converter={StaticResource SomethingToSomethingElseConverter} ConverterParameter={Binding AnotherSourceProperty}} /> 

Actually, this is a problem. And below is her decision.

Update from 01/18/16:
1) If the base class of the converter is inherited from Freezable, then the proxy is simply not needed. In this case, the converter works exactly as usual would work:
 <-.Resources> <converters:-_ x:Key="-_" BindingParameter1="{Binding -_}" /> </-.Resources> 

... please keep this in mind when reading!

In principle, the problem can be solved in two ways: the first DependencyProperty.Register (..), the second - .RegisterAttached (..). The difference is that the second option is both conceptually and architecturally flawed. Therefore, like this:
')
 using System; using System.Globalization; using System.Windows; using System.Windows.Data; namespace BindableConverter { [ValueConversion(typeof(object), typeof(object))] public class BindableConverterBase : DependencyObject, IValueConverter, IMultiValueConverter { #region BindableParameters #region BindableParameter1 public object BindableParameter1 { get { return GetValue(BindableParameter1Property); } set { SetValue(BindableParameter1Property, value); } } public static readonly DependencyProperty BindableParameter1Property = DependencyProperty.Register( nameof(BindableParameter1), typeof(object), typeof(BindableConverterBase), new PropertyMetadata(String.Empty) ); #endregion #region BindableParameter2 public object BindableParameter2 { get { return GetValue(BindableParameter2Property); } set { SetValue(BindableParameter2Property, value); } } public static readonly DependencyProperty BindableParameter2Property = DependencyProperty.Register( nameof(BindableParameter2), typeof(object), typeof(BindableConverterBase), new PropertyMetadata(String.Empty) ); #endregion #region BindableParameter3 public object BindableParameter3 { get { return GetValue(BindableParameter3Property); } set { SetValue(BindableParameter3Property, value); } } public static readonly DependencyProperty BindableParameter3Property = DependencyProperty.Register( nameof(BindableParameter3), typeof(object), typeof(BindableConverterBase), new PropertyMetadata(String.Empty) ); #endregion #endregion #region IValueConverter public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } #endregion #region IMultiValueConverter public virtual object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } public virtual object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } #endregion } } 


Since the class has implemented both interfaces, it will work with ordinary Binding and MultiBinding. Actually, it remains only to inherit from BindableConverter, overriding the right (or necessary, which is rare) methods.

There is one crucial point to use in XAML. Naive attempt type:
 <Window.Resources> <local:NameAndAgeToVladimirPutinConverter x:Key="NameAndAgeToVladimirPutin" BindableParameter1="{Binding FirstName}" BindableParameter2="{Binding Age}" /> </Window.Resources> 

... will result in both parameters being equal to the default value. Always - no matter where exactly in the resources you declare a link to the converter.

I confess, I don’t have a final understanding of why this is happening. In general terms, the point is that the converter is out of Logical \ VisualTree UI-elements, so there is simply no one to attach to it. Anyway, this is the explanation I unearthed at StackOverflow. The solution to the problem looks like this:

 <bc:BindingProxy x:Key="BindingProxy" Data="{Binding}" /> <local:NameAndAgeToVladimirPutinConverter x:Key="NameAndAgeToVladimirPutin" BindableParameter1="{Binding Source={StaticResource BindingProxy}, Path=Data.FirstName}" BindableParameter2="{Binding Source={StaticResource BindingProxy}, Path=Data.Age}"/> ... <TextBlock Grid.Row="0" Text="{Binding Name, Converter={StaticResource NameAndAgeToVladimirPutin}}" /> 


 using System.Windows; namespace BindableConverter { public class BindingProxy : Freezable { protected override Freezable CreateInstanceCore() { return new BindingProxy(); } /// <summary> /// Binding data. /// </summary> public object Data { get { return GetValue(DataProperty); } set { SetValue(DataProperty, value); } } public static readonly DependencyProperty DataProperty = DependencyProperty.Register( nameof(Data), typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null) ); } } 


PS There are plans to screw MarkupExtension so that you can get something like Converter = {bc: BindableConverter BindingProxy = {StaticResource Proxy}, Parameter1 = FirstName, Parameter2 = Age}. If someone has an idea how to do it even more beautiful and concise, then please.

Also very relevant is the question of why all the same without BindingProxy does not work.

Source: https://habr.com/ru/post/275223/


All Articles