views:

24

answers:

2

I have a DataTemplate that needs to set the IsSelected property on an ItemsControl's container (such as TreeViewItem, ListViewItem or ComboBoxItem). However, it doesn't know the type of the container until it's passed in to it. Since IsSelected isn't part of a common base class or interface, nor is it a common dependency property registered with AddOwner to the various classes (Duh, MS!!! WTF not?!!), I ended up with this mess...

if (container is TreeViewItem) {
    (container as TreeViewItem).IsSelected = true;
    return;
}

if (container is ListBoxItem) {
    (container as ListBoxItem).IsSelected = true;
    return;
}

if (container is ComboBoxItem) {
    (container as ComboBoxItem).IsSelected = true;
    return;
}

...which not only is verbose, but requires me to modify it if I ever use a different ItemsControl that uses different container types! Not good!

Sure I could enhance it a little by putting this logic in extension methods (damn C# for not having extension properties!!) called IsContainerSelected and SetContainerSelected and putting them on UIElement, then moving the above code inside there, but it's just making the outside neater. The inside is still a mess.

My only other thought is to use reflection and look for an IsSelected property and use that if found, but I'm always leery of doing things like that. However, since there isn't a common interface or base class, I'm not really sure I have a choice here.

For context, I'm sharing a complex data template between several different ItemsControls and the template itself has controls that can receive focus such as checkbox and textbox. However, when those controls receive focus via the mouse, the underlying container item doesn't get selected and whatever was selected before remains so.

My workaround is to use an attached behavior that utilizes the preview events to intercept the focus before it happens and set the underlying item accordingly, which works great when I've hard-coded TreeViewItem or ListBoxItem, etc., but I don't want to hard-code the type since the control shouldn't really care. So that's the part that breaks down.

Ugh!!! Why didn't MS just register the same attached property or at least create an ISelectableContainer interface?!!

+1  A: 

I'm not sure that I fully understand your problem, but you could try adding an IsSelected boolean to your model and then binding that property against the Item control it's contained in. That way, you just have to worry about setting that property in the model, regardless of the container.

mdm20
Normally that would be the way to go, but the model item can appear in multiple places (think of the tree's folders as keywords and the leaf represents items to which they apply.) Plus this has nothing to do with the viewmodel. This is a UI behavior issue and would apply regardless of the model. The selection is only used for keyboard navigation. The checkbox is what determines which items are selected and they are bound to the viewmodel (i.e. you check one instance, they all check as they should.) Again, this is just visual behavior, not model or viewmodel.
MarqueIV
Actually... answered my own question, but your answer gave me the idea so I'm voting you up.
MarqueIV
A: 

Per @mdm20's answer, he suggested modifying the ViewModel, which is of course normally what you want to do. However this is a purely view-related issue (keyboard navigation-related) and isn't reflected in the ViewModel at all, nor in this case should it be.

But that gave me an idea! Since I'm using a custom control as the DateTemplate to render the item in whichever control it's being added, that class does have multiple instances, all of which are pointing to the same ViewModel instance, which is what I want! Therefore I added an IsSelected property to the UserControl itself rather than the ViewModel. I then just bound to that and set it as needed. Works great and keeps the ViewModel clean! Purely UI which is where it should be!

MarqueIV