Hello,
I am using observable collection of StaffMember and which as has 3 members - StaffCategory(string), StaffTypes(string), Staff(List)ObservableCollection<StaffMember> StaffMembers StaffCategory and StaffTypes are column 1 and 2 which are constant and created in XAML view as a fields of XamDataGrid. Depending on number of Staff, multiple columns gets populated column3 onwards . All this operation happening on button click. After this, I would like to update FieldLayout depending on number of Staff in collection. If I use xamDataGrid_FieldLayoutInitialized then fieldlayout shows all columns. This done from constructor. private void Xdg_OnFieldLayoutInitialized(object sender, Infragistics.Windows.DataPresenter.Events.FieldLayoutInitializedEventArgs e) { var staffMember = this.ViewModel.StaffMembers.First(); for (Int32 i = 0; i < staffMember.Staff.Count; i++) { var field = new UnboundField { Name = staffMember.Staff[i].Name, BindingMode = BindingMode.TwoWay, BindingPath = new PropertyPath(String.Format("Staff[{0}].Value", i)) }; field.Settings.EditAsType = typeof(String); e.FieldLayout.Fields.Add(field); } } However, when I doing using button click.. it is not showing columns private void BtnGenerate_OnClick(object sender, RoutedEventArgs e) { var staffMember = this.ViewModel.StaffMembers.First(); for (Int32 i = 0; i < StaffMember.Staff.Count; i++) { var field = new UnboundField { Name = StaffMember.Staff[i].Name, BindingMode = BindingMode.TwoWay, BindingPath = new PropertyPath(String.Format("Staff[{0}].Value", i)) }; field.Settings.EditAsType = typeof(String); xdg.FieldLayouts[0].Fields.Add(field); } } Please help me on this if I am missing anything here.
Hi Rob,
I still didn't manage to complete using last idea. so I am going with earlier solution.
Thanks for sharing these details.
Cheers
Sagar
Hi Sagar,
Using the Button's Command property would be ideal for that. You can have an ICommand in your view model and bind it to the Button. When the button is clicked it will automatically trigger the command and you can run code in there. Unfortunately you are going to run into an issue with your current code. Right now in the button click you are iterating over the Staff collection which is fine. The issue is that you are creating UnboundFields and adding them to the XamDataGrid. You won't be able to do this from your view model since the view model is not supposed to reference UI controls.
How you decide to work around this is mainly up to you but one way you might handle this is to create a Behavior<T> that you can attach to the XamDataGrid. This behavior will be responsible for taking information from your view model and creating UnboundFields from it. So for example, in your view model you might create a "FieldViewModel" class and a collection to hold a bunch of them. Inside this FieldViewModel class there would be properties for Name, BindingMode, BindingPath and EditAsType. When your button is clicked and the ICommand is executed you can create a bunch of FieldViewModel objects and store them in a collection. This is where the Behavior<T> will come in.
Inside the Behavior<T> will be a DependencyProperty for a filter view model collection. You will bind this collection to the view model's FieldViewModel collection. So when view model's FieldViewModel collection is populated, it will update the Behavior<T> and the behavior can then take each FieldViewModel object and create a Field/UnboundField for it and add it to the XamDataGrid.
That's just a rough idea so the actual implementation might be different. Hopefully it makes sense.
Hi Rob
Yes, you are correct. Implementing INotifyPropertyChanged does resolve the problem.
Can I ask one more sample in this regards? Is there is way to move code from button click event to View Model? I do not want to keep code in MainWindow.xaml.cs file.
CheersSagar
In my sample it doesn't work because I didn't implement INotifyPropertyChanged on the StaffMembers collection. The StaffMembers collection is not instantiated until GenerateData is called so when the XamDataGrid binds to it, its currently null. Now when the button is clicked it creates the StaffMembers instance but it never notifies the datagrid that the datasource has been updated. If I implement INotifyPropertyChanged on the property then the above code changes you made will work.
private ObservableCollection<StaffMember> _staffMembers;public ObservableCollection<StaffMember> StaffMembers{ get { return _staffMembers; } set { if (_staffMembers != value) { _staffMembers = value; OnPropertyChanged("StaffMembers"); } }}
If you haven't implemented INotifyPropertyChanged on your properties then that is most likely why your columns do not appear. The XamDataGrid still thinks it has a null DataSource.
Thanks for looking into this so quickly.
I done few changes in your attached solution and managed to replicate issue at my end.
To reproduce, you can do these changes-
1. Change ViewModel like below-
public class MainWindowViewModel { public ObservableCollection<StaffMember> StaffMembers { get; set; }
public MainWindowViewModel() { //Moved your code to method as it will generated during runtime }
public void GenerateData() { StaffMembers = new ObservableCollection<StaffMember>(); StaffMembers.Add(new StaffMember { StaffCategory = "category1", StaffTypes = "type1", Staff = new List<Staff>() }); StaffMembers[0].Staff.Add(new Staff { Name = "staff1", Value = "value1" }); StaffMembers[0].Staff.Add(new Staff { Name = "staff2", Value = "value1" }); StaffMembers[0].Staff.Add(new Staff { Name = "staff3", Value = "value1" }); StaffMembers[0].Staff.Add(new Staff { Name = "staff4", Value = "value1" });
StaffMembers.Add(new StaffMember { StaffCategory = "category2", StaffTypes = "type2", Staff = new List<Staff>() }); StaffMembers[1].Staff.Add(new Staff { Name = "staff1", Value = "value2" }); StaffMembers[1].Staff.Add(new Staff { Name = "staff2", Value = "value2" }); StaffMembers[1].Staff.Add(new Staff { Name = "staff3", Value = "value2" }); StaffMembers[1].Staff.Add(new Staff { Name = "staff4", Value = "value2" });
StaffMembers.Add(new StaffMember { StaffCategory = "category3", StaffTypes = "type3", Staff = new List<Staff>() }); StaffMembers[2].Staff.Add(new Staff { Name = "staff1", Value = "value3" }); StaffMembers[2].Staff.Add(new Staff { Name = "staff2", Value = "value3" }); StaffMembers[2].Staff.Add(new Staff { Name = "staff3", Value = "value3" }); StaffMembers[2].Staff.Add(new Staff { Name = "staff4", Value = "value3" }); } }
2. Call ViewModel method from button click (refer 1st statement in button click)
private void Button_Click(object sender, RoutedEventArgs e) { ((MainWindowViewModel)this.DataContext).GenerateData();
var staffMember = this.ViewModel.StaffMembers.First();
for (Int32 i = 0; i < staffMember.Staff.Count; i++) { var field = new UnboundField { Name = staffMember.Staff[i].Name, BindingMode = BindingMode.TwoWay, BindingPath = new PropertyPath(String.Format("Staff[{0}].Value", i)) };
field.Settings.EditAsType = typeof(String); xdg.FieldLayouts[0].Fields.Add(field); } }
Please let me know your thoughts.