views:

257

answers:

2

In a previous post, I was advised to use a DataTemplate to render a group of Buttons and Labels, and it works wonderfully. The problem is that I have several of these groups of Buttons, and I would like to distinguish one group from another. I plan to use the AlternatingIndex to color each group differently, but that's not enough -- each group actually needs to have its index printed as well.

Here's a contrived example... let's say the Item looks something like this:

       Lock Door
Safe   Unlock Door
       Sound Alarm

If I have a room full of these safes, I'd like to know which one I'm accessing. Therefore, I'd like the list to look like this:

         Lock Door
Safe #1  Unlock Door
         Sound Alarm

         Lock Door
Safe #2  Unlock Door
         Sound Alarm

My ItemsControl (ListBox) is bound to a List in code-behind. After doing some research here on SO, it seems like I need to somehow bind the ItemsControl.Count property. One idea I had was to pass the Content through an IValueConverter. The Content would be databound to ItemsControl.Count. Then the IValueConverter would just format the string to be "Safe #{0}".

It's the databinding part that I'm once again faltering on. The DataContext for this ItemsControl is my ViewModel... so I can only guess that I need to specify a Binding that will give me the ItemsControl instead of the ViewModel.

Is this the right idea? If so, can someone help me with the Binding? If not, what other methods might I try?

+1  A: 

To provide a property for binding that represents the index of the item in the collection, set the AlternationCount property to some huge value (larger than the maximum possible number of items in the collection), then you can bind to it from your data template thus:

{Binding RelativeSource={RelativeSource TemplatedParent},
         Path=TemplatedParent.(ItemsControl.AlternationIndex)}

Also, you will have to tweak your alternation count converter to do the modulus in code, since you're no longer cycling the index automatically (because of the big value of AlternationCount).

Aviad P.
Fantastic, this totally did the trick. When I read about using AlternationIndex to make alternating background colors, I completely missed the point of AlternationCount. Thank you.
Dave
One quick related question -- if I want to get the index of the "Safe", should I just parse out the number from the Content, or is there a better way? I can't use the SelectedIndex because there's no guarantee that the item is selected when the user clicks the button.
Dave
Didn't this solution give you the index of the safe?
Aviad P.
Yes, in order to render it properly in the GUI. Ah, I see -- the right way to handle this is to pass the same binding as a CommandParameter, isn't it?
Dave
You lost me... If you are handling a command on a data object, you can simply check your collection's `IndexOf` method to figure out its index.
Aviad P.
Sorry, it's probably because of my lack of experience.Each ListBoxItem has 3 buttons, and each button has its own Command, which is bound to the ViewModel. When handling the Command, how can you use IndexOf to get at the index of the collection? And when you say collection, are you really referring to the underlying collection that populates the ItemsControl, or are you talking about the ItemsControl itself?
Dave
Actually, the biggest problem for me right now is finding the Command via databinding in the DataTemplate. Apparently, if you use a DataTemplate, the Binding isn't automatically to the DataContext defined for the UserControl itself?
Dave
From within the data template, the data context is your data object, not your view model. In order to 'reach' the view model you have to specify it using the `Source` property of the binding (or `ElementName` if it's a property of the view itself and not a resource). - EDIT: It sounds tho, that you might be better off putting your command properties inside your data objects after all - that way you have implicit context for the command.
Aviad P.
I got it working from the DataTemplate... I finally realized that I could see the binding errors in the output window, so what I ended up doing was searching up the tree for the UserControl, and then nabbed its DataContext. It finally works. :)
Dave
A: 

@Aviad: thanks, I'll try that! Just for completion's sake, I wanted to post what I had just tried. I finally got the databinding to work the way I had proposed:

<Label Grid.Row="1" Grid.Column="0" Content="{Binding Path=Items.Count, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}"></Label>

The end result was wrong -- all of the ListBox items had the index "4", so I guess the Content of all of the Labels gets evaluated after the items are added to the container. Interesting!

Dave