Incorrect relative binding behavior inside of TabControl

Hey, people! I’m finally back into development of my app.

And right after start I collided with one unpleasant situation. For me it seems like a bug, but maybe it isn’t (let me know in comments if so). What happened?
I have array of filters, each filter holds array of categories and each category holds array of subcategories. For display purposes I have TabControl – one tab for one filter – and on each tab I have 2 ListViews(first for categories, second for subcategories). Second ListView takes its source from Subcategories property of SelectedItem of first ListView. On the whole it looks next way:

<StackPanel Orientation="Horizontal">
    <ListBox ItemsSource="{Binding ElementName=cntrl, Path=Categories, Mode=OneWay}"
             SelectedValue="{Binding Filter.Category, Mode=TwoWay}" SelectedValuePath="Title" />

    <ListBox ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Path=Children[0].SelectedItem.SubCategories, Mode=OneWay}"
             SelectedValue="{Binding Filter.Subcategory, Mode=TwoWay}" SelectedValuePath="Title" />
</StackPanel>

You can note ItemsSource’s binding with RelativeSource in the second ListView. All seems good and second ListView successfully takes subcategories for category from first ListView.
But! If I select some category and subcategory, switch to next tab and switch back, then my subcategory is missed. So I started debugger and found next thing, which happens at the moment of tab switch:
debug
For some reason null is given to Subcategory, but I didn’t change parent Category and breakpoint at Category’s setter didn’t fire too. Herewith if on both tabs is selected the same category, then subcategory doesn’t set to null. So why this happens?
I put subcategories ListView before categories ListView like this:

<StackPanel Orientation="Horizontal">
    <ListBox ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Path=Children[1].SelectedItem.SubCategories, Mode=OneWay}"
             SelectedValue="{Binding Filter.Subcategory, Mode=TwoWay}" SelectedValuePath="Title" />
    
    <ListBox ItemsSource="{Binding ElementName=cntrl, Path=Categories, Mode=OneWay}"
             SelectedValue="{Binding Filter.Category, Mode=TwoWay}" SelectedValuePath="Title" />
</StackPanel>

Now if I switch tabs, then null doesn’t put to subcategory value and Filter.Subcategory holds its value between switches. But after switchback appropriate radiobutton isn’t selected.
debug2
Herewith subcategory items are displayed correctly. So what happens here?

When engine draws tab after switch it goes from begin of tab’s XAML to the end. But for some reason it doesn’t clear the tab, it redraws controls one by one. So we receive above results. I believe happens something similar to following:

1) when subcategories list is second: engine redraws categories list, but subcategories list is still old (from old tab). Since second list(subcategory) has relative binding to the first list(category), it becomes new ItemsSource(because categories list is already redrawn and has different SelectedItem). So subcategories list is changed, but there isn’t found old subcategory value, so it’s set to null value. After all engine redraws second list, but subcategory value is already missed.

2) when subcategories list goes first: engine redraws subcategories list, but relative binding doesn’t fire yet, because second list isn’t redrawn. However binding to Filter.Subcategory on SelectedValue attribute fires and set selected list item to nothing (because for now list is empty). After that engine redraws second list, relative binding on first list is fired, but binding on SelectedValue has fired already, so we have correct Filter.Subcategory value, but also list with no selected subcategory. (Note: it’s incorrect behavior, because if we set same values before form load, then all works as expected, see the screenshot below)
debug3

So I believe problem is located. But we still don’t know what to do. So as I believe in above behavior of engine, I thought that some delay before setting bindings could be helpful, because this delay let engine redraw the tab to the end. Luckily binding has exact such property – Delay. So I add delay to binding to Filer.Subcategory like this:

<StackPanel Orientation="Horizontal">
    <ListBox ItemsSource="{Binding ElementName=cntrl, Path=Categories, Mode=OneWay}"
             SelectedValue="{Binding Filter.Category, Mode=TwoWay}" SelectedValuePath="Title" />

    <ListBox ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Path=Children[0].SelectedItem.SubCategories, Mode=OneWay}"
             SelectedValue="{Binding Filter.Subcategory, Delay=1, Mode=TwoWay}" SelectedValuePath="Title" />
</StackPanel>

And that works. I think couple of ticks is enough for engine to draw the tab to the end, and delay for even 1 millisecond doesn’t let change Filter.Subcategory to null. Actually this delay for millisecond looks like dirty hack, but as it fixes above problem, it make me think that my thoughts about redrawing tab controls one-by-one are correct.
If you can refer me to right direction related to this problem then let me know in comments what happens here.
Hope that helps :)

Advertisements

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.

XAML binding to Enum

Hello girls and boys! Today I faced with already solved in many ways Task – binding enum values to combobox. There are many articles on the web about it, this one is most beautiful I found with 10 minutes research. But all these techniques seems to me unintuitive a little, that’s just my vision, but I decided to take another way to solve my problem.

Here I will give simplest possible implementation, which ofc can be complicated with more features. First of all we’ll create our helper class for bindings(I made it in singletone pattern, but it’s optional). BindableBase here is a custom base class, which implements INotifyPropertyChanged interface:

    public class BindingHelper : BindableBase
    {
        private static BindingHelper _instance = null;
        private BindingHelper() { /* Here you can put some default registers */ }
        public static BindingHelper Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new BindingHelper();
                }
                return _instance;
            }
        }

        public Dictionary<String, Object> Enums { get; } = new Dictionary<string, object>();

        public bool RegisterEnum(Type enumType)
        {
            if (!enumType.IsEnum || Enums.ContainsKey(enumType.Name))
            {
                return false;
            }

            Enums.Add(enumType.Name, Enum.GetNames(enumType));
            OnPropertyChanged(nameof(Enums));
            return true;
        }
    }

Next somewhere in the code-behind we must register enum type with BindingHelper. In my case it could be BindingHelper constructor, but I prefer do it somewhere in code:

    BindingHelper.Instance.RegisterEnum(typeof(YourEnumType));

And last step is to bind our ComboBox:

    <ComboBox xmlns:local="clr-namespace:Namespace_Of_BindingHelper"
                ItemsSource="{Binding Source={x:Static local:BindingHelper.Instance}, Path=Enums[YourEnumType]}" />

Ofc xmlns you can implement within any parent element of combobox. And that’s all. Why I like this approach more than others? I think because here we become single entry point through BindingHelper, it’s more intuitive and easily to understand what is going on, and we can simply implement any processing logic of binded entities. Eg. you want some keys to be delivered with enum values, then you could just modify RegisterEnum method like this:

    public bool RegisterEnum(Type enumType)
    {
        if (!enumType.IsEnum || Enums.ContainsKey(enumType.Name))
        {
            return false;
        }

        List<Tuple<Enum, String>> tuples = new List<Tuple<Enum, string>>();
        foreach (Enum e in Enum.GetValues(enumType))
        {
            tuples.Add(new Tuple<Enum, string>(e, e.ToString()));
        }
        Enums.Add(enumType.Name, tuples);
        OnPropertyChanged(nameof(Enums));
        return true;
    }

And XAML:

    <ComboBox xmlns:local="clr-namespace:Namespace_Of_BindingHelper"
                SelectedValuePath="Item1" DisplayMemberPath="Item2"
                ItemsSource="{Binding Source={x:Static local:BindingHelper.Instance}, Path=Enums[YourEnumType]}"
                SelectedValue="{Binding YourEnumType_Variable, Mode=TwoWay}" />

Ofc such approach can be handled not only to enums, but to any other data. I hope this will help someone in the future.