WPF dynamic bindings&convertes

Hi, people! I’m really tired of continuous creating of Converters in each new WPF project. I don’t like how they must be applied in XAML and I don’t like all these similar classes. So this time I decided – enough.

First of all I created generic converter(the only converter which I wanted to see in my project). That’s the code:

public class GenericConverter<TIn, TOut> : IValueConverter
{
    private Func<TIn, TOut> _convert = null;
    private Func<TOut, TIn> _convertBack = null;

    public GenericConverter(Func<TIn, TOut> convert = null, Func<TOut, TIn> convertBack = null)
    {
        _convert = convert;
        _convertBack = convertBack;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (_convert == null)
        {
            throw new NotImplementedException();
        }

        return _convert((TIn)value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (_convertBack == null)
        {
            throw new NotImplementedException();
        }

        return _convertBack((TOut)value);
    }
}

My next thought was to collect different generics implementations in some dictionary and call them from XAML. But it’s impossible as you can’t use binding on bindings converter parameter (or I just didn’t find any solution). Of course I could create many static resources from my code-behind dictionary, but I don’t want to go this way.

So next I decided to make dictionary with bindings, which I created somewhere in code like this one:

var binding = new Binding("IsConnected")
{
    Source = DataModel,
    Mode = BindingMode.OneWay,
    Converter = new GenericConverter<bool, Visibility>((bool b) =>
        {
            return (Visibility)(b ? Visibility.Visible : Visibility.Collapsed);
        })
};

As you can see converter here is really small. Even if you want to use it few times, you always can store it in some collection of similar converters and don’t give a f#ck about all these converter classes.
So I created dictionary with bindings like one above, but again – I couldn’t bind any property to them. And at all it looks silly – binding to binding.

And here we come to my final realization. In previous post you could see BindingHelper. It’s in play again – now it looks like this:

public class BindingHelper : BindableBase
{
    #region Singleton implementation
    private static BindingHelper _instance = null;
    private BindingHelper()
    {
        DynamicBindings = new DPResolver(() => OnPropertyChanged(nameof(DynamicBindings)));
    }

    public static BindingHelper Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new BindingHelper();
            }
            return _instance;
        }
    }
    #endregion Singleton implementation

    #region Enums
    /* cutted for sake, see prev.post for it */
    #endregion Enums

    #region DP resolver
    public DPResolver DynamicBindings { get; private set; }
    public class DPResolver : DependencyObject
    {
        private Action _resolverChanged;
        private Dictionary<String, DependencyProperty> _innerCache = new Dictionary<string, DependencyProperty>();

        public DPResolver(Action resolverChanged)
        {
            _resolverChanged = resolverChanged;
        }
        public Object this[String dpName]
        {
            get
            {
                if (_innerCache.ContainsKey(dpName))
                {
                    return GetValue(_innerCache[dpName]);
                }
                return null;
            }
            set
            {
                if (_innerCache.ContainsKey(dpName) && value != this[dpName])
                {
                    SetValue(_innerCache[dpName], value);
                    _resolverChanged();
                }
            }
        }

        public void RegisterBinding(String dpName, Type endType, Binding binding)
        {
            if (String.IsNullOrWhiteSpace(dpName))
            {
                throw new ArgumentNullException(nameof(dpName));
            }
            if (binding == null)
            {
                throw new ArgumentNullException(nameof(binding));
            }

            DependencyProperty dp;
            if (_innerCache.ContainsKey(dpName))
            {
                dp = _innerCache[dpName];
            }
            else
            {
                dp = DependencyProperty.Register(dpName, endType, typeof(DPResolver), new PropertyMetadata((d,e) => _resolverChanged()));
                _innerCache.Add(dpName, dp);
            }

            if (dp != null)
            {
                var be = BindingOperations.SetBinding(this, dp, binding);
                _resolverChanged();
            }
        }
    }
    #endregion DP resolver
}

What happens here? First of all see at DPResolver class. It inherits from DependencyObject, that’s needed for bindings. Class has indexer, which call GetValue/SetValue for appropriate DependencyProperty in innerDictionary, DP is taken by string name. Actually it seems similar to default DP snippet in visual studio (type ‘propdp’ and press Tab), except that depends on property name we return different values:dp snippet

RegisterBinding method register new DependencyProperty if it still doesn’t exists for dpName, and next it set our binding for this DP.

Idea is next: as we can’t store bindings, we would store DependencyProperty collection. We always can append Binding to one of DPs in collection. We create binding (with our generic converter inside), next we bind it to some DP inside BindingHelper. All we need next is to bind property in XAML to same DP inside BindingHelper.

Do you remember sample binding in begin of this post? Now we can register it in BindingHelper next way:

BindingHelper.Instance.DynamicBindings.RegisterBinding("mainGridVisibility", typeof(Visibility), binding);

Last thing we will made is binding in XAML:

<Grid xmlns:lc="clr-namespace:BindingHelpers_Namespace">
    <Grid.Resources>
        <ResourceDictionary>
            <ObjectDataProvider x:Key="bindingHelper" ObjectInstance="{x:Static lc:BindingHelper.Instance}" />
        </ResourceDictionary>
    </Grid.Resources>

    <TabControl Visibility="{Binding Source={StaticResource bindingHelper}, Path=DynamicBindings[mainGridVisibility], Mode=OneWay}" />
</Grid>

That’s all, now we can drop out all static converters. I know solution is very suspicious, but it totally suits my needs on my little project.
Give attention to this one string inside RegisterBinding method:

dp = DependencyProperty.Register(dpName, endType, typeof(DPResolver), new PropertyMetadata((d,e) => _resolverChanged()));

As you can see we call _resolverChanged() each time as any of our inner DPs was changed (_resolverChanged() fire PropertyChanged for DynamicBindings property inside BindingHelper), which will cause refresh in all elements, which we have binded to BindingHelper’s DynamicBindings property. That’s definitelly is a disadvantage, but I think you can avoid this by realizing DPResolver class similary to ObservableCollection.

I hope this solution might help someone with non-trivial needings on project.

Advertisements

One thought on “WPF dynamic bindings&convertes

Share your thoughts

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