In my Application I've got a Dictionary with int as key and ObservableCollection as value:
Dictionary<int, ObservableCollection<Entity>> SourceDict { get; set; }
Now I want to bind the Dictionary to a Datagrid where all ObservableCollections of this Dictionary should be presented.
If I bind it like this:
ItemsSource="{Binding SourceDict}"
I get a empty Datagrid. Is there a way to do this or before binding should I convert the Dictionary to an ObservableCollection?
<Grid>
<ListView HorizontalAlignment="Center" Width="525" Margin="0,42,0,52" ItemsSource="{Binding SourceList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.View>
<GridView x:Name="gv_Listview">
<GridViewColumn Header="Age">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness=".3" Margin="-6,-3">
<TextBlock TextAlignment="Center" Text="{Binding Age}" Height="30" Margin="6,3" Width="90"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness=".3" Margin="-6,-3">
<TextBlock x:Name="txtAriza" TextAlignment="Center" Text="{Binding Name}" TextWrapping="Wrap" Height="30" Margin="6,3" Width="112"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Surname">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness=".3 .3 .0 .3" Margin="-6,-3">
<TextBlock TextAlignment="Center" Text="{Binding Surname}" Height="30" Margin="6,3" Width="60"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
this is the TestEntity
public class TestEntity : INotifyPropertyChanged
{
private string _name ;
private string _surname;
private int _age ;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
public int Age
{
get { return _age; }
set { _age = value; OnPropertyChanged("Age"); }
}
public string Surname
{
get { return _surname; }
set { _surname = value; OnPropertyChanged("Surname"); }
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
And DataContext
private List<ObservableCollection<TestEntity>> _source = new List<ObservableCollection<TestEntity>>();
public List<ObservableCollection<TestEntity>> SourceList
{
get { return _source; }
set { _source = value; }
}
public DataContext()
{
this.SourceList.Add(new ObservableCollection<TestEntity> { new TestEntity { Age = 21 , Name = "Chris" , Surname = "James"}});
this.SourceList.Add(new ObservableCollection<TestEntity> { new TestEntity { Age = 23, Name = "Jin", Surname = "Evelin" } });
this.SourceList.Add(new ObservableCollection<TestEntity> { new TestEntity { Age = 33, Name = "Ryan", Surname = "Thrusty" } });
this.SourceList.Add(new ObservableCollection<TestEntity> { new TestEntity { Age = 34, Name = "Martin", Surname = "Riggs" } });
}
Hope this helps.
Related
I am making a datagrid inside a window, where two columns are DockPanels with a TextBlock and a Button. When a user selects a file/folder, the TextBlock receives the value and displays it. However, it does not reflect it right away in my case. When I double click on the TextBlock, it suddenly shows. What am I missing here?
XAML
<DataGrid Name="copierDataGrid" Margin="30,50,30,100"
ItemsSource="{Binding SelectedAsm}" SelectedItem="{Binding Selected}" AutoGenerateColumns="False" CanUserResizeColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Prefix" Width="70" Binding="{Binding prefCol, TargetNullValue=''}"/>
<DataGridTemplateColumn Width="235" CanUserResize="False" Header="Select File">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel DockPanel.Dock="Top">
<TextBlock Name="fileLocColtxtBox" Width="185" Margin="0,0,0,0" TextWrapping="Wrap" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Text="{Binding fileLocCol, TargetNullValue=''}"/>
<Button x:Name="brwsFileBtn" Content="..." Width="30" Margin="0,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Click="brwsFileBtn_Click"/>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Suffix" Width="70" Binding="{Binding suffCol, TargetNullValue=''}"/>
<DataGridTemplateColumn Header="Target location" Width="*" CanUserResize="False" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel DockPanel.Dock="Top">
<TextBlock Name="destLocColtxtBox" Width="180" Margin="0,0,0,0" TextWrapping="Wrap" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Text="{Binding destLocCol, TargetNullValue=''}"/>
<Button x:Name="brwsFileBtn" Content="Browse" Width="50" Margin="0,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Click="brwsFolderBtn_Click"/>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
XAML.CS
public itemAsm Selected { get; set; }
public ObservableCollection<itemAsm> SelectedAsm { get; set; }
public unitCopierFrm()
{
InitializeComponent();
SelectedAsm = new ObservableCollection<itemAsm>();
DataContext= this;}
public class itemAsm
{
public string prefCol { get; set; }
public string fileLocCol { get; set; }
public string suffCol { get; set; }
public string destLocCol { get; set; }
}
}private void brwsFolderBtn_Click(object sender, RoutedEventArgs e)
{
if (Selected !=null)
{
System.Windows.Forms.FolderBrowserDialog dlgFolderBrowser = new System.Windows.Forms.FolderBrowserDialog();
dlgFolderBrowser.Description = "Select the destination folder";
dlgFolderBrowser.RootFolder = System.Environment.SpecialFolder.MyComputer;
dlgFolderBrowser.ShowDialog();
string dirPath = dlgFolderBrowser.SelectedPath;
//if (dlgFolderBrowser.ShowDialog == Windows)
if (dirPath != null)
{
Selected.destLocCol = dirPath;
}
}
}
itemAsm should implement INotifyPropertyChanged and raise the PropertyChanged event to notify the view whenever the data-bound property has been set to a new value:
public class itemAsm : INotifyPropertyChanged
{
private string _prefCol;
public string prefCol
{
get { return _prefCol; }
set { _prefCol = value; NotifyPropertyChanged(); }
}
private string _fileLocCol;
public string fileLocCol
{
get { return _fileLocCol; }
set { _fileLocCol = value; NotifyPropertyChanged(); }
}
private string _suffCol;
public string suffCol
{
get { return _suffCol; }
set { _suffCol = value; NotifyPropertyChanged(); }
}
private string _destLocCol;
public string destLocCol
{
get { return _destLocCol; }
set { _destLocCol = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
You may also want to consider renaming the properties according to the C# naming conventions.
I'm new to WPF development and I don't get any further with google either. I don't understand how I can validate my model data with ICommand. I understand that there are annotations and that the ICommand interface offers a canExecute method. For example I want to say that the Name and the Surname is required.
I tried it with [Required(ErrorMessage = "Title is required.")] in my Model class, but I don't get it to work. may someone can help me.
So far I have the following:
public class Student : INotifyPropertyChanged
{
private string name;
private string surname;
private int age;
private string course;
public string Course
{
get { return course; }
set
{
course = value;
OnPropertyChanged();
}
}
public int Age
{
get { return age; }
set
{
age = value;
OnPropertyChanged();
}
}
public string Surname
{
get { return surname; }
set
{
surname = value;
OnPropertyChanged();
}
}
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class StudentViewModel
{
private IList<Student> _studentList;
public StudentViewModel()
{
_studentList = new List<Student>
{
new Student{Name="Todd",Surname="Johnsen",Age=29,Course="Software-Development"},
new Student{Name="Mike",Surname="Kroshka",Age=31,Course="Marketing"},
new Student{Name="Marie",Surname="Tedd",Age=21,Course="Marketing"},
new Student{Name="Susane",Surname="Müller",Age=31,Course="Marketing"},
new Student{Name="Herbert",Surname="Rehl",Age=18,Course="Software-Development"},
new Student{Name="Markus",Surname="Stanz",Age=23,Course="Software-Development"},
new Student{Name="Sabine",Surname="Bergsen",Age=19,Course="Marketing"}
};
}
public IList<Student> Students
{
get { return _studentList; }
set { _studentList = value; }
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if(mUpdater == null)
{
mUpdater = new Updater();
}
return mUpdater;
}
set
{
mUpdater = value;
}
}
}
public class Updater : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
}
}
<Window x:Class="Student_list_mvvm_wpf_core.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Student_list_mvvm_wpf_core"
mc:Ignorable="d"
Title="Student-list" Height="350" Width="600">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0">Name</Label>
<Label Grid.Row="1">Surname</Label>
<Label Grid.Row="2">Age</Label>
<Label Grid.Row="3">Course</Label>
<Button x:Name="btnUpdateStudent" Grid.Row="3" Grid.Column="2"
Width="100" Margin="2" HorizontalAlignment="Left"
Command="{Binding Path=UpdateCommand}">Update Student</Button>
<ListView x:Name="studentGrid" ItemsSource="{Binding Students}"
Grid.Row="4" Grid.ColumnSpan="3" Margin="5">
<ListView.View>
<GridView x:Name="gridStudent">
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="100" />
<GridViewColumn Header="Surname" DisplayMemberBinding="{Binding Surname}" Width="100" />
<GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}" Width="50" />
<GridViewColumn Header="Course" DisplayMemberBinding="{Binding Course}" Width="200" />
</GridView>
</ListView.View>
</ListView>
<TextBox Grid.Row="0" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedItem.Name, ElementName=studentGrid}"
x:Name="txtName"></TextBox>
<TextBox Grid.Row="1" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedItem.Surname, ElementName=studentGrid}"
x:Name="txtSurname"></TextBox>
<TextBox Grid.Row="2" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedItem.Age, ElementName=studentGrid}"
x:Name="txtAge"></TextBox>
<TextBox Grid.Row="3" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedItem.Course, ElementName=studentGrid}"
x:Name="txtCourse"></TextBox>
</Grid>
Using IDataErrorInfo to validate your model looks more suited in your case. First, Update your ViewModel to have a property that bound to the ListView's SelectedItem:
public Student SelectedStudent
{
get { return _selectedStudent; }
set { _selectedStudent = value; }
}
Second, update the Student's model to do data validation:
public class Student : INotifyPropertyChanged, IDataErrorInfo
{
private string name;
private string surname;
private int age;
private string course;
public string Course
{
get { return course; }
set
{
course = value;
OnPropertyChanged();
}
}
public int Age
{
get { return age; }
set
{
age = value;
OnPropertyChanged();
}
}
public string Surname
{
get { return surname; }
set
{
surname = value;
OnPropertyChanged();
}
}
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
public string Error => string.Empty;
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Name")
{
if (string.IsNullOrEmpty(Name))
result = "Name is required.";
}
if (columnName == "Surname")
{
if (string.IsNullOrEmpty(Surname))
result = "Surname is required.";
}
return result;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Finally, update your xaml:
-Bind between your ListView's SelectedItem and the Property that you've created.
-Validate the TextBox's Text
<TextBox Grid.Row="0" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedStudent.Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=true, NotifyOnValidationError=False}"
x:Name="txtName"></TextBox>
<TextBox Grid.Row="1" Grid.Column="1" Width="200"
HorizontalAlignment="Left" Margin="2"
Text="{Binding SelectedStudent.Surname,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=true, NotifyOnValidationError=False}"
x:Name="txtSurname"></TextBox>
-Add a MultiDataTrigger to enable and disable the Button when the suited conditions are met.
<Button x:Name="btnUpdateStudent" Grid.Row="3" Grid.Column="2"
Width="100" Margin="2" HorizontalAlignment="Left"
Content="Update Student" Command="{Binding UpdateCommand}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=txtName, Path=(Validation.HasError)}" Value="false" />
<Condition Binding="{Binding ElementName=txtSurname, Path=(Validation.HasError)}" Value="false" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Output:
Here is a Gist for the full source. If you insist on using the canExecute, this should help.
I have the following XAML
<Label Content="Player Name" Grid.Row="0" Grid.Column="0" Margin="2,0,2,0" HorizontalAlignment="Right" Foreground="White"/>
<TextBox Grid.Row="0" Grid.Column="1" Margin="2,2,2,2" Text="{Binding PlayerFirstName}"/>
<Label Content="Player Last Name" Grid.Row="1" Grid.Column="0" Margin="2,0,2,0" HorizontalAlignment="Right" Foreground="White"/>
<TextBox Grid.Row="1" Grid.Column="1" Margin="2,2,2,2" Text="{Binding PlayerLastName}"/>
<Label Content="Player College" Grid.Row="2" Grid.Column="0" Margin="2,0,2,0" HorizontalAlignment="Right" Foreground="White"/>
<ComboBox Grid.Row="2" Grid.Column="1" Margin="2,2,2,2"
ItemsSource="{Binding Colleges}" DisplayMemberPath="CollegeName"
SelectedItem="{Binding SelectedCollege}"/>
<Button Content="Add Player" Grid.Row="3" Grid.Column="1" Width="75" Margin="2,2,2,2"
Background="Blue" Foreground="White" HorizontalAlignment="Right"
Command="{Binding NewPlayer}"/>
<ListView Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="1" Margin="2,2,2,2"
ItemsSource="{Binding Players, UpdateSourceTrigger=PropertyChanged}">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name" Width="100" DisplayMemberBinding="{Binding First}"/>
<GridViewColumn Header="Last Name" Width="100" DisplayMemberBinding="{Binding Last}"/>
<GridViewColumn Header="College" Width="100" DisplayMemberBinding="{Binding College}"/>
</GridView>
</ListView.View>
</ListView>
Which is bound to my PlayersViewModel Class
public class PlayersViewModel : ObservableObject
{
#region Variables
private DataView _Players = Controller.PlayerView.GetData().DefaultView;
private DataView _Colleges = Controller.CollegeTableAdapter.GetData().DefaultView;
private string _PlayerFirstName;
private string _PlayerLastName;
private DataRowView _SelectedCollege;
private long _PlayerCollegeID;
#endregion
#region Bindings
public DataView Players
{
get
{
return _Players;
}
set
{
_Players = value;
RaisePropertyChangedEvent("Players");
}
}
public DataView Colleges
{
get { return _Colleges; }
set
{
_Colleges = value;
RaisePropertyChangedEvent("Colleges");
}
}
public string PlayerFirstName
{
get { return _PlayerFirstName; }
set
{
_PlayerFirstName = value;
RaisePropertyChangedEvent("PlayerFirstName");
}
}
public string PlayerLastName
{
get { return _PlayerLastName; }
set
{
_PlayerLastName = value;
RaisePropertyChangedEvent("PlayerLastName");
}
}
public DataRowView SelectedCollege
{
get { return _SelectedCollege; }
set
{
_SelectedCollege = value;
_PlayerCollegeID = _SelectedCollege.Row.Field<long>("CollegeID");
RaisePropertyChangedEvent("SelectedCollege");
}
}
public ICommand NewPlayer
{
get { return new DelegateCommand(AddPlayer); }
}
#endregion
#region Methods
private void AddPlayer()
{
Controller.PersonTableAdapter.InsertPerson(_PlayerFirstName, _PlayerLastName, _PlayerCollegeID);
Players = Controller.PlayerView.GetData().DefaultView;
}
#endregion
}
This all works great, except when I run the InsertPerson Query in my PersonTableAdapter (which works manually and I've proven to work from the program), The Players ListView doesn't update after adding a new entry.
I found out what I did wrong.
Always make sure you are Implementing the INotifyPropertyChanged Interface in your ObservableObject Class
Mine looks like this now:
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
<ListView Name="listViewLoadDisableSchems" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.View>
<GridView>
<GridViewColumn >
<GridViewColumn.CellTemplate >
<DataTemplate >
<DataTemplate >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center" >
<Label Name="lblSchemeID" VerticalAlignment="Center" Margin="0" Content="{Binding Id}" Visibility="Hidden" />
<CheckBox Name="chkScheme" VerticalAlignment="Center" Margin="0,0,0,0" Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I want to get lblSchemeID if checkbox is selected. so i can update database.
How i can do it on butten click?
You need to look at some basic MVVM patterns to get this going. Try this in the XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate >
<DataTemplate >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center" >
<CheckBox VerticalAlignment="Center" Margin="0,0,0,0" Content="{Binding Name}" IsChecked="{Binding IsSelected}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<Button Grid.Row="1" Content="What is checked?" Command="{Binding GoCommand}"></Button>
and your equivalent of this in the view model & code behind:
public class MainViewModel
{
public ObservableCollection<TestItem> Items { get; set; } = new ObservableCollection<TestItem> { new TestItem() { Id = 1, Name = "Foo" }, new TestItem() { Id = 2, Name = "Bar" } };
public ICommand GoCommand => new DelegateCommand(Go);
void Go()
{
MessageBox.Show(string.Join(Environment.NewLine, Items.Where(x => x.IsSelected).Select(x => x.Name)));
}
}
public class TestItem : INotifyPropertyChanged
{
private bool _isSelected;
public int Id { get; set; }
public string Name { get; set; }
public bool IsSelected
{
get { return _isSelected; }
set
{
if (value == _isSelected) return;
_isSelected = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
You can get an implementation of delegate command from Google. Ditto implementations of INotifyPropertyChanged if you struggle with that.
I've a problem with the WPF datagrid. I have two ComboBox columns and the second one should set it's datacontext dependant of what is selected in the first one.
ViewModel
public class MyCollection: AbstractViewModel
{
private BindingList<string> _subitems;
public BindingList<string> Subitems
{
get { return _subitems; }
set { _subitems = value; Notify("Subitems");}
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; Notify("Name");}
}
}
Xaml
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SelectedName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedValuePath="Name"
x:Name="CollectionBox"
SelectedValue="{Binding SelectedName}"
DisplayMemberPath="Name"
ItemsSource="{Binding SomeBinding}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SelectedSubitem}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding SelectedSubitem}"
ItemsSource="{Binding ElementName=CollectionBox, Path=SelectedItem.Subitems}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
The binding for my first ComboBox is ok but I did not see any items in my second ComboBox (the viewmodel has entries for booth of the ComboBoxes); only the TextBox binding works as expected. I tryed it with a ComboBoxColumn as well but with the same result.
here an simple working example how you could use your ViewModel to achieve your needs
XAML
<DataGrid ItemsSource="{Binding Source}" Width="500">
<DataGrid.Columns>
<DataGridTemplateColumn Header="main" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SelectedName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedValuePath="Name"
SelectedValue="{Binding SelectedName}"
SelectedItem="{Binding SelectedMainItem, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name"
ItemsSource="{Binding SomeBinding}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="sub" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SelectedSubitem}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding SelectedSubitem}"
ItemsSource="{Binding Path=SelectedMainItem.Subitems}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new VMColl();
}
}
public class VMColl
{
List<VM> source;
public List<VM> Source
{
get { return source; }
set { source = value; }
}
public VMColl()
{
source = new List<VM>(){new VM(),new VM(),new VM(),new VM()};
}
}
/// <summary>
/// your Row
/// </summary>
public class VM : Notify
{
private List<mainObj> _items;
public List<mainObj> SomeBinding
{
get { return _items; }
set
{
_items = value;
OnPropertyChanged("Items");
}
}
private string _selectedName;
public string SelectedName
{
get { return _selectedName; }
set
{
_selectedName = value;
OnPropertyChanged("SelectedName");
}
}
private mainObj _selectedMainItem;
public mainObj SelectedMainItem
{
get { return _selectedMainItem; }
set
{
_selectedMainItem = value;
OnPropertyChanged("SelectedMainItem");
OnPropertyChanged("SelectedMainItem.Subitems");
}
}
private string _selectedSubitem;
public string SelectedSubitem
{
get { return _selectedSubitem; }
set
{
_selectedSubitem = value;
OnPropertyChanged("SelectedSubitem");
}
}
public VM()
{
SomeBinding = new List<mainObj>() {new mainObj("first"),new mainObj("second"),new mainObj("someother") };
}
}
public class mainObj : Notify
{
private BindingList<string> _subitems;
public BindingList<string> Subitems
{
get { return _subitems; }
set
{
_subitems = value;
OnPropertyChanged("Subitems");
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public mainObj(string name)
{
_name = name;
_subitems = new BindingList<string>(){"1","2","3"};
}
}
public class Notify : INotifyPropertyChanged
{
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
An update of #WiiMaxx answer :
To get selected subitem's value, you have to add a trigger in the second combobox :
<DataTemplate>
<ComboBox SelectedValue="{Binding SelectedSubitem , UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=SelectedMainItem.Subitems}" />
</DataTemplate>