I want to get / count all checked items from DataGrid. I've 20 items in DataGrid as you can see in below pic. Out of 20, visible items are 14 or 13. When i mark checked on all items by clicking Select All Button, all 20 items gets mark checked. but when i count checked items by clicking Print Button, the counting shows it mark just 14. what the matter? kindly help
Datagrid:
<DataGrid HorizontalAlignment="Left" Height="265" Margin="10,74,0,0"
Name="GridTable" AutoGenerateColumns="False" VerticalAlignment="Top" Width="766" CanUserAddRows="False" IsReadOnly="False" SelectionMode="Single" SelectionUnit="Cell">
<DataGrid.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="#0073c4"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Select" Binding="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="Code" Width="1*" IsReadOnly="True" Binding="{Binding Code}"/>
<DataGridTextColumn Header="Name" Width="1*" IsReadOnly="True" Binding="{Binding Name}"/>
</DataGrid.Columns>
</DataGrid>
SelectAll Button:
private void SelectAllBtn_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < GridTable.Items.Count; i++)
{
var item = GridTable.Items[i];
Item itemm = (Item)GridTable.Items[i];
itemm.IsSelected = true;
var mycheckbox = GridTable.Columns[0].GetCellContent(item) as CheckBox;
if (mycheckbox != null)
{
mycheckbox.IsChecked = true;
}
}
}
Print Button click:
private void Print_Click(object sender, RoutedEventArgs e)
{
int count = 0;
for (int i = 0; i < GridTable.Items.Count; i++)
{
var item = GridTable.Items[i];
if (GridTable.Columns[0].GetCellContent(item) as CheckBox != null)
{
var mycheckbox = GridTable.Columns[0].GetCellContent(item) as CheckBox;
if ((bool)mycheckbox.IsChecked)
{
count++;
listTobePrint.Add(tableList[i]);
}
}
}
MessageBox.Show(count.ToString());
Note: this MessageBox.Show(count.ToString());
counts 14
Item.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Inventory_Control.Classes
{
class Item : INotifyPropertyChanged
{
private string id;
private string code;
private string name;
private string description;
private string quantity;
private string availableQuantity;
private string unitprice;
private string subtotal;
private string category;
private string type;
private string location;
private string index;
private bool _isSelected;
public string Id
{
get { return id; }
set
{
id = value;
NotifyPropertyChanged("Id");
}
}
public string Code
{
get { return code; }
set
{
code = value;
NotifyPropertyChanged("Code");
}
}
public string Name
{
get { return name; }
set
{
name = value;
NotifyPropertyChanged("Name");
}
}
public string Description
{
get { return description; }
set
{
description = value;
NotifyPropertyChanged("Description");
}
}
public string Quantity
{
get { return quantity; }
set
{
quantity = value;
NotifyPropertyChanged("Quantity");
}
}
public string AvailableQuantity
{
get { return availableQuantity; }
set
{
availableQuantity = value;
NotifyPropertyChanged("AvailableQuantity");
}
}
public string UnitPrice
{
get { return unitprice; }
set
{
unitprice = value;
NotifyPropertyChanged("UnitPrice");
}
}
public string SubTotal
{
get { return subtotal; }
set
{
subtotal = value;
NotifyPropertyChanged("SubTotal");
}
}
public string Category
{
get { return category; }
set
{
category = value;
NotifyPropertyChanged("Category");
}
}
public string Type
{
get { return type; }
set
{
type = value;
NotifyPropertyChanged("Type");
}
}
public string Location
{
get { return location; }
set
{
location = value;
NotifyPropertyChanged("Location");
}
}
public string Index
{
get { return index; }
set
{
index = value;
NotifyPropertyChanged("Index");
}
}
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
It's because your DataGrid uses row virtualisation. It is telling you the truth - it only has 14 selected rows because that is all it needs to present your underlying data.
The CODE magazine article XAML Anti-Patterns: Virtualization has a great explanation.
I notice you haven't databound your DataGrid, you must be adding the rows programmatically? The correct (proper) way to access your selected items is to go to your viewmodel that contains the collection of Item objects that you've bound the DataGrid to, and iterate that collection to find the selected items.
Related
I use this codes. In this codes, I want to change each row's background color when I click the button.
I tried to bind with DataGridRow's background. But I only get BindingExpression.
I know that if I use ObservableCollection as rowdata, it will slove very easy. But I cannot use the collection because I want to bind each column's visibility, too.
I cannot slove to this problem with this code? Please some help.
<StackPanel>
<CheckBox IsChecked="{Binding IsChecked}" Content="I change the header of Column A and I hide Column B." Margin="10"/>
<Button Content="Click!" Click="Button_OnClick" Margin="10" Width="50"/>
<DataGrid IsReadOnly="True"
ItemsSource="{Binding Table}"
AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn"
x:Name="DataGrid1"
Loaded="DataGrid1_Loaded">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{Binding MyBackground, Mode=TwoWay}"/>
</Style>
</DataGrid.RowStyle>
</DataGrid>
</StackPanel>
public partial class MainWindow : Window
{
public ViewModel _viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = _viewModel;
}
private void DataGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
BindingOperations.SetBinding(e.Column,
DataGridColumn.HeaderProperty,
new Binding($"{nameof(ViewModel.ColumnHeader)}[{e.PropertyName}]")
{
Source = _viewModel
});
BindingOperations.SetBinding(e.Column,
DataGridColumn.VisibilityProperty,
new Binding($"{nameof(ViewModel.ColumnVisibility)}[{e.PropertyName}].{nameof(BindableValue_ColumnVisible<Visibility>.Value)}")
{
Source = _viewModel
});
}
private void Button_OnClick(object sender, RoutedEventArgs e)
{
}
int mCount = 0;
string[] displayedColumnOrder;
private void DataGrid1_Loaded(object sender, RoutedEventArgs e)
{
displayedColumnOrder = new string[_viewModel.Table.Columns.Count];
DataGrid _datagrid = (DataGrid)sender;
_getColumnOrder(_datagrid.Columns);
}
void _getColumnOrder(IEnumerable<DataGridColumn> columnCollection)
{
DataGridColumn[] columnArray;
int columnIndexWorking;
displayedColumnOrder = new string[columnCollection.Count()];
columnArray = columnCollection.ToArray();
foreach (var item_Column in columnCollection)
{
columnIndexWorking = item_Column.DisplayIndex;
displayedColumnOrder[columnIndexWorking] = item_Column.Header.ToString();
}
}
}
public class ViewModel : BindableBase
{
private Brush _myBackground = Brushes.AliceBlue;
public Brush MyBackground
{
get
{
return _myBackground;
}
set
{
_myBackground = value;
NotifyPropertyChanged(nameof(MyBackground));
}
}
private bool _isChecked = false;
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
_isChecked = value;
if (value == true)
{
SetHeader();
SetVisible();
}
else
{
UnSetHeader();
UnSetVisible();
}
NotifyPropertyChanged(nameof(IsChecked));
}
}
public DataTable Table { get; } = new DataTable();
public Dictionary<string, string> ColumnHeader { get; } = new Dictionary<string, string>();
public Dictionary<string, BindableValue_ColumnVisible<Visibility>> ColumnVisibility { get; } = new Dictionary<string, BindableValue_ColumnVisible<Visibility>>();
public ViewModel()
{
Table.Columns.Add("A");
Table.Columns.Add("B");
Table.Columns.Add("C");
Table.Columns.Add("D");
Table.Columns.Add("E");
for (int i = 0; i < 10; i++)
{
Table.Rows.Add($"A-{i}", $"B-{i}", $"C-{i}", $"D-{i}", $"E-{i}");
}
foreach (DataColumn column in Table.Columns)
{
ColumnHeader.Add(column.ColumnName, $"Column {column.ColumnName}");
if (column.ColumnName == "B")
{
ColumnVisibility.Add(column.ColumnName, BindableValue_ColumnVisible.Create(Visibility.Collapsed));
}
else
{
ColumnVisibility.Add(column.ColumnName, BindableValue_ColumnVisible.Create(Visibility.Visible));
}
}
}
public void SetHeader()
{
ColumnHeader["A"] = "I changed Column A!!";
NotifyPropertyChanged(nameof(ColumnHeader));
}
public void SetVisible()
{
ColumnVisibility["B"].Value = Visibility.Collapsed;
}
public void UnSetHeader()
{
ColumnHeader["A"] = "Column A";
NotifyPropertyChanged(nameof(ColumnHeader));
}
public void UnSetVisible()
{
ColumnVisibility["B"].Value = Visibility.Visible;
}
}
public class BindableValue_ColumnVisible<T> : BindableBase
{
public T Value
{
get => _value;
set => SetColumnVisibleProperty(ref _value, value);
}
private T _value;
public BindableValue_ColumnVisible()
{
}
public BindableValue_ColumnVisible(T value)
{
Value = value;
}
}
public static class BindableValue_ColumnVisible
{
public static BindableValue_ColumnVisible<T> Create<T>(T value) => new BindableValue_ColumnVisible<T>(value);
}
public class BindableBase : INotifyPropertyChanged
{
protected virtual bool SetColumnVisibleProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
return SetColumnVisibleProperty(ref field, value, null, propertyName);
}
protected virtual bool SetColumnVisibleProperty<T>(ref T field, T value, Action onChanged, [CallerMemberName]string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
onChanged?.Invoke();
NotifyPropertyChanged(propertyName);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
If you add a "MyBackground" column to your DataTable, your current RowStyle and binding should work:
public ViewModel()
{
Table.Columns.Add("A");
Table.Columns.Add("B");
Table.Columns.Add("C");
Table.Columns.Add("D");
Table.Columns.Add("E");
Table.Columns.Add("MyBackground");
for (int i = 0; i < 10; i++)
{
Table.Rows.Add($"A-{i}", $"B-{i}", $"C-{i}", $"D-{i}", $"E-{i}", "Yellow");
}
...
}
If you set the column to a known string representation of a brush, such as for example "Yellow" or "Red", you don't have to something else. Else, you could use a converter that converts the value in the DataTable to a Brush.
By the way, it's pointless to set the Mode of this Binding to TwoWay.
public class MyDataTable : System.Data.DataTable {
private Brush _myBackground = Brushes.AliceBlue;
public Brush MyBackground {
get {
return _myBackground;
}
set {
_myBackground = value;
NotifyPropertyChanged(nameof(MyBackground));
}
}
}
public MyDataTable Table { get; } = new MyDataTable();
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="{Binding MyBackground }" />
</Style>
I have two classes that have identical Name and Position properties. A ComboBox in a DataGrid has a list of StaffMember class taken from a database. It displays the name only but upon selection both Name and Position properties should change in the DataGrid bound to ObservableCollection of another class - Person.
So far I'm using the SelectionChanged event of the combobox to browse the VisualTree, access parent DataContext and change two properties at a time.
Is there a different approach to it?
Update. Here's an illustrating picture:
I get the Name and Position from a third party service and display the name in the combobox. As user selects a name the UI should update both Name and Position properties in the table. The table also has Age and many other columns/properties in real world. That is why there are two classes: a name/position list from a database to select from and a class which is the ItemSource of the table. I also have to deal with cases when people have the same name but different positions. Hope this explains the question better.
public class StaffMember : NotifyObject
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
private string _position;
public string Position
{
get { return _position; }
set { _position = value; OnPropertyChanged("Position"); }
}
}
public class Person : NotifyObject
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
private string _position;
public string Position
{
get { return _position; }
set { _position = value; OnPropertyChanged("Position"); }
}
public double Age { get; set; }
}
ViewModel:
public class ViewModel : NotifyObject
{
public ObservableCollection<Person> SelectedPeople { get; set; }
public List<StaffMember> Staff { get; set; }
public ViewModel()
{
Staff = new List<StaffMember>
{
new StaffMember { Name = "Sigmund Freud", Position = "Psychologist"},
new StaffMember { Name = "Louis Armstrong", Position = "Musician"},
new StaffMember { Name = "John Doe", Position = "Superviser"},
new StaffMember { Name = "John Doe", Position = "Manager"},
};
SelectedPeople = new ObservableCollection<Person> {
new Person { Name = "Sigmund Freud", Position = "123", Age= 161 },
new Person(),
new Person() };
}
}
public abstract class NotifyObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
public void RaiseProperychanged(string propertyName)
{
OnPropertyChanged(propertyName);
}
}
XAML:
<Grid>
<DataGrid ItemsSource="{Binding SelectedPeople}"
AutoGenerateColumns="False"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="ComboBoxSelect" ItemsSource="{Binding ElementName=TheMainWindow, Path=DataContext.Staff}"
SelectedValue="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridRow}, Path=DataContext.Name, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectionChanged="ComboBoxSelect_SelectionChanged"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Position" Binding="{Binding Position, Mode=TwoWay}"/>
<DataGridTextColumn Header="Age" Binding="{Binding Age}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Code-Behind:
private void ComboBoxSelect_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = sender as ComboBox;
var parent = sender as DependencyObject;
while (!(parent is DataGridCell))
{
parent = VisualTreeHelper.GetParent(parent);
if (parent is null) return;
}
var cell = parent as DataGridCell;
if (cell == null) return;
var person = cell.DataContext as Person;
if (person == null) return;
person.Position = ((StaffMember)comboBox.SelectedItem).Position.ToString();
}
It is not a good idea to mix MVVM and code-behind.
You have a duplication of data in Person and StaffMember class.
What about idea of having StaffMember property in Person class?
public class StaffMember : NotifyObject
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
private string _position;
public string Position
{
get { return _position; }
set { _position = value; OnPropertyChanged("Position"); }
}
}
public class Person : NotifyObject
{
private string _staffMember;
public string StaffMember
{
get { return _staffMember; }
set { _staffMember = value; OnPropertyChanged("StaffMember"); }
}
public double Age { get; set; }
}
I have a small DataGrid to do a simple operation. Fields are 3: Number 1, 2 and Result Numer.
DataGrid code is as follows:
<DataGrid x:Name="dgNumbers" ItemsSource="{Binding lstOperations, Mode=TwoWay}" CanUserAddRows="True" AutoGenerateColumns="False" CellEditEnding="dgNumbers_CellEditEnding">
<DataGrid.Columns>
<DataGridTextColumn Header="Number 1" Width="*" Binding="{Binding N1, Mode=TwoWay}"/>
<DataGridTextColumn Header="Number 2" Width="*" Binding="{Binding N2, Mode=TwoWay}"/>
<DataGridTextColumn Header="Result" Width="*" Binding="{Binding Result, Mode=TwoWay}" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
I created an object where I keep the number 1, the number and outcome. This is the class code:
public class Numbers
{
public decimal N1 { get; set; }
public decimal N2 { get; set; }
public decimal Result { get; set; }
}
I made this small example to try to understand the Binding that make the ObservableCollection.
For this example, I have the following code in the event:
public MainWindow()
{
InitializeComponent();
lstOperations = new ObservableCollection<Numbers>();
}
ObservableCollection<Numbers> lstOperations;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Numbers n = new Numbers();
n.N1 = 10;
n.N2 = 5;
n.Result = 15;
lstOperations.Add(n);
dgNumbers.ItemsSource = lstOperations;
}
private void dgNumbers_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
foreach(var item in lstOperations)
{
item.Result = item.N1 + item.N2;
}
}
What I am trying to know if changing a data collection, this fact is reflected in the DataGrid, if possible, how to do it right ?, if not possible, how to achieve something similar?
You can also install by NuGet Prism.Core and use the BindableBase class:
using Prism.Mvvm;
using System.Collections.ObjectModel;
using System.Windows;
namespace YourApplication
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
private void dgNumbers_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
foreach (var item in (DataContext as MainWindowViewModel).LstOperations)
{
item.Result = item.N1 + item.N2;
}
}
}
public class MainWindowViewModel : BindableBase
{
private ObservableCollection<Numbers> _lstOperations;
public ObservableCollection<Numbers> LstOperations
{
get { return _lstOperations; }
set
{
_lstOperations = value;
OnPropertyChanged();
}
}
public MainWindowViewModel()
{
_lstOperations = new ObservableCollection<Numbers>();
Numbers n = new Numbers
{
N1 = 10,
N2 = 5,
Result = 15
};
LstOperations.Add(n);
}
}
public class Numbers : BindableBase
{
private decimal _n1;
public decimal N1
{
get { return _n1; }
set { SetProperty(ref _n1, value); }
}
private decimal _n2;
public decimal N2
{
get { return _n2; }
set { SetProperty(ref _n2, value); }
}
private decimal _result;
public decimal Result
{
get { return _result; }
set { SetProperty(ref _result, value); }
}
}
}
In the end you also have to change the Binding in the View:
<DataGrid x:Name="dgNumbers" ItemsSource="{Binding LstOperations, Mode=TwoWay}" CanUserAddRows="True" AutoGenerateColumns="False" CellEditEnding="dgNumbers_CellEditEnding">
Using BindableBase is quite easy because it takes the CallerMemberName attribute in the implementation, so you don't have to specify which property is being called (you write OnPropertyChanged() in the setter instead of OnPropertyChanged("propertyName")). And it's even better, because there is also the SetProperty method, which sets the new value AND calls OnPropertyChanged event only when needed (when the new value is really new, really changed).
Do you have a class which implements INotifyPropertyChanged?
You can subscribe to ObservableCollection events. CollectionChange would probably do the trick but doesn't take advantage of data binding.
public MainWindow()
{
InitializeComponent();
lstOperations = new ObservableCollection<Numbers>();
lstOperations.CollectionChanged += MyCollectionChanged;
}
private MyCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
dgNumbers.ItemsSource = lstOperations;
}
So the basic data binding would go like this.
public class ModelView : INotifyPropertyChanged
{
public ModelView()
{
lstOperations = new ObservableCollection<Numbers>();
lstOperations.CollectionChanged += new NotifyCollectionChangedEventHandler((obj, e) => { OnPropertyChanged("lstOperations "); });
}
//----------------- Implementing the interface here
public event PropertyChangedEventHandler PropertyChanged;
// Call this method when you want the GUI updated.
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
//-------------------Your Properties-----------------------------
private ObservableCollection<Numbers> _lstOperations ;
public ObservableCollection<Numbers> lstOperations
{
get{return _lstOperations ;}
set
{
_lstOperations = value;
OnPropertyChanged("lstOperations");
}
}
Ok the class above now contains your variable you want to bind. Now you need to set your datacontext for the Datagrid.
// Need your model instance.
private ModelView Model;
public MainWindow()
{
InitializeComponent();
Model = new ModelView();
dgNumbers.DataContext = Model;
}
Now anywhere you are manipulating lstOperations you manipulate Model.lstOperations.
forgive my stupid tip but for me it works when i bind such a list
public class ModelNumber
{
public decimal N1 { get; set; }
public decimal N2 { get; set; }
public decimal Result { get; set; }
}
public class ViewModelNumber : NotifyPropertyChanged, IDataErrorInfo
{
protected ModelNumber __dataModel = null;
#region ---constructor---
public ViewModelNumber()
{
// model init
this.__dataModel = new ModelNumber();
}
#endregion
#region ---accessoren model basis---
public decimal N1
{
get
{
return this.__dataModel.N1;
}
set
{
if (this.__dataModel.N1 != value)
{
this.__dataModel.N1 = value;
this.OnPropertyChanged("N1");
}
}
}
public decimal N2
{
get
{
return this.__dataModel.N2;
}
set
{
if (this.__dataModel.N2 != value)
{
this.__dataModel.N2 = value;
this.OnPropertyChanged("N1");
}
}
}
public decimal Result
{
get
{
return this.__dataModel.Result;
}
set
{
if (this.__dataModel.Result != value)
{
this.__dataModel.Result = value;
this.OnPropertyChanged("N1");
}
}
}
#endregion
#region ---validation---
/// <summary>Gets an error message indicating what is wrong with this object.</summary>
public string Error
{
get { throw new NotImplementedException(); }
}
/// <summary>Gets the error message for the property with the given name.</summary>
public string this[string _columnName]
{
get
{
try
{
if (_columnName != null)
{
switch (_columnName)
{
default:
break;
}
}
return (null);
}
catch (Exception _except)
{
// mlog
Log.Exception(this.GetType().FullName, MethodBase.GetCurrentMethod().Name, _except);
return (null);
}
}
}
#endregion
}
ObservableCollection<ViewModelNumber> lstOperations;
This is my first question so I'll do my best.
I'm trying to get a simple WPF DataGrid control to be "refreshed" using ObservableCollection and MVVM as the numerous tutorials on the web explain.
In context, these are my model clases:
(PersonViewModel.cs)
public class PersonViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _id;
private int _idAddress;
private string _name;
private int _age;
private string _address;
public int Id
{
get { return this._id; }
set
{
if (value != this._id)
{
this._id = value;
OnPropertyChanged();
}
}
}
public int IdAddress
{
get { return this._idAddress; }
set
{
if (value != this._idAddress)
{
this._idAddress = value;
OnPropertyChanged();
}
}
}
public string Name
{
get { return this._name; }
set
{
if (value != this._name)
{
this._name = value;
OnPropertyChanged();
}
}
}
public int Age
{
get { return this._age; }
set
{
if (value != this._age)
{
this._age = value;
OnPropertyChanged();
}
}
}
public string Address
{
get { return this._address; }
set
{
if (value != this._address)
{
this._address = value;
OnPropertyChanged();
}
}
}
private void OnPropertyChanged([CallerMemberName]String caller = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(caller));
}
}
}
(AddressViewModel.cs)
public class AddressViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _id;
private string _street;
private int _number;
public int Id
{
get { return this._id; }
set
{
if (value != this._id)
{
this._id = value;
OnPropertyChanged();
}
}
}
public string Street
{
get { return this._street; }
set
{
if (value != this._street)
{
this._street = value;
OnPropertyChanged();
}
}
}
public int Number
{
get { return this._number; }
set
{
if (value != this._number)
{
this._number = value;
OnPropertyChanged();
}
}
}
private void OnPropertyChanged([CallerMemberName]String caller = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(caller));
}
}
}
And here's a representation of the database model (cannot upload pic < 10 posts)
Address
Id
Street
Number
Person
Id
IdAddress
Name
Age
Address ---> this property concatenates Street and Number of Address entity.
So, as you can see, it's a very simple example. Just a proof-of-concept. Problem is, whenever I try to add a new entity to the database (through Entity Framework 6 and LINQ) I must inevitably add that ViewModel entity to the DataGrid's data context.
This is the code working as of today:
public static Person CreatePerson(PersonViewModel personVM)
{
var person = new Person
{
IdAddress = personVM.IdAddress,
Name = personVM.Name,
Age = personVM.Age
};
try
{
using (var context = new OCDemoContext())
{
context.Database.Connection.Open();
context.Person.Add(person);
context.SaveChanges();
context.Database.Connection.Close();
}
}
catch
{
throw;
}
return person;
}
As you can see, the need here is to show in DataGrid a column that concatenates two properties of Address database entity: Street and Number, and giving that value to Address property of PersonViewModel class to show as a column in the DataGrid.
The code that adds the entity to the DataGrid itemssource collection after the insert to the database:
// Add to the database
PersonsGateway.CreatePerson(personVM);
// Update view on DataGrid's itemssource collection
ViewModel model = this.xGrid.DataContext as ViewModel;
model.Persons.Insert(0, personVM);
XAML as:
<Window x:Class="OCDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" xmlns:local="clr-namespace:OCDemo">
<Window.Resources>
<local:ViewModel x:Key="xViewModel" />
</Window.Resources>
<Grid x:Name="xGrid" DataContext="{StaticResource xViewModel}">
<DataGrid x:Name="xDataGrid" Grid.Row="0" ItemsSource="{Binding Persons}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Name}" Header="Name"/>
<DataGridTextColumn Binding="{Binding Path=Age}" Header="Age"/>
<DataGridTextColumn Binding="{Binding Path=Address}" Header="Address"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
And ViewModel.cs
public class ViewModel
{
public ObservableCollection<PersonViewModel> Persons { get; set; }
public ViewModel()
{
this.Persons = PersonsGateway.RetrievePersons();
}
}
And T4 .tt file updated as MSDN - Updating code generation for data binding explains.
So, it is possible to depend only on adding the entity to the database and not to always add the entity to the itemssource collection after that on a ObservableCollection scenario?
Change your view model to the following
public class ViewModel
{
public ObservableCollection<PersonViewModel> Persons { get; private set; }
public ViewModel()
{
Persons = new ObservableCollection<PersonViewModel>();
Persons.AddRange(PersonsGateway.RetrievePersons().ToList());
}
}
Its important to create the observable collection once and not recreate it, to rely on change notifications, so create it and then use the standard methods to manage it.
Ok, I should of suggested that instead of this code
PersonsGateway.CreatePerson(personVM);
ViewModel model = this.xGrid.DataContext as ViewModel;
model.Persons.Insert(0, personVM);
you should do
PersonsGateway.CreatePerson(personVM);
ViewModel model = this.xGrid.DataContext as ViewModel;
model.Persons.Add(personVM);
Below is my View code. My List is being set and updated at back-end but its not reflected in front-end even though I bonded it with ListItems.
Can you please tell why is this happening? Please tell me if you need other files.
ListOfVehicle.xaml
<Window x:Class="Seris.ListOfVehicle"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="VehicalForm" Height="600" Width="700">
<Grid>
<Label Content="Add Vehicle" HorizontalAlignment="Left" Height="27" Margin="261,8,0,0" VerticalAlignment="Top" Width="85" FontWeight="Bold" FontSize="12"/>
<Label Content="SERIS CAD" HorizontalAlignment="Left" Height="30" Margin="53,8,0,0" VerticalAlignment="Top" Width="84" FontWeight="Bold"/>
<Menu x:Name="ListOfPersonnnel" HorizontalAlignment="Left" Height="32" Margin="10,35,0,0" VerticalAlignment="Top" Width="603">
<MenuItem Header="Manage Vehicle >>" />
</Menu>
<Button Name="Add_Button" CommandParameter="add" Command="{Binding OpenAddWindow_Command}" Content="Add" Height="28" Width="81" Margin="246,396,315,46"/>
<Button Name="Replace_Button" CommandParameter="replace" Command="{Binding RemoveButton_Command}" IsEnabled="{Binding isEnableReplaceButton, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Content="Replace" Height="28" Width="81" Margin="345,396,216,46"/>
<Button Name="Remove_Button" CommandParameter="remove" Command="{Binding ReplaceButton_Command}" IsEnabled="{Binding isEnableReplaceButton, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Content="Remove" Height="28" Width="81" Margin="442,396,119,46"/>
<Label x:Name="Error_Label" Content="{Binding ErrorMessage, UpdateSourceTrigger=PropertyChanged}" Foreground="Red" HorizontalAlignment="Left" Height="30" Width="100" Margin="88,206,0,224"/>
<ListView Name ="Grid" Margin="104,67,185,226" >
<DataGrid Name="DG" ItemsSource="{Binding ListItems, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedRow, Mode=TwoWay}" GridLinesVisibility="None" IsReadOnly="True" AutoGenerateColumns="False" BorderThickness="0">
<DataGrid.Columns>
<DataGridTextColumn Header="Vehical No" Binding="{Binding VehicalNo}"/>
<DataGridTextColumn Header="Model" Binding="{Binding Model}" />
<DataGridTextColumn Header="ManufacturingDate" Binding="{Binding ManufacturingDate}" />
<DataGridTextColumn Header="IUNo" Binding="{Binding IUNo}" />
<DataGridTextColumn Header="Personnel" Binding="{Binding PersonnelNameSelected}" />
<DataGridTextColumn Header="Unique No" Binding="{Binding UniqueNo}"/>
</DataGrid.Columns>
</DataGrid>
</ListView>
</Grid>
VehicleMainViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Seris.Models;
using System.Collections.ObjectModel;
using System.Windows.Input;
using Seris.Commands;
using Seris.ViewModels;
using System.Windows;
using System.Windows.Controls;
using System.Threading;
using System.ComponentModel;
using Seris.Views;
namespace Seris.ViewModels
{
public class VehicleMainViewModel : ObservableObject
{
#region Getters-Setters
// Static Variables...
private static bool _IsEnableReplaceButton;
public static bool IsEnableReplaceButton
{
get { return _IsEnableReplaceButton; }
set { _IsEnableReplaceButton = value; }
}
// Non-Static Variables...
private string _VehicleNo_Error;
public string VehicleNo_Error
{
get { return _VehicleNo_Error; }
set { _VehicleNo_Error = value; OnPropertyChanged("VehicleNo_Error"); }
}
private string _Model_Error;
public string Model_Error
{
get { return _Model_Error; }
set { _Model_Error = value; OnPropertyChanged("Model_Error"); }
}
private string _ManufacturingDate_Error;
public string ManufacturingDate_Error
{
get { return _ManufacturingDate_Error; }
set { _ManufacturingDate_Error = value; OnPropertyChanged("ManufacturingDate_Error"); }
}
private string _IUNo_Error;
public string IUNo_Error
{
get { return _IUNo_Error; }
set { _IUNo_Error = value; OnPropertyChanged("IUNo_Error"); }
}
private string _Personnel_Error;
public string Personnel_Error
{
get { return _Personnel_Error; }
set { _Personnel_Error = value; OnPropertyChanged("Personnel_Error"); }
}
private string _ErroMesage;
public string ErrorMessage
{
get { return _ErroMesage; }
set
{
_ErroMesage = value; OnPropertyChanged("ErrorMessage");
if (ErrorMessage.Trim() == "" || ErrorMessage == null)
HelpVisibility = "hidden";
else
HelpVisibility = "visible";
}
}
private string _HelpVisibility;
public string HelpVisibility
{
get { return _HelpVisibility; }
set { _HelpVisibility = value; OnPropertyChanged("HelpVisibility"); }
}
private static AddVehicle _addVehicle;
public static AddVehicle addVehicle
{
get
{
return _addVehicle;
}
set
{
_addVehicle = value;
}
}
// Form Components
private Guid? _UniqueNo;
public Guid? UniqueNo
{
get { return _UniqueNo; }
set
{
if (value == null || (!value.Equals(_UniqueNo)))
{
_UniqueNo = value;
OnPropertyChanged("UniqueNo");
}
}
}
private string _VehicleNo;
public string VehicleNo
{
get { return _VehicleNo; }
set
{
if (value == null || (!value.Equals(_VehicleNo)))
{
_VehicleNo = value;
EditText = _VehicleNo;
OnPropertyChanged("VehicleNo");
validateSpecificData(1);
}
}
}
private string _Model;
public string Model
{
get { return _Model; }
set
{
if (value == null || (!value.Equals(_Model)))
{
_Model = value;
EditText = _Model;
OnPropertyChanged("Model");
validateSpecificData(2);
}
}
}
private DateTime? _ManufacturingDate;
public DateTime? ManufacturingDate
{
get { return _ManufacturingDate; }
set
{
if (value == null || (!value.Equals(_ManufacturingDate)))
{
_ManufacturingDate = value;
EditText = _ManufacturingDate.ToString();
OnPropertyChanged("ManufacturingDate");
validateSpecificData(3);
}
}
}
private string _IUNo;
public string IUNo
{
get { return _IUNo; }
set
{
if (value == null || (!value.Equals(_IUNo)))
{
_IUNo = value;
EditText = _IUNo;
OnPropertyChanged("IUNo");
validateSpecificData(4);
}
}
}
private string _PersonnelNameSelected;
public string PersonnelNameSelected
{
get { return _PersonnelNameSelected; }
set
{
if (value != _PersonnelNameSelected)
{
_PersonnelNameSelected = value;
EditText = _PersonnelNameSelected;
OnPropertyChanged("PersonnelNameSelected");
}
}
}
private ObservableCollection<string> _PersonnelName;
public ObservableCollection<string> PersonnelName
{
get { return _PersonnelName; }
set
{
if (value != _PersonnelName)
{
_PersonnelName = value;
EditText = _VehicleNo;
OnPropertyChanged("PersonnelName");
}
}
}
private ObservableCollection<VehicleModel> _listItems;
public ObservableCollection<VehicleModel> ListItems
{
get { return _listItems; }
set
{
if (value == null || (!value.Equals(_listItems)))
{
_listItems = value;
}
}
}
// Other Variables
private string _EditText;
public string EditText
{
get { return _EditText; }
set
{
if (value != _EditText)
{
_EditText = value;
OnPropertyChanged("EditText");
}
}
}
private VehicleModel _SelectedRow;
public VehicleModel SelectedRow
{
get { return _SelectedRow; }
set
{
if (value != _SelectedRow)
{
_SelectedRow = value;
OnPropertyChanged("SelectedRow");
if (SelectedRow != null)
{
UniqueNo = _SelectedRow.UniqueNo;
VehicleNo = _SelectedRow.VehicleNo;
Model = _SelectedRow.Model;
ManufacturingDate = _SelectedRow.ManufacturingDate;
IUNo = _SelectedRow.IUNo;
PersonnelNameSelected = _SelectedRow.PersonnelNameSelected;
_IsEnableReplaceButton = true;
}
}
}
}
private int _Progress;
public int Progress
{
get { return _Progress; }
set { _Progress = value; OnPropertyChanged("Progress"); }
}
#endregion
// Command Variables
private ICommand _saveButton_Command;
public ICommand SaveButton_Command
{
get { return _saveButton_Command; }
set { _saveButton_Command = value; }
}
private ICommand _ReplaceButton_Command;
public ICommand ReplaceButton_Command
{
get { return _ReplaceButton_Command; }
set { _ReplaceButton_Command = value; }
}
private ICommand _RemoveButton_Command;
public ICommand RemoveButton_Command
{
get { return _RemoveButton_Command; }
set { _RemoveButton_Command = value; }
}
private ICommand _OpenAddWindow_Command;
public ICommand OpenAddWindow_Command
{
get { return _OpenAddWindow_Command; }
set { _OpenAddWindow_Command = value; }
}
#region Methods
//Static Methods...
public static void showMessage(string message)
{
MessageBox.Show(message);
}
//Non-Static Methods...
public void SaveToList(object o1)
{
try
{
// Setting Flags
ErrorMessage = "";
// To Verify Validations
validateAllData();
// ProgressBar
//Progress = 0;
//ProgressBar();
// Adding a Record
ListItems.Add(new VehicleModel(VehicleNo, Model, ManufacturingDate, IUNo, PersonnelNameSelected));
// Setting Flags etc.
IsEnableReplaceButton = false;
CloseAdd();
// Clearing Form
UniqueNo = null;
VehicleNo = null;
Model = null;
ManufacturingDate = null;
IUNo = null;
PersonnelNameSelected = null;
}
catch (Exception ex)
{
Progress = 0;
ErrorMessage = ex.Message;
}
}
public void ReplaceToList(object o1)
{
try
{
VehicleModel vm = ListItems.First(x => x.UniqueNo == UniqueNo);
int indexToRemove = ListItems.IndexOf(vm);
// Setting Flags
ErrorMessage = "";
// To Verify Validations
validateAllData();
// ProgressBar
Progress = 0;
ProgressBar();
// Replacing Record
ListItems.Insert(indexToRemove + 1, vm);
ListItems.RemoveAt(indexToRemove);
// Clearing Form
VehicleNo = null;
Model = null;
ManufacturingDate = null;
IUNo = null;
PersonnelNameSelected = null;
// Setting Flags etc.
ErrorMessage = "";
IsEnableReplaceButton = false;
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
}
}
public void RemoveList(object o1)
{
VehicleModel vm = ListItems.First(x => x.UniqueNo == UniqueNo);
int indexToRemove = ListItems.IndexOf(vm);
ErrorMessage = "";
try
{
// Setting Flags
ErrorMessage = "";
Progress = 0;
// Removing Selected Record
ListItems.RemoveAt(indexToRemove);
// Clearing Form
VehicleNo = null;
Model = null;
ManufacturingDate = null;
IUNo = null;
PersonnelNameSelected = null;
// Setting Flags etc.
ErrorMessage = "";
IsEnableReplaceButton = false;
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
}
}
void ProgressBar()
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
(sender as BackgroundWorker).ReportProgress(i);
Thread.Sleep(5);
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Progress = e.ProgressPercentage;
}
public void validateAllData()
{
VehicleModel tempObject = new VehicleModel(VehicleNo, Model, ManufacturingDate, IUNo, PersonnelNameSelected);
}
public void validateSpecificData(int ErrorCode)
{
VehicleModel tempObject = new VehicleModel();
switch(ErrorCode)
{
case 1: tempObject.VehicleNo=VehicleNo;break;
case 2: tempObject.Model=Model;break;
case 3: tempObject.ManufacturingDate=ManufacturingDate;break;
case 4: tempObject.IUNo=IUNo;break;
}
tempObject.ValidateSpecificData(this, ErrorCode);
}
public void OpenAdd(object o1)
{
if(addVehicle==null)
{
addVehicle = new AddVehicle();
}
addVehicle.Show();
}
public void CloseAdd()
{
if (addVehicle != null)
{
addVehicle.Close();
addVehicle = null;
}
}
#endregion
#region Constructors
public VehicleMainViewModel()
{
// Initialization
//VehicleModel vm = new VehicleModel();
ListItems = new ObservableCollection<VehicleModel>();
PersonnelName = ListOfPersonnelViewModel.PersonNameList_Updating;
//PersonnelName = new ObservableCollection<string>() { "ABC", "DEF", "GHI" };
// Setting Flags
ErrorMessage = "";
IsEnableReplaceButton = false;
// Commands Initialization
SaveButton_Command = new RelayCommand(new Action<object>(SaveToList));
ReplaceButton_Command = new RelayCommand(new Action<object>(ReplaceToList));
RemoveButton_Command = new RelayCommand(new Action<object>(RemoveList));
OpenAddWindow_Command = new RelayCommand(new Action<object>(OpenAdd));
}
#endregion
}
}
Probably you may have not created Singleton instance so possibility of creating multiple instance for each window. Below is an example for your ViewModel Singleton.
private static VehicleMainViewModel _Instance;
public static VehicleMainViewModel getInstance
{
get
{
if(_Instance==null)
{
_Instance = new VehicleMainViewModel();
}
return _Instance;
}
}