4

So I'm stumped at the moment, and hoping another pair of eyes can catch what I'm missing or at least point me in a direction to dig further. Sorry in advance, I don't often ask quick and easy questions.

Scenario: Huge silverlight solution with some precarious parts to it they want built on with new features but no large effort rewrites or anything. One of which being a virtualized ListBox. This ListBox is a parent to many different things in ways I wouldn't normally expect to see done the way they were. So to give a little visualization of the hierarchy in pseudo;

ListBox (Parent)

-ListBoxItemTemplate --A user control that hosts a ContentControl where other UserControls are swapped out in it.

---Multiple other UserControls that get swapped out in the previous ContentControl at runtime based on numerous conditions.

----Within each nested UserControl there's often other nested ItemsControls and collections of sorts with their Item Template.

Hopefully I've still got your understanding on this monster but if not, here's something a little more visual to help convey the issue I'm getting to; enter image description here

So here's my current issue. Based on what they want, without doing some major refactoring I'm trying to make lemonade and use the SelectedItem bool from the parent listbox to perform some things in some of these sub-view UserControls.

I do this with a quick ancestor grab for the IsSelected of the parent ListBoxItem. I do this (xaml wise) via a quick {Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}} from the sub UserControls.

Which works as expected in most cases and returns True/False just fine to do what it needs to do. However in some cases when other various UserControls are acting as the item then it may return True as IsSelected to the first level nested UserControl, but False to one of the sub-nested UserControls. Or vice versa. The funny thing is, it isn't consistent at all.

For example say I load 20 items. All using the exact same structure of nested controls. I'll get weird results back where say 18 of the items will return True, and maybe 2 return False. Which in a basic explanation means say I select one ListBoxItem. Everything is returned as expected, everything does what it's supposed to do. HOWEVER, I'll select another ListBoxItem from the same list, that's using all the exact same parts and the outermost parent element will return the property for the expected selected item....BUT THEN an entirely different item on the list will receive the property also.

Or to help visualize. Imagine you're looking at a listbox, you select an item and stuff inside it appears. You select another and the same thing happens as expected. You select another from the top of the list, and an item at the bottom of the list will display the action meant for what you've selected. The item you meant to select will still return Focused and display as focused, except an entirely different item you haven't touched gets the action result and throws the bool to True when it should be False.

Hopefully I haven't lost you in this mess. My question is what the hell might cause this kind of behavior? Do any alarm bells go off (besides the obvious ones) for you?

There are no other ListBox controls used anywhere else in there. I'm not even sure exactly what to look for? Maybe it's just a bug in the ancestor relationship binding in Silverlight? Maybe some weird naming collision or something? I'm not sure and troubleshooting has become the bane of my existence, but I'm limited on options considering if it were my choice. I'd rip all this crap out and do it in a way that made more sense.

Either way, thanks for letting me borrow some brain time considering how confusing I'm sure this is for anybody. :)

Chris W.
  • 22,835
  • 3
  • 60
  • 94
  • 1
    It's just my thoughts. How about performing an action on the item when *SelectionChanged* in the *ListBox* rather than looking if item is selected. Or it's not the way you're looking for because children controls bind to parents and it should be done like it? I guess there might be something with naming and VisualTree isn't getting it properly. Maybe the solution is to name all of this UserControls etc. with names so it wouldn't be possible to have conflict (but you said it's rather obvious). Anyway I would start with x:Naming them all. – Michal Kania Dec 01 '15 at 20:47
  • @MichalKozak Yea, I've got some limitations I'm supposed to work around as it is. I'll have to check into that thought more today thanks man. – Chris W. Dec 02 '15 at 14:42

2 Answers2

2

To me this sounds like your ancestor-type-chain (this term is totally made up) looks different for different items, and therefore your ancestor binding yields for some items an ancestor that was not expected (different level in the hierarchy).

enter image description here

You can try the following: Add a BindableObjectReference to the resources of your ItemTemplate or your ItemContainerStyle.Template and replace you ancestor binding with a binding to this newly added resource. This way you can be sure to hit the correct level in the hierarchy.

<ListBox.ItemTemplate>
    <DataTemplate>
        <Grid...>
          <Grid.Resources>
            <BindableObjectReference x:Key="TopLevelListBoxItem"
              Object="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}}"/>
          </Grid.Resources>
          ...
        </Grid>
    </DataTemplate>
</ListBox.ItemTemplate>


<MyNestedSubControl
  ...
  Foo="{Binding Path=Object.IsSelected, Source={StaticResource TopLevelListBoxItem}}"
  .../>

the code for BindableObjectReference:

public class BindableObjectReference : DependencyObject
{
    public object Object
    {
        get { return GetValue( ObjectProperty ); }
        set { SetValue( ObjectProperty, value ); }
    }

    public static readonly DependencyProperty ObjectProperty =
        DependencyProperty.Register( "Object", typeof( object ),
        typeof( BindableObjectReference ), new PropertyMetadata( null ) );
}

If this does not work: Another cause might be the virtualization. Try to disable virtualization and see if the issue still exists.

Martin
  • 5,714
  • 2
  • 21
  • 41
  • Ah right on Martin, let me check into this today. I've got to work around some things I'm supposed to avoid touching that makes things more interesting. Will swing back here soon as I dig some more. Thanks amigo. – Chris W. Dec 02 '15 at 14:44
  • It turned out to be a lot more complex than originally thought with a bunch of other factors involved in one heck of an "interestingly" built part of the system. Took 5 seniors in a room knocking heads most of a day to iron out even some of the details but after finally getting permission to reach outside the original scope it's at least partially curbed some frustrations. Either way I really dug your bindable object reference idea so marking it as answer. Thanks again my friend, cheers! – Chris W. Dec 03 '15 at 14:37
  • You're welcome. BTW I love a good Silverlight question. If you feel like you need to torture your brain with one: I still don't know how to correctly implement an `IsFocused` [extension method in Silverlight](http://stackoverflow.com/questions/31816083/find-the-parent-richtextbox-from-the-focused-hyperlink-within) – Martin Dec 03 '15 at 15:48
  • Ooooh yea! That one was from when I was traveling, forgot all about it. I'll swing back to it here in a bit after meetings and see if we can't knock heads on it. :) – Chris W. Dec 03 '15 at 15:56
0

Just adding some additional information here.

The FindAncestor, AncestorType will look at base classes as well. Given that ListBoxItem is the base class for ComboBoxItem and ListViewItem it may be possible that it's fetching the value from the incorrect control.

momar
  • 364
  • 1
  • 3
  • That's a pretty valid thought. Although the issue still occurs even though no other controls that meet that criteria exist in the tree and I'd expect it to be a bit more consistent when reproducing. Good call though and I'll still be digging in further to see if that may be the case. – Chris W. Dec 02 '15 at 14:51