I have a WPF project which reads Database entries. I built the model with EF 6 from the database.
Now I am a little bit confused how to apply the MVVM Pattern.
This is the model class generated from EF 6:
public partial class Folder
{
public string FolderPath { get; set; }
public string UserGroup { get; set; }
public string Permission { get; set; }
public string AccessControllType { get; set; }
public string IsInherited { get; set; }
public int FolderId { get; set; }
}
This is the MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private ADAccessRightsEntities _context = new ADAccessRightsEntities();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Data.CollectionViewSource folderViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("folderViewSource")));
folderViewSource.Source = _context.Folders.ToList();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
this._context.Dispose();
}
}
This is my xaml code:
<Window x:Class="PermissoinViewer.MainWindow"
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:PermissoinViewer"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
<Window.Resources>
<CollectionViewSource x:Key="folderViewSource" d:DesignSource="{d:DesignInstance {x:Type local:Folder}, CreateList=True}"/>
<CollectionViewSource x:Key="userGroupViewSource" d:DesignSource="{d:DesignInstance {x:Type local:UserGroup}, CreateList=True}"/>
</Window.Resources>
<Grid DataContext="{StaticResource folderViewSource}">
<DataGrid x:Name="folderDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding}" Margin="10,10,313,209" RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn x:Name="accessControllTypeColumn" Binding="{Binding AccessControllType}" Header="Access Controll Type" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="folderIdColumn" Binding="{Binding FolderId}" Header="Folder Id" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="folderPathColumn" Binding="{Binding FolderPath}" Header="Folder Path" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="isInheritedColumn" Binding="{Binding IsInherited}" Header="Is Inherited" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="permissionColumn" Binding="{Binding Permission}" Header="Permission" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="userGroupColumn" Binding="{Binding UserGroup}" Header="User Group" Width="SizeToHeader"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
How do I include a VM class now?
Edit1:
I created a FolderVM class.
class FolderVM : AViewModel
{
private Folder folder;
public FolderVM(Folder folder)
{
this.folder = folder;
}
public string FolderPath
{
get => folder.FolderPath;
set
{
folder.FolderPath = value;
CallPropertyChanged();
}
}
public string UserGroup
{
get => folder.UserGroup;
set
{
folder.UserGroup = value;
CallPropertyChanged();
}
}
public string Permission
{
get => folder.Permission;
set
{
folder.Permission = value;
CallPropertyChanged();
}
}
public string AccessControllType
{
get => folder.AccessControllType;
set
{
folder.AccessControllType = value;
CallPropertyChanged();
}
}
public string IsInherited
{
get => folder.IsInherited;
set
{
folder.IsInherited = value;
CallPropertyChanged();
}
}
public int FolderId
{
get => folder.FolderId;
set
{
folder.FolderId = value;
CallPropertyChanged();
}
}
}
AView Model:
abstract class AViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void CallPropertyChanged([CallerMemberName] string property = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
I want to filter them that only a specific UserGroup is displayed in the view. How should I continue?
Related
i have a xaml page in namespace Dca.KnxPanel.View with a Datagrid defined as
<DataGrid Grid.Row="2" Grid.Column="2" Name="GroupAddressDataGrid" ItemsSource="{Binding Source={x:Static viewModel:DcaAppVm._ProjectGroupAddress}}" AutoGenerateColumns="False" Margin="14,0,-14,0" Grid.RowSpan="2" >
<Border Background="GhostWhite" BorderBrush="Gainsboro" BorderThickness="1">
</Border>
<DataGrid.Columns>
<DataGridTextColumn Header="Group Address" Binding="{Binding Path=Address_Value}" Width="Auto"/>
<DataGridTextColumn Header="Description" Binding="{Binding Path=Datapoint_Name}" Width="Auto"/>
<DataGridTextColumn Header="Length" Binding="{Binding Path=Datapoint_Object_Size}" Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
where the DCA_GroupAddressList and DCA_GroupAddress are defined as in the namespace Dca.KnxPanel.Model
namespace Dca.KnxPanel.Model
{
[DebuggerDisplay("{Address_Value} {Datapoint_Name} {Datapoint_Object_Size}")]
public class DCA_GroupAddress
{
public DCA_GroupAddress(GroupAddress data) {
Address_Value = data.AddressValue.ToString();
Datapoint_Name = data.Name;
if (data.ObjectSize != null)
{
Datapoint_Object_Size = data.ObjectSize.Value;
}
}
public string Address_Value { get; set; }
public string Datapoint_Name { get; set; }
public ObjectSize Datapoint_Object_Size { get; set; }
}
public class DCA_GroupAddressList : ObservableCollection<DCA_GroupAddress>
{
public static DCA_GroupAddressList dca_groupaddresslist { get; set; }
public void Add(GroupAddress item)
{
this.Add(new DCA_GroupAddress(item));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action != null)
{
base.OnCollectionChanged(e);
}
}
}
}
the problem is that the Binding data in DataGrid not works but i don't understand why...
Can you help me?
i have tried many solution and at the end this is what works:
<DataGrid Grid.Row="2" Grid.Column="2" Name="GroupAddressDataGrid" ItemsSource="{Binding _ProjectGroupAddress}" AutoGenerateColumns="False" Margin="14,0,-14,0" Grid.RowSpan="2" >
<Border Background="GhostWhite" BorderBrush="Gainsboro" BorderThickness="1">
</Border>
<DataGrid.Columns>
<DataGridTextColumn Header="Group Address" Binding="{Binding Path=Address_Value}" Width="Auto"/>
<DataGridTextColumn Header="Description" Binding="{Binding Path=Datapoint_Name}" Width="Auto"/>
<DataGridTextColumn Header="Length" Binding="{Binding Path=Datapoint_Object_Size}" Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
The class DCA_GroupAddress now is:
public class DCA_GroupAddress : INotifyPropertyChanged
{
private string address_value, datapoint_name;
private ObjectSize datapoint_object_size;
public DCA_GroupAddress(GroupAddress data) {
Address_Value = data.AddressValue.ToString();
Datapoint_Name = data.Name;
if (data.ObjectSize != null)
{
Datapoint_Object_Size = data.ObjectSize.Value;
}
}
public string Address_Value {
get { return address_value; }
set {
address_value = value;
OnPropertyChanged(new PropertyChangedEventArgs("address_value"));
}
}
public string Datapoint_Name
{
get { return datapoint_name; }
set
{
datapoint_name = value;
OnPropertyChanged(new PropertyChangedEventArgs("datapoint_name"));
}
}
public ObjectSize Datapoint_Object_Size
{
get { return datapoint_object_size; }
set
{
datapoint_object_size = value;
OnPropertyChanged(new PropertyChangedEventArgs("datapoint_object_size"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
}
and i have define a container
public ObservableCollection<DCA_GroupAddress> _ProjectGroupAddress { get; set; } = new ObservableCollection<DCA_GroupAddress>();
my question is why i need to add these code:
GroupAddressDataGrid.Items.Clear();
GroupAddressDataGrid.ItemsSource = _ProjectGroupAddress;
in the UI class constructor? Seems that Items are not empty when the DataGrid is created so the software not use the ItemsSource. I need to clear the items before the binding works...
Thanks
stackoverflow!
Starting with my code:
XAML
<DataGrid Margin="25,112,25,10" Name="datGrid" RowHeight="30"
ColumnWidth="150" BorderThickness="1"
Style="{StaticResource AzureDataGrid}" IsReadOnly="False"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Content="CREATE" Name="headerCheckBox"
FontWeight="Bold" Width="Auto"
Checked="headerCheckBox_Checked"/>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Username" Binding="{Binding Path=Username}"/>
<DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName}"/>
<DataGridTextColumn Header="Last Name" Binding="{Binding Path=LastName}"/>
<DataGridTextColumn Header="Email" Binding="{Binding Path=Email}"/>
</DataGrid.Columns>
</DataGrid>
And C#
public partial class MainWindow : Window
{
ObservableCollection<MyData> MyItems = new ObservableCollection<MyData>();
public MainWindow()
{
datGrid.ItemsSource = MyItems;
MyItems.Add(new MyData { IsChecked = false, Username = "apetecca", FirstName = "Anthony", LastName = "Petecca", Email = "apetecca#email.com"});
MyItems.Add(new MyData { IsChecked = true, Username = "jhalls", FirstName = "Jake", LastName = "Halls", Email = "jhalls#email.com" });
}
public class MyData
{
public bool IsChecked { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
private void headerCheckBox_Checked(object sender, RoutedEventArgs e)
{
foreach (var item in MyItems)
{
item.IsChecked = true;
}
}
When I click on the headerCheckBox to check it, I've watched the variables in the foreach loop to see it is changing the items from false to true but doesn't visually show that on the DataGrid. However, if I manually check or uncheck the boxes, it displays correctly when going through that loop. The loop correctly sets the values but does not change the GUI display.
Everything I have seen online indicates it has to do with TwoWay mode and UpdateSourceTrigger. Both of these are set and I can't seem to find anything else on these.
Any help would be greatly appreciated!
EDIT-
I also tried changing my class to look like this
public class MyData
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged("IsChecked");
}
}
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
This didn't resolve it either.
Implement INotifyPropertyChanged and then have your properties raise event to make this happen.
Something like:
public class MyData : INotifyPropertyChanged
{
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set
{
if (isChecked != value)
{
isChecked = value;
OnPropertyChanged("IsChecked");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I would like to add a ComboBox column in my dataGrid "Product" with the list of Suppliers. But nothing I did works. What is the good way to bind this column?
The Models:
public partial class foodSupplier
{
public foodSupplier()
{
this.products = new HashSet<product>();
}
public int idfoodSupplier { get; set; }
public string supplier { get; set; }
public virtual ICollection<product> products { get; set; }
}
public partial class product
{
public int idproduct { get; set; }
public string #ref { get; set; }
public int supplier { get; set; }
public string refsup { get; set; }
public string description { get; set; }
public int MOQ { get; set; }
public int unit { get; set; }
public decimal priceMOQ { get; set; }
public virtual foodSupplier foodSupplier { get; set; }
public virtual unit unit1 { get; set; }
}
Here my command base (ViewModel):
public class CommandBase<T> :INotifyPropertyChanged
{
#region "INotifyPropertyChanged members"
public event PropertyChangedEventHandler PropertyChanged;
//This routine is called each time a property value has been set.
//This will //cause an event to notify WPF via data-binding that a change has occurred.
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private ObservableCollection<T> collection;
public ObservableCollection<T> Collection
{
get
{
if (collection == null)
{
collection = new ObservableCollection<T>();
}
return collection;
}
set { collection = value; OnPropertyChanged("Collection");}
}
private ICommand getCommand;
private ICommand saveCommand;
private ICommand removeCommand;
public ICommand GetCommand
{
get
{
return getCommand ?? (getCommand = new RelayCommand(param => Get(),param=>CanGet()));
}
}
protected virtual bool CanGet()
{
return true;
}
public ICommand SaveCommand
{
get
{
return saveCommand ?? (saveCommand = new RelayCommand(param => Save()));
}
}
protected virtual bool CanSave()
{
return true;
}
public ICommand DeleteCommand
{
get
{
return removeCommand ?? (removeCommand = new RelayCommand(param=>Delete()));
}
}
protected virtual bool CanDelete()
{
return true;
}
}
Here the ProductViewModel:
public class ProductViewModel : CommandBase<product>
{
public Context ctx = new Context();
protected override void Get()
{
ctx.products.ToList().ForEach(supplier => ctx.products.Local.Add(supplier));
Collection = ctx.products.Local;
}
protected override bool CanGet()
{
return true;
}
protected override void Save()
{
foreach (product item in Collection)
{
if (ctx.Entry(item).State == System.Data.Entity.EntityState.Added)
{
ctx.products.Add(item);
}
}
ctx.SaveChanges();
}
}
}
Here the SupplierViewModel:
public class SupplierViewModel : CommandBase<foodSupplier>
{
public Context ctx = new Context();
protected override void Get()
{
ctx.foodSuppliers.ToList().ForEach(supplier => ctx.foodSuppliers.Local.Add(supplier));
Collection = ctx.foodSuppliers.Local;
}
protected override bool CanGet()
{
return true;
}
protected override void Save()
{
foreach (foodSupplier item in Collection)
{
if (ctx.Entry(item).State == System.Data.Entity.EntityState.Added)
{
ctx.foodSuppliers.Add(item);
}
}
ctx.SaveChanges();
}
}
Here the view:
<Page.Resources>
<vm:ProductViewModel x:Key="product"/>
<vm:SupplierViewModel x:Key="supplier"/>
</Page.Resources>
<Grid DataContext="{Binding Source={StaticResource product}}">
<StackPanel Grid.ColumnSpan="2" Margin="0,0,58,0">
<Button x:Name="BtnDelete"
Content="Delete"
Command="{Binding DeleteCommand}"/>
<Button x:Name="BtnAdd"
Content="Save"
Command="{Binding SaveCommand}"/>
<DataGrid x:Name="dataGrid" Margin="5" ItemsSource="{Binding Collection}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="id" Binding="{Binding idproduct, UpdateSourceTrigger=PropertyChanged}" Visibility="Hidden"/>
<DataGridTextColumn Header="Ref FTHK" Binding="{Binding ref, UpdateSourceTrigger=PropertyChanged}" />
<DataGridComboBoxColumn Header="Supplier" ItemsSource="{Binding Collection, Source={StaticResource supplier}}????" DisplayMemberPath="supplier" SelectedValueBinding="{Binding ????}" SelectedValuePath="idSupplier"/>
<DataGridTextColumn Header="Ref Supplier" Binding="{Binding refsup, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="Description" Binding="{Binding description, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="MOQ" Binding="{Binding MOQ, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="Unit" Binding="{Binding unit, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="Prix/MOQ" Binding="{Binding priceMOQ, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding GetCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
</StackPanel>
</Grid>
you have to load the SupplierCollection same as you did in loading products. I will prefer the on demand concept. Somthing like the code below
public class CommandBase<T> : INotifyPropertyChanged
{
#region "INotifyPropertyChanged members"
public event PropertyChangedEventHandler PropertyChanged;
//This routine is called each time a property value has been set.
//This will //cause an event to notify WPF via data-binding that a change has occurred.
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private ObservableCollection<T> collection;
public ObservableCollection<T> Collection
{
get
{
if (collection == null)
{
Get();
}
return collection;
}
set { collection = value; OnPropertyChanged("Collection"); }
}
private ICommand getCommand;
private ICommand saveCommand;
private ICommand removeCommand;
public ICommand GetCommand
{
get
{
return getCommand ?? (getCommand = new RelayCommand(Get, CanGet));
}
}
protected virtual bool CanGet()
{
return true;
}
protected virtual void Get()
{
//return true;
}
//public ICommand SaveCommand
//{
// get
// {
// return saveCommand ?? (saveCommand = new RelayCommand(param => Save()));
// }
//}
protected virtual bool CanSave()
{
return true;
}
//public ICommand DeleteCommand
//{
// get
// {
// return removeCommand ?? (removeCommand = new RelayCommand(param => Delete()));
// }
//}
protected virtual bool CanDelete()
{
return true;
}
}
Now you view will look like.
<Page.Resources>
<vm:ProductViewModel x:Key="product"/>
<vm:SupplierViewModel x:Key="supplier"/>
<DataTemplate x:Key="SupplierDataTemplate">
<TextBlock Text="{Binding supplier}"/>
</DataTemplate>
</Page.Resources>
<Grid>
<StackPanel>
<Grid DataContext="{Binding Source={StaticResource product}}" x:Name="ProductGrid">
<StackPanel Grid.ColumnSpan="2" Margin="0,0,58,0">
<Button x:Name="BtnDelete"
Content="Delete"
Command="{Binding DeleteCommand}"/>
<Button x:Name="BtnAdd"
Content="Save"
Command="{Binding SaveCommand}"/>
<DataGrid x:Name="dataGrid" Margin="5" ItemsSource="{Binding Collection}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="id" Binding="{Binding idproduct, UpdateSourceTrigger=PropertyChanged}" Visibility="Hidden"/>
<DataGridTextColumn Header="Ref FTHK" Binding="{Binding ref, UpdateSourceTrigger=PropertyChanged}" />
<DataGridComboBoxColumn Header="Supplier" ItemsSource="{Binding Collection, Source={StaticResource supplier}}"
DisplayMemberPath="supplier"
SelectedValueBinding="{Binding supplier}"/>
<DataGridTextColumn Header="Ref Supplier" Binding="{Binding refsup, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="Description" Binding="{Binding description, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="MOQ" Binding="{Binding MOQ, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="Unit" Binding="{Binding unit, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="Prix/MOQ" Binding="{Binding priceMOQ, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
<!--<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding GetCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>-->
</DataGrid>
</StackPanel>
</Grid>
</StackPanel>
</Grid>
Hope it helps
I have two classes, Customer and Order. The entity, VM and xaml codes are below. What I'm trying to achieve is when I select a customer in datagrid on left, I want to see his/her orders on datagrid on the right. I can see the customers on the left datagrid, however I can't see their orders. I'm using Entity framework and MVVMLight. I'd appreciate if anyone can say what I'm missing. Thanks.
Here is base class :
public class BaseEntity : INotifyPropertyChanged
{
public virtual int Id { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Here is Customer class :
public class Customer : BaseEntity
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
private ICollection<Order> _orders;
public ICollection<Order> Orders
{
get { return _orders; }
set
{
_orders = value;
OnPropertyChanged("Orders");
}
}
}
Here is Order Class:
public class Order : BaseEntity
{
private string _explanation;
public string Explanation
{
get { return _explanation; }
set
{
_explanation = value;
OnPropertyChanged("Explanation");
}
}
private Customer _customer;
public Customer Customer
{
get { return _customer; }
set
{
_customer = value;
OnPropertyChanged("Customer");
}
}
}
And in ViewModel I have only Customer VM :
public class CustomerVM : ViewModelBase
{
protected CustomerOrderContext _context;
protected ObservableCollection<Customer> _entities;
protected Customer _selectedentity;
public ObservableCollection<Customer> Entities
{
get { return _entities; }
set
{
_entities = value;
RaisePropertyChanged(() => this.Entities);
DisplayEntityInfo();
}
}
public Customer SelectedEntity
{
get { return _selectedentity; }
set
{
_selectedentity = value;
RaisePropertyChanged(() => this.SelectedEntity);
DisplayEntityInfo();
}
}
public CustomerVM()
{
_context = new CustomerOrderContext();
Entities = GetEntities();
SelectedEntity = Entities.FirstOrDefault();
}
public ObservableCollection<Customer> GetEntities()
{
ObservableCollection<Customer> entities;
entities = new ObservableCollection<Customer>(_context.Set<Customer>());
return entities;
}
}
And this is my .xaml file :
<Window x:Class="CustomerOrder.App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cm="clr-namespace:System.ComponentModel;assembly=System"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:CustomerOrder.App.ViewModel"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
DataContext="{Binding Source={StaticResource Locator}, Path=CustomerView}"
Title="MainWindow" Height="500" Width="900">
<Grid>
<Canvas>
<DataGrid x:Name="maingrid" ItemsSource="{Binding Entities}" SelectedItem="{Binding SelectedEntity}" AutoGenerateColumns="False" Canvas.Left="10" Canvas.Top="265">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="200"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<DataGrid x:Name="ordergrid" ItemsSource="{Binding ElementName=maingrid, Path=SelectedItem.Orders}" AutoGenerateColumns="True" CanUserDeleteRows="True" Canvas.Top="265" Canvas.Left="597">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Explanation}" Header="Orders" Width="200"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Canvas>
</Grid>
</Window>
The first DataGrid has SelectedItem bound to 'SelectedEntity'. You should bind the second DataGrid's ItemSource property to the same 'SelectedEntity' property on your ViewModel
<DataGrid x:Name="ordergrid" ItemsSource="{Binding SelectedEntity.Orders}" AutoGenerateColumns="True" CanUserDeleteRows="True" Canvas.Top="265" Canvas.Left="597">
My little project looks now quite different. Now I have ObservableCollection PackagesList with data which I want to bind with whole DataGrid (via Binding Path) and ObservableCollection DestinationItememsSource where I store cases for DataGridComboboxColumn (as ItemsSourceBinding). SelectedItem property in DatagridComboboxColumn is one value from PackageInfo (and it is one of DestinationNames cases ofc). Binding on DatagridTextBoxColumns is ok.
XAML:
<Window x:Class="test01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="400" Loaded="Window_Loaded">
<Grid>
<Border x:Name="mainBorder" Margin="20">
<DataGrid x:Name="mainDataGrid" x:Uid="mainDataGrid" AutoGenerateColumns="False"
AlternationCount="2" SelectionMode="Single" HorizontalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=id}"
Header="ID" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path=name}"
Header="Name" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path=quantity}"
Header="Quantity" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path=price}"
Header="Price" Width="Auto" IsReadOnly="True"/>
<DataGridComboBoxColumn ItemsSource="{Binding DestinationItemsSource}"
SelectedItemBinding="{Binding Path=destination, UpdateSourceTrigger=PropertyChanged}"
Header="Destination" Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
</Border>
</Grid>
</Window>
C#:
public class PackageInfo
{
public int id { get; set; }
public string name { get; set; }
public int quantity { get; set; }
public double price { get; set; }
public string destination { get; set; }
}
public class DestinationNames : INotifyPropertyChanged
{
public string name { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public DestinationNames(string value)
{
this.name = value;
}
public string DestinationName
{
get { return name; }
set
{
name = value;
OnPropertyChanged("DestinationName");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public partial class MainWindow : Window
{
public ObservableCollection<DestinationNames> DestinationItememsSource { get; set; }
public ObservableCollection<PackageInfo> PackagesList { get; set; }
public MainWindow()
{
InitializeComponent();
}
public void Window_Loaded(object sender, RoutedEventArgs e)
{
LoadPackages();
}
public void LoadPackages()
{
DestinationItememsSource = new ObservableCollection<DestinationNames>();
DestinationItememsSource.Add(new DestinationNames("London"));
DestinationItememsSource.Add(new DestinationNames("Plymouth"));
DestinationItememsSource.Add(new DestinationNames("Birmingham"));
DestinationItememsSource.Add(new DestinationNames("Cambridge"));
PackagesList = new ObservableCollection<PackageInfo>();
PackagesList.Add(new PackageInfo { id = 1, name = "Potato", quantity = 3, price = 2.2, destination = "London" });
PackagesList.Add(new PackageInfo { id = 2, name = "Tomato", quantity = 5, price = 3.8, destination = "Plymouth" });
PackagesList.Add(new PackageInfo { id = 3, name = "Carrot", quantity = 1, price = 5.1, destination = "London" });
PackagesList.Add(new PackageInfo { id = 4, name = "Pea", quantity = 6, price = 1.8, destination = "Plymouth" });
PackagesList.Add(new PackageInfo { id = 5, name = "Bean", quantity = 2, price = 1.5, destination = "Birmingham" });
mainDataGrid.ItemsSource = PackagesList;
}
}
How to bind this DatagridComboboxColumn properly? Should I use INotifyCollectionChanged ?? What if I want to all data in datagrid will be automatically synced with ObservableCollection ?? Please help with some example.
Have PackageInfo implement INotifyPropertyChanged.
An ObservableCollection automatically implements INotifyCollectionChanged.
You will need to add List or ObservableCollection Destinations as a property of PackageInfo
NO class DestinationNames
Just a class DestinationName
Your binding to DestinationItememsSource does not work, because it isnt part of the PackageInfo object. You also cant bind to the Windows DataContext via FindAncestor or ElementName-binding because DataGrid-Columns wont be added to the visual tree.
One solution I can think of is using the CollectionViewSource:
<Window.Resources>
<CollectionViewSource Source="{Binding RelativeSource={RelativeSource Self}, Path=DestinationItememsSource}" x:Key="destinations">
<!--here you can also add Group and SortDescriptions-->
</CollectionViewSource>
</Window.Resources>
<DataGridComboBoxColumn ItemsSource="{Binding Source={StaticResource ResourceKey=destinations}}".../>
I cant test it atm, because I'm still downloading VS 2012.^^
Anotherone would be to simply add a List of Destinations to your PackageInfo object (but this would cause a lot of redundancies).