Reputation: 105
I am creating a program that will display a questionnaire. The questionnaire is divided by Categories (Cat 1, Cat 2, etc.). Each category can contain many questions, and each question can have many multiple choice answers. I have constructed this questionnaire using a DataGrid (objDatagrid) with another DataGrid (objInnerDatagrid) nested inside a RowDetailsTemplate. I'm trying to set it up so that all questions and all answers are displayed when the program loads. This means that nothing is collapsed by default. I accomplish this by setting the outer DataGrid to : RowDetailsVisibilityMode="Visible"
Unfortunately, because of my code, the answers for each question are now all identical based on whichever Question is the current selection. When I first set this up I was not using RowDetailsVisibilityMode="Visible". So the default action was to collapse all Questions that were not selected. But as I stated this is not my intended design.
I am pretty sure the error in my code is in the inner grid (objInnerDataGrid) ItemsSource binding. But I don't know what to change it to.
XAML:
<Grid>
<StackPanel>
<TextBox Controls:TextBoxHelper.Watermark="enter search term" Name="TxtFilter" Margin="10" TextChanged="TxtFilter_TextChanged" Height="25" MinWidth="250" HorizontalAlignment="Stretch"/>
<DataGrid x:Name="objDatagrid" ItemsSource="{Binding DataView}" IsReadOnly="True" CanUserAddRows="False" CanUserDeleteRows="False" Loaded="objDatagrid_Loaded" AutoGenerateColumns="False"
HeadersVisibility="None" ScrollViewer.VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDetailsVisibilityMode="Visible">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid x:Name="objInnerDatagrid" ItemsSource="{Binding ElementName=objDatagrid, Path=SelectedItem.Answers}" CanUserAddRows="False" IsReadOnly="True" CanUserDeleteRows="False"
HeadersVisibility="None" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<DataGrid.Columns>
<DataGridCheckBoxColumn ElementStyle="{DynamicResource MetroDataGridCheckBox}"
EditingElementStyle="{DynamicResource MetroDataGridCheckBox}"
Binding="{Binding Answers.AnswerText}"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
<DataGrid.Columns>
<DataGridTextColumn Width="Auto" Binding="{Binding QuestionText}" IsReadOnly="True"/>
</DataGrid.Columns>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Margin="15 0 15 0" Header="{Binding Path=Name}" IsExpanded="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ItemsPresenter VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
</StackPanel>
</Grid>
C#:
public partial class DataEmbed : UserControl
{
DataTable dt = new DataTable();
public DataEmbed()
{
InitializeComponent();
DataContext = this;
_dataView = new ListCollectionView(new ObservableCollection<Question>(Populate()));
_dataView.GroupDescriptions.Add(new PropertyGroupDescription("CategoryString"));
CollectionView cv = (CollectionView)CollectionViewSource.GetDefaultView(DataView);
cv.Filter = CustomFilter;
}
private List<Question> Populate()
{
return new List<Question>()
{
new Question()
{
CategoryString = "Cat 1",
QuestionText = "Question 1",
Answers = new ObservableCollection<Answer>
{
new Answer {AnswerText = "Answer 1"},
new Answer {AnswerText = "Answer 2"},
new Answer {AnswerText = "Answer 3"},
new Answer {AnswerText = "Answer 4"}
}
},
new Question()
{
CategoryString = "Cat 1",
QuestionText = "Question 2",
Answers = new ObservableCollection<Answer>
{
new Answer {AnswerText = "Answer 5"},
new Answer {AnswerText = "Answer 6"},
new Answer {AnswerText = "Answer 7"},
new Answer {AnswerText = "Answer 8"},
}
},
new Question()
{
CategoryString = "Cat 2",
QuestionText = "Question 1",
Answers = new ObservableCollection<Answer>
{
new Answer {AnswerText = "Answer 9"},
new Answer {AnswerText = "Answer 10"},
new Answer {AnswerText = "Answer 11"},
new Answer {AnswerText = "Answer 12"},
}
}
};
}
private ListCollectionView _dataView;
public ListCollectionView DataView
{
get { return _dataView; }
set { _dataView = value; }
}
private void objDatagrid_Loaded(object sender, RoutedEventArgs e)
{
}
}
Classes:
public class Question
{
public string CategoryString { get; set; }
public bool IsCategoryExpanded { get; set; }
public string QuestionText { get; set; }
public ObservableCollection<Answer> Answers { get; set; }
}
public class Answer
{
public string AnswerText { get; set; }
}
Upvotes: 1
Views: 3150
Reputation: 31576
Your assumption about the inner grid binding is correct, for you have an explicit binding to the currently selected row which in effect steps out of the current item and binds it for all rows.
<DataGrid x:Name="objInnerDatagrid"
ItemsSource="{Binding ElementName=objDatagrid, Path=SelectedItem.Answers}"
The binding you want, for all rows, is a relative binding based on the current item's parent datacontext such as
<DataGrid x:Name="objInnerDatagrid"
ItemsSource="{Binding Answers}"
You might have to specify DataContext
explicitly, if so use this:
<DataGrid x:Name="objInnerDatagrid"
ItemsSource="{Binding DataContext.Answers}"
Remember that binding ultimately is code reflection based on the objects datacontext which in most cases is inherited from the parent, so in these situations think about the parent and bind accordingly.
Upvotes: 1