I have two ListBoxes, one inside another. And both ListBoxes will have items dynamically added into them upon user request.
Each time a button (not shown in the below code snippet) is clicked, a new item is added to the listbox. Each new item includes a new listbox and others.
And I have buttons inside the inner listbox, one for each list item of the inner listbox.
With DataContext, I can find the data binded to the inner list item, and make changes to it, so that changes are reflected on the proper list item.
However, I also need to make changes to the data binded to the outer list item, which corresponds to the button. How can I know which one it is?
I have came up with a solution, which I believe it not elegant enough. By making changes to the model, I can have each inner data holds a reference to the outer data, so that I can find the data binded to the outer list item. This doesn’t seem like a proper solution though. Do you have any suggestions?
Below is code snippet of the xaml. I’ve simplified it, hope it’s easy to understand. I feel you don’t have to read the whole code.
<ListBox Name="QuestionsListBox" HorizontalAlignment="Stretch" ItemContainerStyle="{StaticResource ListItem}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Text="{Binding Question, Mode=TwoWay}" Grid.Row="0" HorizontalAlignment="Stretch" TextWrapping="Wrap"/>
<ListBox Name="ChoicesListBox" ItemsSource="{Binding Choices}" Grid.Row="1" HorizontalAlignment="Stretch" ItemContainerStyle="{StaticResource ListItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Click="ChoiceAddButton_Click" Height="72" Width="72" HorizontalAlignment="Left" BorderBrush="Transparent">
<Button.Background>
<ImageBrush ImageSource="/Images/choices.add.png" Stretch="Fill" />
</Button.Background>
</Button>
<TextBox Text="{Binding Value, Mode=TwoWay}" HorizontalAlignment="Stretch" Grid.Column="1" Margin="-20,0" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Why not just use
QuestionsListBox.DataContextinsideChoiceAddButton_Clickdirectly? You have a direct way to reference the outerListBoxfrom your code behind since you’ve given it a name, andDataContextis an accessible property.This works fine for me in a demo solution using your provided XAML.
Edit 2:
Sorry, wasn’t thinking. The
Button‘sDataContextwill be aChoice, not theChoicescollection.Your inner
ListBox‘sDataContextis not aQuestion, it’sChoices. Your outerTextBoxhasQuestion.Questionas itsDataContext. BindingTextorItemsSourcemakes theDataContextpoint to the binding target. Here is a bit of tricky XAML to sneak in aDataContextreference.Add an
ElementNameto your outerTextBox:Now, add a hidden TextBlock inside your inner
ListBox:Finally, inside your event handler, you can navigate around the tree to get that reference:
I’d like to note that this isn’t really an elegant solution. It is an all UI solution, however, which could be a useful thing. But better design would be to include
Parentreferences in yourChoiceandChoiceList(or whatever they’re called) classes and use that. Then it would be as simple as callingbtn.DataContext.Parent.Parentwith appropriate type conversions. This way your code becomes easier to read and maintain.