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

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