Group Style Header never appears - wpf

My GroupStyle Header never appears in the combobox....
The Grouping is working fine....It's sole a binding issue but am not able to figure out.
<ComboBox Height="23" Margin="33,45,125,0" Name="comboBox1" VerticalAlignment="Top" ItemsSource="{Binding}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<Border Background="Red">
<TextBlock Text="{Binding Path=value}" />
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock FontSize="12" FontWeight="Bold" Foreground="DarkGray">
<Button Content="{Binding Path=Location}"/>
<TextBlock Text="{Binding Path=Location}" />
<Button>bbbb</Button>
</TextBlock>
<ItemsPresenter/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
</ComboBox>
and code behind
public class Store
{
public string Location { get; set; }
public string value { get; set; }
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
var myData = new ObservableCollection<Store>
{
new Store { Location = "Group1", value = "Item 1" },
new Store { Location = "Bombay", value = "Item 2" },
new Store { Location = "Group2", value = "Item 11" }
}
ICollectionView view = CollectionViewSource.GetDefaultView(myData);
view.GroupDescriptions.Add(new PropertyGroupDescription("Location"));
DataContext = myData;
}
}

Try changing the Binding Path from "Location" to "Name"
<GroupStyle.HeaderTemplate>
...
<Button Content="{Binding Path=Location}"/>
<TextBlock Text="{Binding Path=Location}" />
...
<GroupStyle.HeaderTemplate>
...like this...
<GroupStyle.HeaderTemplate>
...
<Button Content="{Binding Path=Name}"/>
<TextBlock Text="{Binding Path=Name}" />
...
<GroupStyle.HeaderTemplate>

Related

Can you create ItemTemplate for Stack Panels with bindings?

I have a stack panel of stack panels with similar controls. Could a template be used instead? Each field is a property of an Custom Object.
<StackPanel Margin="10,0,0,0">
<StackPanel x:Name="ReferenceStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="Reference" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=UReference, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
<StackPanel x:Name="TitleStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="Title" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
<StackPanel x:Name="SponsorStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="Sponsor" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=Sponsor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
<StackPanel x:Name="AlignmentStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="Alignment" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=Alignment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
<StackPanel x:Name="NTAStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="NTA" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=NTA, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
<StackPanel x:Name="KeywordsStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="Keywords" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=Keywords, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
<StackPanel x:Name="FocusAreasStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="FocusAreas" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=FocusAreas, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
<StackPanel x:Name="PlatformStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="Platform" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=Platform, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
<StackPanel x:Name="FiscalYearStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="FiscalYear" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=FiscalYear, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
<StackPanel x:Name="OriginatingDocumentStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="OriginatingDocument" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=OriginatingDocument, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
<StackPanel x:Name="RecommendationsStackPanel" Margin="5" Visibility="{Binding}">
<Label Content="Recommendations" Style="{StaticResource LabelStyle}"/>
<Controls:MarkingsTextbox Value="{Binding Path=Recommendations, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
</StackPanel>
</StackPanel>
I was thinking something like this but it didn't work.
<DataTemplate x:Key="Template">
<StackPanel>
<Label Content="{Binding}" />
<TextBox Text="{Binding}"/>
</StackPanel>
</DataTemplate>
I am also reading something about ContentControl but not sure how they work. It is one object I am trying to display, in an edit properties view.
The simplest solution to this would be if it's ok to have your label and textbox side by side.
Then you could use a propertygrid:
https://github.com/xceedsoftware/wpftoolkit/wiki/PropertyGrid
The toolkit is not free for commercial use, however, the source is available and you could take that and customise/simplify it for your usage.
If that does not suit then you could use reflection to build out a collection of property viewmodels which have PropertyName and PropertyValue.
Add them to a List you bind to itemssource of an itemscontrol.
You can then template each to whatever you like.
I'm not clear what that Visibility binding is all about, can't work out what the intent is there.
You could, however, use an attribute to control which properties are displayed.
The following proof of concept iterates properties to build out a list. It is very quick and dirty and intended just to get you started rather than a cut and paste solution.
MainWindow
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding PropertyValues}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBox Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
MainWindowViewModels and some classes.
public partial class MainWindowViewModel : ObservableValidator
{
[ObservableProperty]
private List<PropertyNameValue> propertyValues = new();
private PropertyInfo[] propertyList = typeof(Thingummy).GetProperties();
private Thingummy thing = new Thingummy { Reference="AAAAA", Sponsor="BBBB", Title="CCCC"};
private void GetProperties()
{
foreach (PropertyInfo property in propertyList)
{
PropertyNameValue pnv = new PropertyNameValue{ Name= property.Name, Value=property.GetValue(thing).ToString() };
PropertyValues.Add(pnv);
}
}
private void SetProperties()
{
for (int i = 0; i < propertyList.Length; i++)
{
propertyList[i].SetValue(thing, PropertyValues[i].Value);
}
}
public MainWindowViewModel()
{
GetProperties();
}
}
public class Thingummy
{
public string Reference { get; set; }
public string Title { get; set; }
public string Sponsor { get; set; }
}
public class PropertyNameValue
{
public string Name { get; set; }
public string Value { get; set; }
}
I don't know what type your class is you're editing. I've called mine Thingummy and of course it only has the first few properties.
There's no save button but that'd call SetProperties.
You could potentially make this generic and pass in a type to a generic viewmodel if you put your mind to it.
Make the itemssource a list and define different viewmodels for string, double etc. Define a template for each type. Perhaps put any get/ set logic in there as well.
Anyhow, this spins up and I can edit ok.

WPF MVVM Combobox SelectionChanged just after reload the ViewModel

My problem is, after the SelectionChanged event nothing happen. The TextBox didnt get any new value relaited to the ComboBox. When I reload the ViewModel (Go page 1 and back), the TexBox has his new value relaited to the ComboBox.
Below you can see what I have on till now.
View:
<StackPanel Orientation="Vertical" Grid.Row="1">
<Border Width="150" Height="150" CornerRadius="80" BorderThickness="1" BorderBrush="Gray" HorizontalAlignment="Center">
<Border.Background>
<ImageBrush ImageSource="/Assets/FEBSolution.png"/>
</Border.Background>
</Border>
<TextBlock x:Name="EmployerName" Text="{Binding EmployerName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" Margin="0 10 0 0" FontWeight="Bold"/>
<TextBlock x:Name="EmpDescription" Text="{Binding EmpDescription, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="11" HorizontalAlignment="Center" Opacity="0.8"/>
<TextBlock x:Name="EmpMotto" Text="{Binding EmpMotto, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="8" HorizontalAlignment="Center" Opacity="0.8"/>
<StackPanel Margin="20">
<StackPanel Orientation="Horizontal" Margin="0 3" HorizontalAlignment="Left">
<materialDesign:PackIcon Kind="Location" />
<TextBlock x:Name="EmpLocation" Text="{Binding EmpLocation, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10 0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 3" HorizontalAlignment="Left">
<materialDesign:PackIcon Kind="Phone" />
<TextBlock x:Name="EmpPhone" Text="{Binding EmpPhone, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10 0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 3" HorizontalAlignment="Left">
<materialDesign:PackIcon Kind="Email" />
<TextBlock x:Name="EmpEmail" Text="{Binding EmpEmail, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10 0"/>
</StackPanel>
</StackPanel>
</StackPanel>
<ComboBox x:Name="cmbEmployer" Grid.Row="2" SelectedValuePath="ID" SelectedValue="{Binding ID}" ItemsSource="{Binding contractDetails}" SelectedItem="{Binding ContractSelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="EmployerName" materialDesign:HintAssist.Hint="Employer" Width="200" HorizontalAlignment="Center" Margin="10">
<ie:Interaction.Triggers>
<ie:EventTrigger EventName="SelectionChanged">
<ie:InvokeCommandAction Command="{Binding SelectionChangedCommand, UpdateSourceTrigger=PropertyChanged}" CommandParameter="{Binding ElementName=cmbEmployer, Path=SelectedItem}"/>
</ie:EventTrigger>
</ie:Interaction.Triggers>
</ComboBox>
ViewModel:
private ContractDetail _contractSelectedItem;
public ContractDetail ContractSelectedItem
{
get { return _contractSelectedItem; }
set
{
_contractSelectedItem = value;
EmployerName = _contractSelectedItem.EmployerName;
EmpDescription = _contractSelectedItem.EmpDescription;
EmpMotto = _contractSelectedItem.EmpMotto;
EmpLocation = _contractSelectedItem.EmpLocation;
EmpPhone = _contractSelectedItem.EmpPhone;
EmpEmail = _contractSelectedItem.EmpEmail;
OnPropertyChanged(nameof(ContractSelectedItem));
}
}
public List<ContractDetail> contractDetails { get; set; }
#region Public all Commands
//Command for change User
public ICommand ChangeUserCommand { get; private set; }
public ICommand CloseWindowCommand { get; private set; }
public ICommand SelectionChangedCommand { get; private set; }
#endregion
//PRWContext for general use
private PRWContext context = new PRWContext();
public ApplicationSettingViewModel()
{
// Load the data from PRW Database
LoadUserData();
BindContractComboBox();
//Commands
ChangeUserCommand = new RelayCommand(() => ExecuteChangeUserCommand());
CloseWindowCommand = new RelayCommand(() => ExecuteCloseWindowCommand());
SelectionChangedCommand = new RelayCommand(() => ExecuteSelectionChangedCommand());
}
private void ExecuteCloseWindowCommand()
{
foreach (Window window in Application.Current.Windows)
{
if (window is ApplicationSettingWindow)
{
window.Close();
break;
}
}
}
private void ExecuteChangeUserCommand()
{
UserSettingWindow view = new UserSettingWindow();
view.Show();
foreach (Window window in Application.Current.Windows)
{
if (window is ApplicationSettingWindow)
{
window.Close();
break;
}
}
}
private void ExecuteSelectionChangedCommand()
{
var item = context.ContractDetails.ToList();
contractDetails = item;
}
//Load the user data
private void LoadUserData()
{
UserName = Settings.Default["UserName"].ToString();
JobDescription = Settings.Default["UsrJobDescription"].ToString();
Motto = Settings.Default["UsrMotto"].ToString();
Street = Settings.Default["UsrStreet"].ToString();
City = Settings.Default["UsrCity"].ToString();
Country = Settings.Default["UsrCountry"].ToString();
PhoneNumber = Settings.Default["UsrPhoneNumber"].ToString();
Email = Settings.Default["UsrEmail"].ToString();
}
private void BindContractComboBox()
{
var item = context.ContractDetails.ToList();
contractDetails = item;
}
#region PropertyChanged EventHandler
//propertychanged eventhandler
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
For sure there is somthing missing, otherwise it will do the magic ;) I just dont know where i miss something. Any help will be welcome.
You aren't calling the OnPropertyChanged() for
EmployerName,
EmpDescription,
EmpMotto,
EmpLocation,
EmpPhone,
EmpEmail
that's why your view doesn't get updated, and why are you actually using these separate properties when you can just use the ContractSelectedItem directly. You can access the nested properties like this "ContractSelectedItem.EmployerName"
Try this in your viewmodel
private ContractDetail _contractSelectedItem;
public ContractDetail ContractSelectedItem
{
get { return _contractSelectedItem; }
set
{
_contractSelectedItem = value;
OnPropertyChanged(nameof(ContractSelectedItem));
}
}
and change your view's binding like this
<StackPanel Orientation="Vertical" Grid.Row="1">
<Border Width="150" Height="150" CornerRadius="80" BorderThickness="1" BorderBrush="Gray" HorizontalAlignment="Center">
<Border.Background>
<ImageBrush ImageSource="/Assets/FEBSolution.png"/>
</Border.Background>
</Border>
<TextBlock x:Name="EmployerName" Text="{Binding ContractSelectedItem.EmployerName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" Margin="0 10 0 0" FontWeight="Bold"/>
<TextBlock x:Name="EmpDescription" Text="{Binding ContractSelectedItem.EmpDescription, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="11" HorizontalAlignment="Center" Opacity="0.8"/>
<TextBlock x:Name="EmpMotto" Text="{Binding ContractSelectedItem.EmpMotto, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="8" HorizontalAlignment="Center" Opacity="0.8"/>
<StackPanel Margin="20">
<StackPanel Orientation="Horizontal" Margin="0 3" HorizontalAlignment="Left">
<materialDesign:PackIcon Kind="Location" />
<TextBlock x:Name="EmpLocation" Text="{Binding ContractSelectedItem.EmpLocation, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10 0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 3" HorizontalAlignment="Left">
<materialDesign:PackIcon Kind="Phone" />
<TextBlock x:Name="EmpPhone" Text="{Binding ContractSelectedItem.EmpPhone, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10 0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 3" HorizontalAlignment="Left">
<materialDesign:PackIcon Kind="Email" />
<TextBlock x:Name="EmpEmail" Text="{Binding ContractSelectedItem.EmpEmail, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10 0"/>
</StackPanel>
</StackPanel>
</StackPanel>
<ComboBox x:Name="cmbEmployer" Grid.Row="2" SelectedValuePath="ID" SelectedValue="{Binding ID}" ItemsSource="{Binding contractDetails}" SelectedItem="{Binding ContractSelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="EmployerName" materialDesign:HintAssist.Hint="Employer" Width="200" HorizontalAlignment="Center" Margin="10">
<ie:Interaction.Triggers>
<ie:EventTrigger EventName="SelectionChanged">
<ie:InvokeCommandAction Command="{Binding SelectionChangedCommand, UpdateSourceTrigger=PropertyChanged}" CommandParameter="{Binding ElementName=cmbEmployer, Path=SelectedItem}"/>
</ie:EventTrigger>
</ie:Interaction.Triggers>
</ComboBox>
Also, there doesn't seem a reason to use the SelectionChanged event when you are just repopulating the contractDetails property every time the selection changes. Try to remove it if you aren't doing anything other then just repopulating the property.

Data binding cardview control wpf

Here is my xaml
<syncfusion:CardView Grid.Column="1"
Grid.Row="2"
Grid.ColumnSpan="4"
Grid.RowSpan="4"
ItemsSource="{Binding Events}">
<syncfusion:CardView.DataContext>
<viewModel:DefaultViewModel />
</syncfusion:CardView.DataContext>
<syncfusion:CardView.ItemTemplate>
<DataTemplate>
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBoxItem>
<ListBoxItem.DataContext>
<viewModel:DefaultViewModel />
</ListBoxItem.DataContext>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Event Name:" />
<TextBlock Text="{Binding EventName}"
Margin="5,0,0,0" />
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Event Genre:" />
<TextBlock Text="{Binding EventGenre}"
Margin="5,0,0,0" />
</StackPanel>
</ListBoxItem>
</ListBox>
</DataTemplate>
</syncfusion:CardView.ItemTemplate>
<syncfusion:CardView.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding EventName}" />
</DataTemplate>
</syncfusion:CardView.HeaderTemplate>
</syncfusion:CardView>
My viewModel
public DefaultViewModel()
{
CustomerDatabaseEntities context = new CustomerDatabaseEntities();
Events = (from data in context.Event_Details select data.eventTitle).ToList();
}
public string EventName {get;set;}
public string EventGenre {get;set;}
My Model
public partial class Event_Details
{
public string eventTitle { get; set; }
public string eventGenre { get; set; }
}
I'm trying to display each value of eventTitle and eventGenre in its own card similar to this:
.
Each event will have its own card, with its respective details.Although the card view was able to show that i had two different events(refer to link above), both the eventTitle and eventGenre were blank. How would i be able to display each of these values in their own cards?
<DataTemplate>
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Event Name:" />
<TextBlock Text="{Binding eventTitle }"
Margin="5,0,0,0" />
</StackPanel>
</ListBoxItem>
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Event Genre:" />
<TextBlock Text="{Binding eventGenre }"
Margin="5,0,0,0" />
</StackPanel>
</ListBoxItem>
</ListBox>
</DataTemplate>

Multiple levels of binding in WPF ItemsControl

How can i bind multiple levels of data in same time like List of chapters and under each chapter list of pages.
The class structure and xaml i used is shown here
public class ContentsPage
{
public string cname{ get; set; }
public string label { get; set; }
}
public class Chapter
{
public string name { get; set; }
public string id { get; set; }
public List<ContentsPage> pages { get; set; }
}
public class Model
{
public List<Chapter> chapters { get; set; }
}
<ItemsControl x:Name="TopLevelListBox" ItemsSource="{Binding}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander ExpandDirection="Down" Width="175">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=name}" Margin="0,0,5,0"/>
</StackPanel>
</Expander.Header>
<ListBox x:Name="SubListBox" ItemsSource="{Binding Path=enrichments}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=cname}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
For binding i used the code
Model data = new Model(); //load data
TopLevelListBox.DataContext = data.chapters;
Only my expander headers are filled with result. What i need to do fill the pages inside the expander ? Any ideas or samples link for doing the same
Your ItemsControl Item template should change a bit. Instead of setting the ListBox as Content of Expander, set it as Content Template.
<Expander ExpandDirection="Down"
Width="175" Content="{Binding}">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=name}"
Margin="0,0,5,0" />
</StackPanel>
</Expander.Header>
<Expander.ContentTemplate>
<DataTemplate>
<ListBox x:Name="SubListBox"
ItemsSource="{Binding Path=enrichments}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=cname}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</Expander.ContentTemplate>
</Expander>
And make sure you have sub items to show in ListBox.

WPF Relative Binding using hierarchical collection structure

I have an ObservableCollection containing an ObservableCollection:
public class BooksDetailModel
{
public BookModel Book{ get; set; }
public ObservableCollection<AuthorModel> Authors { get; set; }
}
Property in ViewModel:
public ObservableCollection<BooksDetailModel> Books { get; set; }
I would like to render this in a ListBox like this:
Book1
Author1
Author2
Book2
Author1
etc.
The top level bind is easy but I am having trouble with the inner child collection.
XAML so far:
<ListBox ItemsSource="{Binding Books}" BorderBrush="{x:Null}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Book.Name}" FontSize="12" FontWeight="Bold" />
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ???, Path=Author.Name}" FontSize="10" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Any suggestions for ??? - Relative binding the inner listbox itemsource to the parent itemsource's Authors collection.
You should bind the ItemsSource of the inner ListBox to the Authors property. And the binding in the DataTemplate will be simply binding to the Name property of the author:
<ListBox ItemsSource="{Binding Books}" BorderBrush="{x:Null}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Book.Name}" FontSize="12" FontWeight="Bold" />
<ListBox ItemsSource="{Binding Authors}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontSize="10" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Resources