Proper MVVM for a WPF ListCollectionView? - c#

I'm implementing a ListCollectionView so I can get groupings of objects within my datagrid. It seems to be working pretty well, but it doesn't feel like proper MVVM. I'm a little confused because a property can't directly be defined as a ListCollectionView. Instead, a separate collection has to be provided that implements IList.
The original ObservableCollection<EntityConcept> has too much information to display in the datagrid at once. The user selects one of the EntityConcept using a combobox, then a ListCollectionView<PropertyItem> is constructed to display all of the PropertyItem that belong to that EntityConcept.
I feel like it would make sense for the ObservableCollection<PropertyItem> to be defined as a ListCollectionView from the start. However, I don't think I can do that.
public class EntityConcept
{
private string _Name;
private ObservableCollection<PropertyItem> _propertyList;
public ObservableCollection<PropertyItem> propertyList
{
get
{
return _propertyList;
}
set
{
_propertyList = value;
}
}
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
}
}
}
Instead, a ListCollectionView is constructed based on what the user selects in the combobox:
<ComboBox x:Name="ifcCombo"
Grid.Column="2"
Grid.Row="1"
Margin="0,3,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="150"
DisplayMemberPath="ifcEntity"
SelectedItem="{Binding chosenConcept}"
ItemsSource="{Binding ConceptList}"/>
<!--Grid.Row 3-->
<DataGrid x:Name="propertiesTable"
ItemsSource="{Binding chosenConceptProperties}"
Grid.Column="1"
Grid.ColumnSpan="3"
Grid.Row="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutoGenerateColumns="False"
CanUserAddRows="False"
Margin="0,25,0,0" CellEditEnding="propertiesTable_CellEditEnding">
<DataGrid.Columns>
<DataGridCheckBoxColumn Width="75" MinWidth="50" Header="" IsReadOnly="False" Binding="{Binding On}"/>
<DataGridTextColumn Width="10*" MinWidth="250" Header="Property Name" Binding="{Binding PropertyName}" IsReadOnly="True" FontSize="10" />
<DataGridTextColumn Width="10*" MinWidth="250" Header="Mapping" Binding="{Binding Mapping}" IsReadOnly="False" FontSize="10" />
<!--<DataGridTemplateColumn Width="*" Header="" CellTemplateSelector="{StaticResource myDynamicTemplateSelector}"/>-->
</DataGrid.Columns>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{Binding StringType, Converter={StaticResource stringToBrush}}"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander x:Name="exp" IsExpanded="True" Background="White" Foreground="Black" BorderThickness="1,1,1,5">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="HeaderCheckBox" Grid.Column="2" Tag="{Binding Name}" Margin="5,0,0,0" Click="CheckAll"/>
<TextBlock Grid.Column="0" Text="{Binding Path=Name}" Foreground="#2a5fa4" FontWeight="Bold" Margin="10,0,0,0"/>
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter/>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
Instead, the ListCollectionView is manually constructed every time the chosenConcept is set. This doesn't feel correct to me, and I think there must be a cleaner way.
private ObservableCollection<EntityConcept> _ConceptList = new ObservableCollection<EntityConcept>();
private EntityConcept _chosenConcept = new EntityConcept();
private ListCollectionView _chosenConceptProperties;
public EntityConcept chosenConcept
{
get
{
return _chosenConcept;
}
set
{
_chosenConcept = value;
OnPropertyChanged(nameof(chosenConcept));
ConstructChosenConcept();
}
}
public ListCollectionView chosenConceptProperties
{
get
{
return _chosenConceptProperties;
}
set
{
_chosenConceptProperties = value;
OnPropertyChanged(nameof(chosenConceptProperties));
}
}
public void ConstructChosenConcept()
{
if(chosenConcept != null)
{
chosenConceptProperties = new ListCollectionView(chosenConcept.propertyList);
chosenConceptProperties.GroupDescriptions.Add(new PropertyGroupDescription("Pset"));
name = chosenConcept.Name;
description = chosenConcept.Description;
objectType = chosenConcept.ObjectType;
tag = chosenConcept.Tag;
}
}

Related

wpf datagrid expand all not working

Hi have a Datagrid with groups, i added a button to Expand All groups but it's not working, all groups stay collapsed.
I'm using PropertyChanged event Handler and a button with a Command
Here is the xaml:
<StackPanel Grid.Row="0">
<Button x:Name="ExpandAll" Content="Tout deplier" VerticalAlignment="Bottom" Command="{Binding ExpandAll}"/>
<!-- This textblock text is updated by the Expanded property changed -->
<TextBlock Text="{Binding Expanded}" />
</StackPanel>
<DataGrid x:Name="GrdLignes" HorizontalAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="0,0,0,0"
Grid.Row="1" VerticalAlignment="Top" AutoGenerateColumns="False" CanUserAddRows="False"
CanUserDeleteRows="False" ItemsSource="{Binding Lignes}" IsReadOnly="True"
RowDetailsVisibilityMode="VisibleWhenSelected" RowHeaderWidth="25">
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Background="Lavender" IsExpanded="{Binding Expanded}">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" Padding="0,0,5,0" FontWeight="Bold" />
<TextBlock Text="{Binding Path=ItemCount}" Padding="0,0,5,0"/>
<TextBlock Text="Commandes"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Pièce Achat" Binding="{Binding Path=Piece}" FontWeight="Bold"/>
<DataGridTextColumn Header="Type" Binding="{Binding Path=TypeLabel}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="{Binding Path=Type, Converter={StaticResource TypeToBrushConverter}}" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Statut" Binding="{Binding Path=StatutLabel}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="{Binding Path=Statut, Converter={StaticResource StatutToBrushConverter}}" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid RowHeaderWidth="25" ItemsSource="{Binding Path=Lignes}" AutoGenerateColumns="False" Margin="0" CanUserAddRows="False" CanUserDeleteRows="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Acheteur" Binding="{Binding Path=Acheteur}"/>
<DataGridTextColumn Header="Pièce" Binding="{Binding Path=Piece}"/>
<DataGridTextColumn Header="Client" Binding="{Binding Path=Client}"/>
<DataGridTextColumn Header="Ref" Binding="{Binding Path=ArRef}"/>
<DataGridTextColumn Header="Ref Fourn" Binding="{Binding Path=RefFourn}"/>
<DataGridTextColumn Header="Designation" Binding="{Binding Path=Designation}"/>
<DataGridTextColumn Header="Qte" Binding="{Binding Path=CmQte}"/>
<DataGridTextColumn Header="Vendeur" Binding="{Binding Path=Vendeur}"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
Here is the viewModel:
public class MainViewModel : INotifyPropertyChanged
{
private bool _expanded = false;
public bool Expanded
{
get { return _expanded; }
set
{
_expanded = value;
OnPropertyChanged("Expanded");
}
}
public ICommand ExpandAll { get; set; }
public MainViewModel()
{
ExpandAll = new Command(ExpandAllAction);
}
private void ExpandAllAction(object parameters)
{
Expanded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I think you need to set the UpdateSource Trigger to "PropertyChanged" when binding to the Expanded Property.
<Expander Background="Lavender" IsExpanded="{Binding Expanded, UpdateSourceTrigger=PropertyChanged}">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" Padding="0,0,5,0" FontWeight="Bold" />
<TextBlock Text="{Binding Path=ItemCount}" Padding="0,0,5,0"/>
<TextBlock Text="Commandes"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
I found a solution from this thread:
https://stackoverflow.com/a/12611779/3182178
I added in MainWindow class this code:
public static class VisualTreeHelper
{
public static Collection<T> GetVisualChildren<T>(DependencyObject current) where T : DependencyObject
{
if (current == null)
return null;
var children = new Collection<T>();
GetVisualChildren(current, children);
return children;
}
private static void GetVisualChildren<T>(DependencyObject current, Collection<T> children) where T : DependencyObject
{
if (current != null)
{
if (current.GetType() == typeof(T))
children.Add((T)current);
for (int i = 0; i < System.Windows.Media.VisualTreeHelper.GetChildrenCount(current); i++)
{
GetVisualChildren(System.Windows.Media.VisualTreeHelper.GetChild(current, i), children);
}
}
}
}
private void ExpandAll_OnClick(object sender, RoutedEventArgs e)
{
Collection<Expander> collection = VisualTreeHelper.GetVisualChildren<Expander>(GrdLignes);
foreach (Expander expander in collection)
expander.IsExpanded = true;
}
private void CollapseAll_OnClick(object sender, RoutedEventArgs e)
{
Collection<Expander> collection = VisualTreeHelper.GetVisualChildren<Expander>(GrdLignes);
foreach (Expander expander in collection)
expander.IsExpanded = false;
}
Then inside the xaml i added two button with this code:
<Button Name="ExpandAll" Content="++" VerticalAlignment="Bottom" Width="30" Click="ExpandAll_OnClick"/>
<Button Name="CollapseAll" Content="--" VerticalAlignment="Bottom" Width="30" Margin="0" Click="CollapseAll_OnClick"/>
It's not the best but it's working...
Using one-way binding to set all groups open or close after a button click command.
View
<UserControl.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</UserControl.Resources>
<!-- grid code -->
<Expander IsExpanded="{Binding Source={StaticResource proxy}, Path=Data.Expanded, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
ViewModel
public bool Expanded
{
get { return _expanded; }
set { _expanded = value; OnPropertyChanged(); }
}
Proxy
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
It's because the binding is done on the group, not on the main model. Replace your XAML code by:
<Expander IsExpanded="{Binding Path=DataContext.IsExpanded, Mode=OneWay,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
Note the one way mode: when the user expands or collapses the groups, you don't want to push back the change to your model.

WPF DataGridCell Style binding issue

I'm facing a strange issue binding Collection to DataGrid on which when selecting a row the binding seems to disappear (I have an empty cell)
Here is my object list
public ReadOnlyCollection<ItemPoint> CurrentItemPoints { get; private set; }
ItemPoint object is defined as :
public sealed class ItemPoint : PropertyChangedNotifierBase
{
private bool _IsSubscribed = false;
private string _Name = string.Empty;
private string _Value = string.Empty;
private DateTime _ValueTime = DateTime.UtcNow;
public bool IsSubscribed
{
get { return _IsSubscribed; }
set
{
if (_IsSubscribed != value)
ChangeSubscription(value);
ChangeProperty("IsSubscribed", ref _IsSubscribed, value);
}
}
public string Name
{
get { return _Name; }
private set
{
ChangeProperty("Name", ref _Name, value);
}
}
public string Value
{
get { return _Value; }
set
{
_Value = value;
}
}
public DateTime ValueTime
{
get { return _ValueTime; }
private set
{
ChangeProperty("ValueTime", ref _ValueTime, value);
}
}
}
here is my xaml:
<DataGrid Name="PointsList" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="2,0,0,0"
ItemsSource="{Binding CurrentItemPoints, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding Path=IsSubscribed, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Width="10">
<DataGridCheckBoxColumn.CellStyle>
<Style>
<EventSetter Event="CheckBox.Checked" Handler="SubscriptionCheckBox_Checked"/>
</Style>
</DataGridCheckBoxColumn.CellStyle>
</DataGridCheckBoxColumn>
<DataGridTextColumn Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" Header="Name" Width="*"/>
<DataGridTextColumn Binding="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, IsAsync=True, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}" Header="WorkingValueColumn" Width="*"/>
<DataGridTextColumn Binding="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, IsAsync=True, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}" Header="NonWorkingValueColumn" Width="*">
<DataGridTextColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Path=ValueTime, UpdateSourceTrigger=PropertyChanged, NotifyOnTargetUpdated=True, Mode=OneWay, StringFormat='{}{0:dd-MM-yyyy HH:mm:ss.fff}'}" Header="Time" Width="*">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
If I now display the datagrid everything is ok... until I select any cell of the grid.
Doing that I will have the "WorkingValueColumn" working well... meaning that will keep displaying the selected item value while the "NonWorkingValueColumn" will display an empty cell...
Here is a screenshot of part of datagrid as sample:
I've been looking for a while now without finding any kind of explication... I though I could have missed some selection template or something like that but I really don't know.
Thanks for your help!
I finally could workaround that strange behavior changing the DataTextColumn by a DataTemplateColumn as that:
<DataGridTemplateColumn Header="Value">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}" HorizontalAlignment="Stretch"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}" BorderThickness="0" HorizontalAlignment="Stretch"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

WPF DataGrid GroupStyle

I have the following DataGrid in WPF with two groups.
First group is a bool flag which represents if a person is active/inactive.
The second group (or sub-group) is the ID of each person.
Each person can have multiple cities, therefore the grouping for the ID, because each person is shown multiple in the DataGrid.
Here is the XAML:
<DataGrid CanUserAddRows="False" AutoGenerateColumns="False" ItemsSource="{Binding DataSource}">
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<DataTrigger Binding="{Binding Name}" Value="True">
<Setter Property="Background" Value="{StaticResource ActiveBrush}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Name}" Value="False">
<Setter Property="Background" Value="{StaticResource InactiveBrush}"/>
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="Foreground" Value="Gray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name, Converter={StaticResource BoolToTextConverter}}" Margin="5 2 5 2"/>
<TextBlock Text=":" Margin="0 2 5 2"/>
<TextBlock Text="{Binding ItemCount}" Margin="0 2 0 2"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Background="LightSteelBlue">
<TextBlock Text="{Binding Name}" Foreground="White" Margin="5 2 5 2"/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<DockPanel>
<Button BorderThickness="0" Content="Edit" Margin="3"
Command="{Binding Commands.Edit}"
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
</DockPanel>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="ID" Binding="{Binding ID}" IsReadOnly="True"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True"/>
<DataGridTextColumn Header="City" Binding="{Binding City}" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
It all works fine!
However, I don't like the blue row for each sub-group.
What I want to achieve is the grouping style in the following image:
For each sub-group I want the Edit button and the ID to appear only once per person.
How would I do this? Is it possible in XAML only or should I remove the reduntant content in code-behind?
Edit
Here some test data:
public class Person
{
public Person(bool active, int id, string name, string city)
{
Active = active;
ID = id;
Name = name;
City = city;
}
public bool Active { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public string City { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
var data = new ObservableCollection<Person>
{
new Person(true, 233, "Max", "New York"),
new Person(true, 233, "Max", "Los Angeles"),
new Person(true, 314, "John", "Paris"),
new Person(true, 578, "Mary", "Vienna"),
new Person(true, 782, "Susan", "Rome"),
new Person(true, 782, "Susan", "Prague"),
new Person(true, 782, "Susan", "San Francisco"),
new Person(false, 151, "Henry", "Chicago")
};
DataSource = new ListCollectionView(data);
}
private ListCollectionView _dataSource;
public ListCollectionView DataSource
{
get { return _dataSource; }
set
{
_dataSource = value;
_dataSource.GroupDescriptions.Add(new PropertyGroupDescription("Active"));
_dataSource.GroupDescriptions.Add(new PropertyGroupDescription("ID"));
}
}
I would highly recommend changing the data structure to:
public class Person {
public bool Active { get; set; }
public int ID { get; set; }
public string Name { get; set; }
public Collection Cities { get; set; }
}
Otherwise you can change this GroupStyle
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Background="LightSteelBlue">
<TextBlock Text="{Binding Name}" Foreground="White" Margin="5 2 5 2"/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
To
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="EditButtonColumn" />
<ColumnDefinition SharedSizeGroup="IDColumn" />
<ColumnDefinition SharedSizeGroup="NameColumn" />
<ColumnDefinition SharedSizeGroup="PresenterColumn" Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" BorderThickness="0" Content="Edit" Margin="3"
CommandParameter="{Binding Path=Items[0]}" />
<TextBlock Grid.Column="1" Text="{Binding Path=Items[0].ID}" />
<TextBlock Grid.Column="2" Text="{Binding Path=Items[0].Name}" />
<ItemsPresenter Grid.Column="3" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
Change the template to suit your needs

Support for dynamic properties in DataGrid

I have a simple DataGrid, that looks like this
<DataGrid ItemsSource="{Binding GridList}">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Height" Value="Auto" />
<Setter Property="Background" Value="Black" />
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding ID}" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
But now I want to add another TextBlock in DataTemplate that looks like this
<TextBlock Grid.Column="1" Text="{Binding Values["Entity1"]}" />
Or
<TextBlock Grid.Column="1" Text="{Binding Values.Entity1}" />
But I cannot get it working, actually I do not know how to create the Last one so it is going to work
The model for the List looks like this
class ListModel {
public int ID {get;set;}
public IDictionary<string, string> Values {get;set;}
}
Of course I can replace the IDictionary to something else but what?
Actually I figured it out, I changed the ListModel to
class ListModel {
public string this[string key] { get{return _values[key];} set{_values[key]=value;}}
public int ID{get;set;}
public IDictionary<string,string> _values = new Dictionary<string,string>();
}
And the XAML to
<TextBlock Grid.Column="1" Text="{Binding [Entity1]}" />

How do I bind variables in the listbox nested templates?

<ListBox Name="serumListBox" VerticalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Visible" ItemsSource="{Binding Path=SerumList}">
<ListBox.Resources>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Self}}"
Value="True">
<Setter Property="IsSelected" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" >
<Button Width="250" Height="70"
HorizontalContentAlignment="Stretch" Click="SerumListItem_Click" >
<Button.ContentTemplate>
<DataTemplate>
<StackPanel>
<Button Click="SerumListItemRemove_Click" VerticalAlignment="Top" HorizontalAlignment="Right" Width="22" >
<Image Source="./Images/close.png"></Image>
</Button>
<Label VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,-9,0,0" Content="{Binding Name}" />
<Label VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,-4,0,0" Content="{Binding LotNum}"/>
</StackPanel>
</DataTemplate>
</Button.ContentTemplate>
</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code Behind :
public partial class MainWindow : MetroWindow
{
private ObservableCollection<Serum> serumList = new ObservableCollection<Serum>();
public ObservableCollection<Serum> SerumList
{
get { return serumList; }
set { serumList = value; }
}
public MainWindow()
{
InitializeComponent();
serumListBox.ItemsSource = SerumList;
}
private void buttonInsert_Click(object sender, RoutedEventArgs e)
{
serumList.Add(new Serum() { Name = "aadasfas", Year = "2", Month = "2", LotNum = "2", Type = "2" });
}
private void SerumListItemRemove_Click(object sender, RoutedEventArgs e)
{
serumList.RemoveAt(serumListBox.SelectedIndex);
}
private void SerumListItem_Click(object sender, RoutedEventArgs e)
{
}
}
Since the button datatemplate is a nested template of listbox datatemplate I can't properly bind label content.Also didn't find any resources or examples for this kind.Any Ideas?
Try your Label.Content Binding's as:
...
<Label Margin="0,-9,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="{Binding DataContext.Name,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem}}}" />
<Label Margin="0,-4,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="{Binding DataContext.LotNum,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem}}}" />
...

Categories