listbox selected items binding update from source - c#

I have a customlistbox whose itemssource is bound to an observablecollection in the viewmodel. I have created a SelectedItemsList DependencyProperty in the customListbox so that the user can select items and the viewmodel will updated. This works perfectly.
I would also like the bound list in the viewmodel, when changed, to update the selected items in the customListbox.
static FrameworkPropertyMetadata fpm = new FrameworkPropertyMetadata(
new ObservableCollection<MyItem>(),
(FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault),
new PropertyChangedCallback(OnSelectedItemsChanged)
);
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//the code
}
public static readonly DependencyProperty SelectedItemsListProperty =
DependencyProperty.Register("SelectedItemsList",
typeof(ObservableCollection<MyItem>),
typeof(CustomListBox), fpm);
SelectedItems is read only. Is there anyway to update the selected items from the viewModel? Is there an alternative to ListBox that would be more suitable?

I figured that I would post my solution just in case it helps anyone.
Here is my very simple Item class
class MyItem
{
public string MyString { get; set; }
public MyItem(string m)
{
MyString = m;
}
}
Here is my CustomListBox Code
class CustomListBox : ListBox
{
public CustomListBox()
{
this.SelectionChanged += CustomListBox_SelectionChanged;
}
void CustomListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ObservableCollection<MyItem> tempList = new ObservableCollection<MyItem>();
foreach (MyItem i in this.SelectedItems)
{
tempList.Add(i);
}
this.SelectedItemsList = tempList;
}
#region SelectedItemsList
public ObservableCollection<MyItem> SelectedItemsList
{
get { return (ObservableCollection<MyItem>)GetValue(SelectedItemsListProperty); }
set { SetValue(SelectedItemsListProperty, value); }
}
public static readonly DependencyProperty SelectedItemsListProperty =
DependencyProperty.Register("SelectedItemsList", typeof(ObservableCollection<MyItem>), typeof(CustomListBox),
new PropertyMetadata(new ObservableCollection<MyItem>(), new PropertyChangedCallback(OnSelectionChanged)));
public static void OnSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CustomListBox clb = d as CustomListBox;
var selectedItems = e.NewValue as ObservableCollection<MyItem>;
if (selectedItems != null)
{
clb.SetSelectedItems(selectedItems);
}
}
#endregion
}
The XAML Binding in my window
<local:CustomListBox Height="500" Width="200" x:Name="listview" Margin="0,40,0,0" ItemsSource="{Binding MyItemsList}"
Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" TabIndex="50"
SelectionMode="Multiple" SelectedItemsList="{Binding SelectedMyItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=MyString}" />
</DataTemplate>
</ListBox.ItemTemplate>
</local:CustomListBox>
And my ViewModel
class MyViewModel : ViewModelBase
{
public MyViewModel()
{
this.SelectChangeMyItemsCommand = new BaseCommand(new Action(SelectChangeMyItems));
}
private ObservableCollection<MyItem> selectedMyItems = null;
public ObservableCollection<MyItem> SelectedMyItems
{
get
{
if (selectedMyItems == null)
{
selectedMyItems = new ObservableCollection<MyItem>();
}
return selectedMyItems;
}
set
{
selectedMyItems = value;
OnPropertyChanged("SelectedMyItems");
}
}
private ObservableCollection<MyItem> myItemsList = null;
public ObservableCollection<MyItem> MyItemsList
{
get
{
if (myItemsList == null)
{
MyItemsRefresh();
}
return myItemsList;
}
set
{
myItemsList = value;
OnPropertyChanged("MyItemsList");
}
}
public void MyItemsRefresh()
{
ObservableCollection<MyItem> tempMyList = new ObservableCollection<MyItem>();
tempMyList.Add(new MyItem("Angry Apple"));
tempMyList.Add(new MyItem("Big Bird"));
tempMyList.Add(new MyItem("Candy Cane"));
tempMyList.Add(new MyItem("Daring Dart"));
MyItemsList = tempMyList;
}
private static bool iseven = true;
public ICommand SelectChangeMyItemsCommand { get; private set; }
public void SelectChangeMyItems()
{
ObservableCollection<MyItem> items = new ObservableCollection<MyItem>();
for(int i = 0; i < myItemsList.Count; i++)
{
if (iseven && IsEven(i))
{
items.Add(MyItemsList[i]);
}
else if (!iseven && !IsEven(i))
{
items.Add(MyItemsList[i]);
}
}
this.SelectedMyItems = items;
iseven = !iseven;
}
public static bool IsEven(int value)
{
return value % 2 == 0;
}
}

Related

Can I bind to Background color each DataGridRow?

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>

WPF: Problems with DataContext and ViewModel

I'm having a problem getting the content to display in my program, and I suspect I messed up something relating to the DataContext. The controls I am using come from an extension called Syncfusion (to display graphs) but it could be any other control displaying these items.
MainWindow.xaml.cs:
public MainWindow()
{
InitializeComponent();
ViewModel _viewModel = new ViewModel();
DataContext = _viewModel;
}
ViewModel.cs
public class ViewModel
{
public ObservableCollection<TotalData> TotalDataColl { get; set; }
public ViewModel()
{
TotalDataColl = new ObservableCollection<TotalData>();
var vm = new ChartViewModel
{
Series = new ObservableCollection<SeriesViewModel>
{
new SeriesViewModel{type="Lemons", Items = new ObservableCollection<ItemViewModel>{new ItemViewModel{source = "January", value = 25}, new ItemViewModel{source = "February", value = 35}}},
new SeriesViewModel{type="Oranges",Items = new ObservableCollection<ItemViewModel>{new ItemViewModel{source = "January", value = 22}, new ItemViewModel{source = "February", value = 36}}}
}
};
}
}
MainWindow.xaml
<Grid>
<local:ExtendedChart ItemsSource="{Binding Series}" Margin="0,-38,0,97">
<local:ExtendedChart.ItemTemplate>
<DataTemplate>
<chart:ColumnSeries ItemsSource="{Binding Items}" DependentValuePath="value" IndependentValuePath="source" Title="{Binding type}" />
</DataTemplate>
</local:ExtendedChart.ItemTemplate>
</local:ExtendedChart>
</Grid>
DataClass.cs
public class ChartViewModel : ObservableObject
{
private ObservableCollection<SeriesViewModel> _series;
public ObservableCollection<SeriesViewModel> Series
{
get
{
return _series;
}
set
{
_series = value;
OnPropertyChanged("Series");
}
}
}
public class SeriesViewModel : ObservableObject
{
private ObservableCollection<ItemViewModel> items;
private string _type;
public string type { get { return _type; } set { _type = value; OnPropertyChanged("_type"); } }
public ObservableCollection<ItemViewModel> Items { get { return items; } set { items = value; OnPropertyChanged("Items"); } }
}
public class ItemViewModel : ObservableObject
{
private string _source;
private double _value;
public string Source { get { return _source;} set { _source = value; OnPropertyChanged("Source"); } }
public double Value { get { return _value; } set { _value = value;OnPropertyChanged("Value"); } }
}
ViewModel
public class ViewModel
{
public ObservableCollection<TotalData> TotalDataColl { get; set; }
public ChartViewModel ChartSeriesViewModel { get; set; }
public ViewModel()
{
TotalDataColl = new ObservableCollection<TotalData>();
ChartSeriesViewModel = new ChartViewModel
{
Series = new ObservableCollection<SeriesViewModel>
{
new SeriesViewModel{type="Lemons", Items = new ObservableCollection<ItemViewModel>{new ItemViewModel{source = "January", value = 25}, new ItemViewModel{source = "February", value = 35}}},
new SeriesViewModel{type="Oranges",Items = new ObservableCollection<ItemViewModel>{new ItemViewModel{source = "January", value = 22}, new ItemViewModel{source = "February", value = 36}}}
}
};
}
}
MainWindow.Xaml
<Grid>
<local:ExtendedChart ItemsSource="{Binding ChartSeriesViewModel.Series}" Margin="0,-38,0,97">
<local:ExtendedChart.ItemTemplate>
<DataTemplate>
<chart:ColumnSeries ItemsSource="{Binding Items}" DependentValuePath="value" IndependentValuePath="source" Title="{Binding type}" />
</DataTemplate>
</local:ExtendedChart.ItemTemplate>
</local:ExtendedChart>
</Grid>

Observable collection async update

Many people advice to update observablecollection on UI thread using dispatcher. But I want to make something like this. Whit what evil can i face using this implementation? I do not want to use dispatcher because it can make deadlocks in multithreading.
namespace WpfApplication162
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new Data();
}
}
public class Data:INotifyPropertyChanged
{
private ObservableCollection<string> nameList;
public ObservableCollection<string> NameList
{
get
{
return this.nameList;
}
set
{
this.nameList = value;
this.RaisePropertyChaged("NameList");
}
}
public ObservableCollection<string> TempList { get; set; }
public Data()
{
NameList = new ObservableCollection<string>();
NameList.Add("Loading");
Action Start = new Action(UpdateAysnc);
IAsyncResult result = Start.BeginInvoke(new AsyncCallback(SetList), null);
}
public void SetList(object param)
{
NameList = TempList;
}
public void UpdateAysnc()
{
TempList = new ObservableCollection<string>();
System.Threading.Thread.Sleep(10000);
for (int i=0;i<10;i++)
{
TempList.Add(i.ToString());
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChaged(string info)
{
if (PropertyChanged!= null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
Xaml
<Grid>
<ListView ItemsSource="{Binding NameList}">
<ListView.ItemTemplate>
<DataTemplate>
<Label Content="{Binding .}"></Label>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Try this:
public Data()
{
NameList = new ObservableCollection<string>();
NameList.Add("Loading");
UpdateAysnc(results => SetList(results));
}
public void SetList(object param)
{
NameList = TempList;
}
public void UpdateAysnc(Action<ObservableCollection<string>> onUpdateComplete)
{
var tempList = new ObservableCollection<string>();
System.Threading.Thread.Sleep(10000);
for (int i=0;i<10;i++)
{
tempList.Add(i.ToString());
}
onUpdateComplete(tempList);
}

Strange Behavior of CollectionView WPF

I've faced a really strange behavior while using CollectionViews and wonder if somebody can explain me that behavior.
I've got an ObservableCollection, lets call it _sourcelist. Then I create several CollectionViews of _sourcelist, in that way:
CollectionViewSource cvs = new CollectionViewSource() { Source = _sourcelist };
ICollectionView list = cvs.View;
These CollectionViews are each used with a different filter and one CollectionView with no filter (so representing the original source). In my Application I have a TabControl where every TabItem contains a ListBox with one of these CollectionViews as ItemsSource. When I know delete an item from the sourcelist, the ListBox with the CollectionView with no filter gets updated, but the other ListBoxes that contains filtered CollectionViews and contain the deleted item don't get updated. So the deleted item remains in that ListBoxes.
Now, the strange behavior is following:
When I add the following code after code written above:
list.CollectionChanged += (o,e) => { };
Then the other ListBoxes get updated, too.
Why that? I mean, I just registered an event handler for the CollectionChanged event, which does nothing!
So, here is my Code Sample. I know it is a big amount of code, but it is ready to run and reproduces the strange behavior.
When you run the code and try to delete an item in the "All" List, then the corresponding item in the filtered "Under 30" or "Over 30" list will not be deleted. Now uncomment the commented line in the ListViewModel (it is just a registration of an empty Eventhandler), then the corresponding items in the filtered lists will be deleted.
If you have any questions, then please ask! :-)
Thank you for your interest in solving the problem. :-)
P.S.: The RelayCommand is from the assembly: Microsoft.TeamFoundation.Controls
MainWindow.xaml:
<Window x:Class="CollectionTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CollectionTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ListViewModel}">
<ListBox ItemsSource="{Binding List}" SelectedItem="{Binding Selected}" BorderBrush="Transparent" Margin="0" HorizontalContentAlignment="Stretch"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:PersonViewModel}">
<TextBlock Margin="5,0" VerticalAlignment="Center" Text="{Binding Name}"/>
</DataTemplate>
<Style x:Key="{x:Type TabItem}" TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Name}"/>
</Style>
</Window.Resources>
<DockPanel>
<Button DockPanel.Dock="Bottom" Name="DeleteButton" Content="Delete" Command="{Binding DeleteCommand}"/>
<TabControl Name="TabControl" ItemsSource="{Binding Lists}" Padding="0" SelectedItem="{Binding Selected}"/>
</DockPanel>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private MainViewModel mvm;
public MainWindow()
{
InitializeComponent();
mvm = new MainViewModel(CreateRepo());
this.DataContext = mvm;
}
private Repository CreateRepo()
{
Repository repo = new Repository();
for (int i = 20; i < 45; i++)
{
Person p = new Person() { Name = "Person" + i, Age = i };
repo.Add(p);
}
return repo;
}
}
Person:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Repository:
public class Repository
{
private readonly List<Person> _list;
public event EventHandler<RepositoryChangedEventArgs> Added;
public event EventHandler<RepositoryChangedEventArgs> Removed;
public Repository()
{
this._list = new List<Person>();
}
public void Add(Person person)
{
if (person == null)
{
throw new ArgumentNullException("person");
}
if (!this._list.Contains(person))
{
this._list.Add(person);
if (this.Added != null)
{
this.Added(this, new RepositoryChangedEventArgs(person));
}
}
}
public void Remove(Person person)
{
if (person == null)
{
throw new ArgumentNullException("person");
}
if (this._list.Contains(person))
{
this._list.Remove(person);
if (this.Removed != null)
{
this.Removed(this, new RepositoryChangedEventArgs(person));
}
}
}
public List<Person> Get()
{
return new List<Person>(this._list);
}
}
public class RepositoryChangedEventArgs : EventArgs
{
public Person Entity { get; set; }
public RepositoryChangedEventArgs(Person entity)
{
this.Entity = entity;
}
}
PersonViewModel:
public class PersonViewModel : INotifyPropertyChanged
{
private Person _person;
private Repository _repository;
public string Name
{
get
{
return this._person.Name;
}
set
{
if (this._person.Name != value)
{
this._person.Name = value;
this.OnPropertyChanged("Name");
}
}
}
public int Age
{
get
{
return this._person.Age;
}
set
{
if (this._person.Age != value)
{
this._person.Age = value;
this.OnPropertyChanged("Age");
}
}
}
public PersonViewModel(Person person, Repository repository)
{
this._person = person;
this._repository = repository;
}
public void Delete()
{
this._repository.Remove(this._person);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
ListViewModel:
public class ListViewModel : INotifyPropertyChanged
{
private IEnumerable _sourceList;
private ICollectionView _list;
private PersonViewModel _selected;
private string _name;
public ICollectionView List
{
get
{
return this._list;
}
}
public PersonViewModel Selected
{
get
{
return this._selected;
}
set
{
if (this._selected != value)
{
this._selected = value;
this.OnPropertyChanged("Selected");
}
}
}
public string Name
{
get
{
return this._name;
}
set
{
if (this._name != value)
{
this._name = value;
this.OnPropertyChanged("Name");
}
}
}
public ListViewModel(string name, IEnumerable list)
{
this.Name = name;
this._sourceList = list;
CollectionViewSource cvs = new CollectionViewSource() { Source = list };
this._list = cvs.View;
// cvs.View.CollectionChanged += (o, e) => { }; // uncomment this line to let it work
}
public ListViewModel(String name, IEnumerable list, Predicate<object> filter)
: this(name, list)
{
this._list.Filter = filter;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
MainViewModel:
public class MainViewModel : INotifyPropertyChanged
{
protected ObservableCollection<PersonViewModel> _sourceList;
protected ObservableCollection<ListViewModel> _lists;
protected Repository _repository;
protected Dictionary<Person, PersonViewModel> _dictionary;
private RelayCommand _deleteCommand;
private ListViewModel _selected;
public ListViewModel Selected
{
get
{
return this._selected;
}
set
{
if (this._selected != value)
{
this._selected = value;
this.OnPropertyChanged("Selected");
}
}
}
public RelayCommand DeleteCommand
{
get
{
if (this._deleteCommand == null)
{
this._deleteCommand = new RelayCommand(this.ExecutedDelete, this.CanExecuteDelete);
}
return this._deleteCommand;
}
}
public IEnumerable<ListViewModel> Lists
{
get
{
return this._lists;
}
}
public MainViewModel(Repository repository)
{
this._sourceList = new ObservableCollection<PersonViewModel>();
this._lists = new ObservableCollection<ListViewModel>();
this._dictionary = new Dictionary<Person, PersonViewModel>();
this._repository = repository;
this._repository.Added += this.OnAdded;
this._repository.Removed += this.OnRemoved;
this.CreateSourceList(repository);
this.CreateLists();
if (this._lists.Count > 0)
{
this.Selected = this._lists[0];
}
}
protected void CreateSourceList(Repository repository)
{
foreach (Person person in repository.Get())
{
PersonViewModel pvm = new PersonViewModel(person, repository);
this._dictionary.Add(person, pvm);
this._sourceList.Add(pvm);
}
}
protected void CreateLists()
{
this._lists.Add(new ListViewModel("All", this._sourceList));
this._lists.Add(new ListViewModel("Under 30", this._sourceList,
delegate(object o)
{
PersonViewModel pvm = o as PersonViewModel;
return (pvm != null && pvm.Age < 30);
}));
this._lists.Add(new ListViewModel("Over 30", this._sourceList,
delegate(object o)
{
PersonViewModel pvm = o as PersonViewModel;
return (pvm != null && pvm.Age > 30);
}));
}
protected void Remove(Person person)
{
PersonViewModel pvm = this._dictionary[person];
this._dictionary.Remove(person);
this._sourceList.Remove(pvm);
}
protected void OnAdded(object sender, RepositoryChangedEventArgs args)
{
Person addedPerson = args.Entity;
if (addedPerson != null)
{
PersonViewModel pvm = new PersonViewModel(addedPerson, this._repository);
this._dictionary.Add(addedPerson, pvm);
this._sourceList.Add(pvm);
}
}
protected void OnRemoved(object sender, RepositoryChangedEventArgs args)
{
Person removedPerson = args.Entity;
if (removedPerson != null)
{
this.Remove(removedPerson);
}
}
private bool CanExecuteDelete(object o)
{
return true;
}
private void ExecutedDelete(object o)
{
PersonViewModel pvm = null;
if (this.Selected != null && (pvm = this.Selected.Selected) != null)
{
pvm.Delete();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}

WPF datagrid binding with custom columns

Currently I am working on WPF application (with MVVM) in which I am displaying data in the DataGridView.
<DataGrid RowHeaderWidth="0" ItemsSource="{Binding PartsList,UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Item Name" IsReadOnly="True" Width="*" Binding="{Binding ProductName}"></DataGridTextColumn>
<DataGridTextColumn Header="Model Name" IsReadOnly="True" Width="*" Binding="{Binding CarModelName}"></DataGridTextColumn>
<DataGridTextColumn Header="Company Name" IsReadOnly="True" Width="*" Binding="{Binding CompanName}"></DataGridTextColumn>
<DataGridTextColumn Header="Price" IsReadOnly="True" Width="*" Binding="{Binding Rate}">
</DataGrid.Columns>
</DataGrid>
Here PartsList is an ObservableCollection of entity Part.
Now I want to add custom column to the DataGrid which shows discount and another column which shows net amount. How can I do this?
Please give a good idea to do this as I need to work with thousands of records so performance is very important for me.
Thank you in advance.
Try to add columns on the Loaded event of DataGrid:
private void DataGrid_Loaded_1(object sender, RoutedEventArgs e)
{
dataGrid.Columns.Add((DataGridTextColumn)this.Resources["DiscountColumn"]);
dataGrid.Columns.Add((DataGridTextColumn)this.Resources["NetAmountColumn"]);
//Alternatively you can create columns in .cs like
dataGrid.Columns.Add(new DataGridTextColumn() { Header = "Dicount", Binding = new Binding("Discount") });
dataGrid.Columns.Add(new DataGridTextColumn() { Header = "Net Amount", Binding = new Binding("NetAmount") });
}
<Window.Resources>
<DataGridTextColumn x:Key="DiscountColumn" Header="Discount" IsReadOnly="True" Width="*" Binding="{Binding Discount}"/>
<DataGridTextColumn x:Key="NetAmountColumn" Header="Net Amount" IsReadOnly="True" Width="*" Binding="{Binding NetAmount}"/>
</Window.Resources>
<DataGrid RowHeaderWidth="0" x:Name="dataGrid" Loaded="DataGrid_Loaded_1" />
This is an old post but I have done something similar using MVVM and WPF so thought I would through my two pennies worth in.
I cannot give any real indication of how it will perform however we haven't seen any real issues with displaying around a thousand objects in the ItemSource.
Apologies it is going to be a lengthy display of code but I will try to break it down so it is easy to follow.
Ultimately what you need to do is create an Attached Behavior.
Here is mine:
Behavior class
This does the core work in creating the actual columns and add them to your DataGrid based on the ColumnsSource that you bind to it.
public class DataGridColumnCollectionBehavior
{
private object columnsSource;
private DataGrid dataGrid;
public DataGridColumnCollectionBehavior(DataGrid dataGrid)
{
this.dataGrid = dataGrid;
}
public object ColumnsSource
{
get { return this.columnsSource; }
set
{
object oldValue = this.columnsSource;
this.columnsSource = value;
this.ColumnsSourceChanged(oldValue, this.columnsSource);
}
}
public string DisplayMemberFormatMember { get; set; }
public string DisplayMemberMember { get; set; }
public string FontWeightBindingMember { get; set; }
public string FontWeightMember { get; set; }
public string HeaderTextMember { get; set; }
public string IsEditableMember { get; set; }
public string SortMember { get; set; }
public string TextAlignmentMember { get; set; }
public string TextColourMember { get; set; }
public string WidthMember { get; set; }
private void AddHandlers(ICollectionView collectionView)
{
collectionView.CollectionChanged += this.ColumnsSource_CollectionChanged;
}
private void ColumnsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ICollectionView view = sender as ICollectionView;
if (this.dataGrid == null)
{
return;
}
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
for (int i = 0; i < e.NewItems.Count; i++)
{
DataGridColumn column = CreateColumn(e.NewItems[i]);
dataGrid.Columns.Insert(e.NewStartingIndex + i, column);
}
break;
case NotifyCollectionChangedAction.Move:
List<DataGridColumn> columns = new List<DataGridColumn>();
for (int i = 0; i < e.OldItems.Count; i++)
{
DataGridColumn column = dataGrid.Columns[e.OldStartingIndex + i];
columns.Add(column);
}
for (int i = 0; i < e.NewItems.Count; i++)
{
DataGridColumn column = columns[i];
dataGrid.Columns.Insert(e.NewStartingIndex + i, column);
}
break;
case NotifyCollectionChangedAction.Remove:
for (int i = 0; i < e.OldItems.Count; i++)
{
dataGrid.Columns.RemoveAt(e.OldStartingIndex);
}
break;
case NotifyCollectionChangedAction.Replace:
for (int i = 0; i < e.NewItems.Count; i++)
{
DataGridColumn column = CreateColumn(e.NewItems[i]);
dataGrid.Columns[e.NewStartingIndex + i] = column;
}
break;
case NotifyCollectionChangedAction.Reset:
dataGrid.Columns.Clear();
CreateColumns(sender as ICollectionView);
break;
default:
break;
}
}
private void ColumnsSourceChanged(object oldValue, object newValue)
{
if (this.dataGrid != null)
{
dataGrid.Columns.Clear();
if (oldValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);
if (view != null)
{
this.RemoveHandlers(view);
}
}
if (newValue != null)
{
ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
if (view != null)
{
this.AddHandlers(view);
this.CreateColumns(view);
}
}
}
}
private DataGridColumn CreateColumn(object columnSource)
{
DataGridColumn column = new DataGridTemplateColumn();
var textBlockFactory = new FrameworkElementFactory(typeof(TextBlock));
((DataGridTemplateColumn)column).CellTemplate = new DataTemplate { VisualTree = textBlockFactory };
textBlockFactory.SetValue(TextBlock.MarginProperty, new Thickness(3));
if (!string.IsNullOrWhiteSpace(this.FontWeightBindingMember))
{
string propertyName = GetPropertyValue(columnSource, this.FontWeightBindingMember) as string;
textBlockFactory.SetBinding(TextBlock.FontWeightProperty, new Binding(propertyName));
}
else if (!string.IsNullOrWhiteSpace(this.FontWeightMember))
{
textBlockFactory.SetValue(TextBlock.FontWeightProperty, (FontWeight)GetPropertyValue(columnSource, this.FontWeightMember));
}
if (!string.IsNullOrWhiteSpace(this.SortMember))
{
column.SortMemberPath = GetPropertyValue(columnSource, this.SortMember) as string;
}
if (!string.IsNullOrEmpty(this.DisplayMemberMember))
{
string propertyName = GetPropertyValue(columnSource, this.DisplayMemberMember) as string;
string format = null;
if (!string.IsNullOrEmpty(this.DisplayMemberFormatMember))
{
format = GetPropertyValue(columnSource, this.DisplayMemberFormatMember) as string;
}
if (string.IsNullOrEmpty(format))
{
format = "{0}";
}
textBlockFactory.SetBinding(TextBlock.TextProperty, new Binding(propertyName) { StringFormat = format });
// If there is no sort member defined default to the display member.
if (string.IsNullOrWhiteSpace(column.SortMemberPath))
{
column.SortMemberPath = propertyName;
}
}
if (!string.IsNullOrWhiteSpace(this.TextAlignmentMember))
{
textBlockFactory.SetValue(TextBlock.TextAlignmentProperty, GetPropertyValue(columnSource, this.TextAlignmentMember));
}
if (!string.IsNullOrEmpty(this.HeaderTextMember))
{
column.Header = GetPropertyValue(columnSource, this.HeaderTextMember);
}
if (!string.IsNullOrWhiteSpace(this.TextColourMember))
{
string propertyName = GetPropertyValue(columnSource, this.TextColourMember) as string;
textBlockFactory.SetBinding(TextBlock.ForegroundProperty, new Binding(propertyName));
}
if (!string.IsNullOrEmpty(this.WidthMember))
{
double width = (double)GetPropertyValue(columnSource, this.WidthMember);
column.Width = width;
}
return column;
}
private void CreateColumns(ICollectionView collectionView)
{
foreach (object item in collectionView)
{
DataGridColumn column = this.CreateColumn(item);
this.dataGrid.Columns.Add(column);
}
}
private object GetPropertyValue(object obj, string propertyName)
{
object returnVal = null;
if (obj != null)
{
PropertyInfo prop = obj.GetType().GetProperty(propertyName);
if (prop != null)
{
returnVal = prop.GetValue(obj, null);
}
}
return returnVal;
}
private void RemoveHandlers(ICollectionView collectionView)
{
collectionView.CollectionChanged -= this.ColumnsSource_CollectionChanged;
}
}
Accessor class
This is the class that you use within your XAML files in order to create the Bindings.
public static class DataGridColumnCollection
{
public static readonly DependencyProperty ColumnCollectionBehaviourProperty =
DependencyProperty.RegisterAttached("ColumnCollectionBehaviour", typeof(DataGridColumnCollectionBehaviour), typeof(DataGridColumnCollection), new UIPropertyMetadata(null));
public static readonly DependencyProperty ColumnsSourceProperty =
DependencyProperty.RegisterAttached("ColumnsSource", typeof(object), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.ColumnsSourcePropertyChanged));
public static readonly DependencyProperty DisplayMemberFormatMemberProperty =
DependencyProperty.RegisterAttached("DisplayMemberFormatMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.DisplayMemberFormatMemberChanged));
public static readonly DependencyProperty DisplayMemberMemberProperty =
DependencyProperty.RegisterAttached("DisplayMemberMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.DisplayMemberMemberChanged));
public static readonly DependencyProperty FontWeightBindingMemberProperty =
DependencyProperty.RegisterAttached("FontWeightBindingMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.FontWeightBindingMemberChanged));
public static readonly DependencyProperty FontWeightMemberProperty =
DependencyProperty.RegisterAttached("FontWeightMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.FontWeightMemberChanged));
public static readonly DependencyProperty IsEditableMemberProperty =
DependencyProperty.RegisterAttached("IsEditableMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.IsEditableMemberChanged));
public static readonly DependencyProperty HeaderTextMemberProperty =
DependencyProperty.RegisterAttached("HeaderTextMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.HeaderTextMemberChanged));
public static readonly DependencyProperty SortMemberProperty =
DependencyProperty.RegisterAttached("SortMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.SortMemberChanged));
public static readonly DependencyProperty TextAlignmentMemberProperty =
DependencyProperty.RegisterAttached("TextAlignmentMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.TextAlignmentMemberChanged));
public static readonly DependencyProperty TextColourMemberProperty =
DependencyProperty.RegisterAttached("TextColourMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.TextColourMemberChanged));
public static readonly DependencyProperty WidthMemberProperty =
DependencyProperty.RegisterAttached("WidthMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.WidthMemberChanged));
public static DataGridColumnCollectionBehaviour GetColumnCollectionBehaviour(DependencyObject obj)
{
return (DataGridColumnCollectionBehaviour)obj.GetValue(ColumnCollectionBehaviourProperty);
}
public static void SetColumnCollectionBehaviour(DependencyObject obj, DataGridColumnCollectionBehaviour value)
{
obj.SetValue(ColumnCollectionBehaviourProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(DataGrid))]
public static object GetColumnsSource(DependencyObject obj)
{
return (object)obj.GetValue(ColumnsSourceProperty);
}
[AttachedPropertyBrowsableForType(typeof(DataGrid))]
public static void SetColumnsSource(DependencyObject obj, ObservableCollection<DataGridColumn> value)
{
obj.SetValue(ColumnsSourceProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static string GetDisplayMemberFormatMember(DependencyObject obj)
{
return (string)obj.GetValue(DisplayMemberFormatMemberProperty);
}
public static void SetDisplayMemberFormatMember(DependencyObject obj, string value)
{
obj.SetValue(DisplayMemberFormatMemberProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static string GetDisplayMemberMember(DependencyObject obj)
{
return (string)obj.GetValue(DisplayMemberMemberProperty);
}
public static void SetDisplayMemberMember(DependencyObject obj, string value)
{
obj.SetValue(DisplayMemberMemberProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static string GetFontWeightBindingMember(DependencyObject obj)
{
return (string)obj.GetValue(FontWeightBindingMemberProperty);
}
public static void SetFontWeightBindingMember(DependencyObject obj, string value)
{
obj.SetValue(FontWeightBindingMemberProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static string GetFontWeightMember(DependencyObject obj)
{
return (string)obj.GetValue(FontWeightMemberProperty);
}
public static void SetFontWeightMember(DependencyObject obj, string value)
{
obj.SetValue(FontWeightMemberProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static string GetTextAlignmentMember(DependencyObject obj)
{
return (string)obj.GetValue(TextAlignmentMemberProperty);
}
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static void SetTextAlignmentMember(DependencyObject obj, string value)
{
obj.SetValue(TextAlignmentMemberProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static string GetTextColourMember(DependencyObject obj)
{
return (string)obj.GetValue(TextColourMemberProperty);
}
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static void SetTextColourMember(DependencyObject obj, string value)
{
obj.SetValue(TextColourMemberProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(DataGrid))]
public static string GetHeaderTextMember(DependencyObject obj)
{
return (string)obj.GetValue(HeaderTextMemberProperty);
}
[AttachedPropertyBrowsableForType(typeof(DataGrid))]
public static void SetHeaderTextMember(DependencyObject obj, string value)
{
obj.SetValue(HeaderTextMemberProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static string GetWidthMember(DependencyObject obj)
{
return (string)obj.GetValue(WidthMemberProperty);
}
public static void SetWidthMember(DependencyObject obj, string value)
{
obj.SetValue(WidthMemberProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static string GetSortMember(DependencyObject obj)
{
return (string)obj.GetValue(SortMemberProperty);
}
public static void SetSortMember(DependencyObject obj, string value)
{
obj.SetValue(SortMemberProperty, value);
}
private static void ColumnsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).ColumnsSource = e.NewValue;
}
private static void DisplayMemberFormatMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).DisplayMemberFormatMember = e.NewValue as string;
}
private static void DisplayMemberMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).DisplayMemberMember = e.NewValue as string;
}
private static void FontWeightBindingMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).FontWeightBindingMember = e.NewValue as string;
}
private static void FontWeightMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).FontWeightMember = e.NewValue as string;
}
private static void IsEditableMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).IsEditableMember = e.NewValue as string;
}
private static void HeaderTextMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).HeaderTextMember = e.NewValue as string;
}
private static void SortMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).SortMember = e.NewValue as string;
}
private static void TextAlignmentMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).TextAlignmentMember = e.NewValue as string;
}
private static void TextColourMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).TextColourMember = e.NewValue as string;
}
private static void WidthMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridColumnCollection.GetOrCreateBehaviour(sender).WidthMember = e.NewValue as string;
}
private static DataGridColumnCollectionBehaviour GetOrCreateBehaviour(DependencyObject source)
{
DataGridColumnCollectionBehaviour behaviour = DataGridColumnCollection.GetColumnCollectionBehaviour(source);
if (behaviour == null)
{
behaviour = new DataGridColumnCollectionBehaviour(source as DataGrid);
DataGridColumnCollection.SetColumnCollectionBehaviour(source, behaviour);
}
return behaviour;
}
}
Example XAML usage
Now we actually get on to using it.
<DataGrid behaviors:DataGridColumnCollection.ColumnsSource="{Binding ColumnHeaders}"
behaviors:DataGridColumnCollection.DisplayMemberFormatMember="Format" behaviors:DataGridColumnCollection.DisplayMemberMember="DisplayMember"
behaviors:DataGridColumnCollection.FontWeightBindingMember="FontWeightMember"
behaviors:DataGridColumnCollection.HeaderTextMember="Header"
behaviors:DataGridColumnCollection.SortMember="SortMember"
behaviors:DataGridColumnCollection.TextAlignmentMember="TextAlignment"
behaviors:DataGridColumnCollection.TextColourMember="TextColourMember"
behaviors:DataGridColumnCollection.WidthMember="Width"
ItemsSource="{Binding Items}">
Column Header class
This is just my 'simple' class that describes a column.
public class ColumnHeaderDescriptor
{
public string DisplayMember { get; set; }
public string FontWeightMember { get; set; }
public string Format { get; set; }
public string Header { get; set; }
public string SortMember { get; set; }
public TextAlignment TextAlignment { get; set; }
public string TextColourMember { get; set; }
public double Width { get; set; }
}
Instantiating the columns
And this is how I create them.
this.ColumnHeaders.Add(new ColumnHeaderDescriptor { Header = Properties.Resources.Name, DisplayMember = "ItemName", Width = 250 });
this.ColumnHeaders.Add(new ColumnHeaderDescriptor { Header = Properties.Resources.ManufPartNumber, DisplayMember = "ManufPartNumber", Width = 150 });
this.ColumnHeaders.Add(new ColumnHeaderDescriptor { Header = Properties.Resources.LastUnitPrice, DisplayMember = "UnitPriceString", SortMember = "UnitPrice", Width = 90 });
this.ColumnHeaders.Add(new ColumnHeaderDescriptor { Header = Properties.Resources.AssetType, DisplayMember = "AssetType", Width = 100 });
this.ColumnHeaders.Add(new ColumnHeaderDescriptor { Header = Properties.Resources.Quantity, DisplayMember = "QuantityString", SortMember = "Quantity", Width = 80 });
Conclusion
Now I appreciate that this may not be fully MVVM but at the end of the day we have to make sacrifices in order to get the job done. This will allow you to dynamically create columns and show different pieces of information from within your View Model.
My solution is quite a complicated one and I can't take full credit. I am sure I got the starting point from an existing StackOverflow answer however I am at a loss to know where that is now. Given it's complexity by allowing the consumer to determine lots of different things like text colour, etc. this could well be overkill for others solutions and you could remove the unnecessary properties should you not need them.
Assuming you are using Entity Framework for your model; create a partial class of your model with property getters that calculate based on the base class. Ensure that you implement INotifyPropertyChange and then bind the new properties of NetAmount and Discount in new columns.

Categories

Resources