WPF DataGrid Column Total MVVM Patern - c#

I have one column in my DataGrid that is a Price field.
In a TextBlock at the bottom of my form.
How to show the total value in the TextBlock based on the values of the Price column?
XAML-code:
<Grid>
<DataGrid HorizontalAlignment="Left" ItemsSource="{Binding Path=SaleryDetailsCollection, Mode=TwoWay}" AutoGenerateColumns="False" VerticalAlignment="Top" Width="640" Height="192" >
<DataGrid.Columns>
<DataGridTextColumn Header="Type" Binding="{Binding Type, Mode=TwoWay, NotifyOnTargetUpdated=True, ValidatesOnDataErrors=True}" Width="*" />
<DataGridTextColumn Header="Amount" Binding="{Binding Price, Mode=TwoWay, NotifyOnTargetUpdated=True, ValidatesOnDataErrors=True}" Width="*" />
</DataGrid.Columns>
</DataGrid>
<TextBlock HorizontalAlignment="Left" Margin="415,358,0,0" TextWrapping="Wrap" Text="{Binding SalaryTotal}" VerticalAlignment="Top"/>
</Grid>
ViewModel:
public ObservableCollection<SaleryDetailsModel> SaleryDetailsCollection
{
get { return _SaleryDetailsCollection; }
set
{
SalaryTotal = SaleryDetailsCollection.Sum(x => x.Amount);
_SaleryDetailsCollection = value;
NotifyPropertyChanged("SaleryDetailsCollection");
}
}
public Double SalaryTotal
{
get { return _SalaryTotal; }
set
{
_SalaryTotal = value;
NotifyPropertyChanged("SalaryTotal");
}
}
Class SaleryDetailsMode
class SaleryDetailsModel:ViewModel
{
private Double _Amount;
private String _Type;
public Double Amount
{
get { return _Amount; }
set
{
_Amount = value;
NotifyPropertyChanged("Amount");
}
}
public String Type { get { return _Type; } set { _Type = value; NotifyPropertyChanged("Type"); } }
}
Class ViewModel
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

Add this code inside the Constructor
SaleryDetailsCollection = new ObservableCollection<SaleryDetailsModel>();
SaleryDetailsCollection.CollectionChanged += MyItemsSource_CollectionChanged;
In ViewModel
void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach (SaleryDetailsModel item in e.NewItems)
item.PropertyChanged += MyType_PropertyChanged;
if (e.OldItems != null)
foreach (SaleryDetailsModel item in e.OldItems)
item.PropertyChanged -= MyType_PropertyChanged;
}
void MyType_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Amount")
DoWork();
}
private void DoWork()
{
SalaryTotal = SaleryDetailsCollection.Sum(x => x.Amount);
}
XAML
<Grid>
<DataGrid HorizontalAlignment="Left" ItemsSource="{Binding Path=SaleryDetailsCollection, Mode=TwoWay}" AutoGenerateColumns="False" VerticalAlignment="Top" Width="640" Height="192" >
<DataGrid.Columns>
<DataGridTextColumn Header="Type" Binding="{Binding Type, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="*" />
<DataGridTextColumn Header="Amount" Binding="{Binding Price, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="*" />
</DataGrid.Columns>
</DataGrid>
<TextBlock HorizontalAlignment="Left" Margin="415,358,0,0" TextWrapping="Wrap" Text="{Binding SalaryTotal}" VerticalAlignment="Top"/>

The calculation must be done after the field assignment:
public ObservableCollection<SaleryDetailsModel> SaleryDetailsCollection
{
get { return _SaleryDetailsCollection; }
set
{
_SaleryDetailsCollection = value;
SalaryTotal = SaleryDetailsCollection.Sum(x => x.Amount); // This line must be after the field assignment.
NotifyPropertyChanged("SaleryDetailsCollection");
}
}

You need to use a collection which will get notification when its property getting changed. Refer the link https://social.msdn.microsoft.com/Forums/silverlight/en-US/12915e07-be95-4fc5-b8f0-b0a49b10bc57/observablecollection-item-changed. It has NotifyableCollection. I have used it for the below code.
<Grid>
<StackPanel>
<DataGrid x:Name="dgr" HorizontalAlignment="Left" ItemsSource="{Binding Path=Salaries, Mode=TwoWay}"
AutoGenerateColumns="True" VerticalAlignment="Top" Width="640" Height="192" >
</DataGrid>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding TotalSalary}">
</TextBlock>
</StackPanel>
</Grid>
class ViewModel : INotifyPropertyChanged
{
private NotifiableCollection<Salary> salaries;
double _totalSal;
public double TotalSalary
{
get { return _totalSal = Salaries.Sum(x => x.Amount); }
set { _totalSal = value; }
}
public NotifiableCollection<Salary> Salaries
{
get { return salaries; }
set
{
salaries = value;
if (salaries != null)
{
salaries.ItemChanged += salaries_ItemChanged;
}
}
}
void salaries_ItemChanged(object sender, NotifyCollectionChangeEventArgs e)
{
OnPropertyChanged("TotalSalary");
}
public ViewModel()
{
Salaries = new NotifiableCollection<Salary>();
for (int i = 0; i < 10; i++)
{
Salary s = new Salary() { Type="Type"+i,Amount=new Random().Next(20000,30000)};
Salaries.Add(s);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
class Salary : INotifyPropertyChanged
{
private string type;
public string Type
{
get { return type; }
set { type = value; OnPropertyChanged("Type"); }
}
private double amount;
public double Amount
{
get { return amount; }
set { amount = value; OnPropertyChanged("Amount"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public class NotifyCollectionChangeEventArgs : PropertyChangedEventArgs
{
public int Index { get; set; }
public NotifyCollectionChangeEventArgs(int index, string propertyName) : base(propertyName) { Index = index; }
}
public class NotifiableCollection<T> : ObservableCollection<T> where T : class, INotifyPropertyChanged
{
public event EventHandler<NotifyCollectionChangeEventArgs> ItemChanged;
protected override void ClearItems()
{
foreach (var item in this.Items)
item.PropertyChanged -= ItemPropertyChanged;
base.ClearItems();
}
protected override void SetItem(int index, T item)
{
this.Items[index].PropertyChanged -= ItemPropertyChanged;
base.SetItem(index, item);
this.Items[index].PropertyChanged += ItemPropertyChanged;
}
protected override void RemoveItem(int index)
{
this.Items[index].PropertyChanged -= ItemPropertyChanged;
base.RemoveItem(index);
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
item.PropertyChanged += ItemPropertyChanged;
}
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
T changedItem = sender as T;
OnItemChanged(this.IndexOf(changedItem), e.PropertyName);
}
private void OnItemChanged(int index, string propertyName)
{
if (ItemChanged != null)
this.ItemChanged(this, new NotifyCollectionChangeEventArgs(index, propertyName));
}
}

Related

How to Sum every added column in DataGridView?

I have created a SubPODetailView.xaml that includes a SubPoDetailViewModel.cs, which in turn this Model contain a SubInvoice.cs for a datagrid and the SubPO.cs for other details. What I wnat is every time I added a row, the sum of "Amount" columns displayed in the total TextBlock.
The Screenshot of View Page of running Program
SubPODetailView.xaml
<DataGrid ItemsSource="{Binding Invoices}"
SelectedItem="{Binding SelectedInvoice,Mode=TwoWay}"
AutoGenerateColumns="False" RowHeaderWidth="0" >
<DataGrid.Columns>
<DataGridTextColumn Header="Invoices" Width="*"
Binding="{Binding InvoiceName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTemplateColumn Header="Date" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding Date,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Amount" Width="*" Binding="{Binding Amount,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTemplateColumn Header="Status" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Status,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<TextBlock Text="{Binding Total}" Margin="10,0" Background="#FFC9CD7D" Foreground="Black"/>
SubPODetailViewModel.cs
public class SubPODetailViewModel : DetailViewModelBase, ISubPODetailViewModel
{
private ISubPORepository _subPORepository;
private IMessageDialogService _messageDialogService;
private SubPOWrapper _subPO;
private SubInvoiceWrapper _selectedInvoice;
public SubPODetailViewModel(IEventAggregator eventAggregator,
IMessageDialogService messageDialogService,
ISubPORepository subPORepository) : base(eventAggregator)
{
_subPORepository = subPORepository;
_messageDialogService = messageDialogService;
AddInvoiceCommand = new DelegateCommand(OnAddInvoiceExecute);
RemoveInvoiceCommand = new DelegateCommand(OnRemoveInvoiceExecute, OnRemoveInvoiceCanExecute);
Invoices = new ObservableCollection<SubInvoiceWrapper>();
}
// SubPOWrapper for SubPO.cs class
public SubPOWrapper SubPO
{
get { return _subPO; }
private set
{
_subPO = value;
OnPropertyChanged();
}
}
// SubInvoiceWrapper for SubInvoice.cs class
public SubInvoiceWrapper SelectedInvoice
{
get { return _selectedInvoice; }
set
{
_selectedInvoice = value;
OnPropertyChanged();
((DelegateCommand)RemoveInvoiceCommand).RaiseCanExecuteChanged();
}
}
public ICommand AddInvoiceCommand { get; }
public ICommand RemoveInvoiceCommand { get; }
public ObservableCollection<SubInvoiceWrapper> Invoices { get; }
public override async Task LoadAsync(int? subPOId)
{
var subPO = subPOId.HasValue
? await _subPORepository.GetByIdAsync(subPOId.Value)
: CreateNewSubPO();
InitializeSubPO(subPO);
InitializeSubInvoice(subPO.Invoices);
}
private SubPO CreateNewSubPO()
{
var subPO = new SubPO();
_subPORepository.Add(subPO);
return subPO;
}
private void InitializeSubInvoice(ICollection<SubInvoice> invoices)
{
foreach (var wrapper in Invoices)
{
wrapper.PropertyChanged -= SubInvoiceWrapper_PropertyChanged;
}
Invoices.Clear();
foreach (var subInvoice in invoices)
{
var wrapper = new SubInvoiceWrapper(subInvoice);
Invoices.Add(wrapper);
wrapper.PropertyChanged += SubInvoiceWrapper_PropertyChanged;
}
}
private void SubInvoiceWrapper_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!HasChanges)
{
HasChanges = _subPORepository.HasChanges();
}
if (e.PropertyName == nameof(SubInvoiceWrapper.HasErrors))
{
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
}
private void InitializeSubPO(SubPO subPO)
{
SubPO = new SubPOWrapper(subPO);
SubPO.PropertyChanged += (s, e) =>
{
if (!HasChanges)
{
HasChanges = _subPORepository.HasChanges();
}
if (e.PropertyName == nameof(SubPO.HasErrors))
{
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
};
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
if (SubPO.Id == 0)
{
// Little trick to trigger the validation
SubPO.Title = "";
}
}
protected override void OnDeleteExecute()
{
var result = _messageDialogService.ShowOkCancelDialog($"Do you really want to delete the Subcontractor {SubPO.Title}?", "Question");
if (result == MessageDialogResult.OK)
{
_subPORepository.Remove(SubPO.Model);
_subPORepository.SaveAsync();
RaiseDetailDeletedEvent(SubPO.Id);
}
}
protected override bool OnSaveCanExecute()
{
return SubPO != null
&& !SubPO.HasErrors
&& Invoices.All(i=>!i.HasErrors)
&& HasChanges;
}
protected override async void OnSaveExecute()
{
await _subPORepository.SaveAsync();
HasChanges = _subPORepository.HasChanges();
RaiseDetailSavedEvent(SubPO.Id, SubPO.Title);
}
private void OnAddInvoiceExecute()
{
var newInvoice = new SubInvoiceWrapper(new SubInvoice());
newInvoice.PropertyChanged += SubInvoiceWrapper_PropertyChanged;
Invoices.Add(newInvoice);
SubPO.Model.Invoices.Add(newInvoice.Model);
//newInvoice.InvoiceName = "";
newInvoice.Date = DateTime.Now.Date;
// Trigger validation :-)
}
private void OnRemoveInvoiceExecute()
{
SelectedInvoice.PropertyChanged -= SubInvoiceWrapper_PropertyChanged;
_subPORepository.RemoveInvoice(SelectedInvoice.Model);
Invoices.Remove(SelectedInvoice);
SelectedInvoice = null;
HasChanges = _subPORepository.HasChanges();
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
private bool OnRemoveInvoiceCanExecute()
{
return SelectedInvoice !=null;
}
}
}
Note: for checking the whole program as zip file, here you go(just click slow dowload then continue): http://www.filefactory.com/file/681du53ow7us/SubBVZip.zip
Update
Screenshot of the problem I got when running the program, How to sum the rows without adding another new one.
The last TextBlock of SubPODetailView is already bound to Total property in view model.
Create the property Total on view model and add CollectionChanged handler to Invoices:
public SubPODetailViewModel(IEventAggregator eventAggregator,
IMessageDialogService messageDialogService,
ISubPORepository subPORepository) : base(eventAggregator)
{
...
Invoices.CollectionChanged += Invoices_CollectionChanged;
}
private void Invoices_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Total = Invoices.Sum(x => x.Amount);
}
private decimal _total;
public decimal Total
{
get => _total;
set {
_total = value;
OnPropertyChanged();
}
}

when click dataGridview column Visiblity change of a textbox in WPF-MVVM?

I want to make a text Box Visible when clicking a DataGrid Column. I use this text box for description of a data Grid column
My two column has grid view (Item and Amount columns)
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Item" Width="*">
<DataGridTemplateColumn.CellTemplate>
<TextBlock Text="{Binding Item}" VerticalAlignment="Center"/>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Amount" Width="*">
<DataGridTemplateColumn.CellTemplate>
<TextBlock Text="{Binding Amount}" VerticalAlignment="Center"/>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I want get the sum of Column [Amount] and show it on a textbox, that is visible only when I click [Amount] Column
Please try the next code:
1. Xaml:
<Window x:Class="DataGridSoHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dataGridSoHelpAttempt="clr-namespace:DataGridSoHelpAttempt"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Title="MainWindow" Height="350" Width="525" x:Name="This">
<Window.DataContext>
<dataGridSoHelpAttempt:MainViewModel/>
</Window.DataContext>
<Grid x:Name="MyGrid">
<DataGrid x:Name="MyDataGrid" ItemsSource="{Binding DataSource}" AutoGenerateColumns="False">
<i:Interaction.Behaviors>
<dataGridSoHelpAttempt:ColumnSelectingBehavior TotalPresenterVisibility="{Binding TotalsVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</i:Interaction.Behaviors>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Comments" Binding="{Binding Comments}" Width="150"/>
<DataGridTextColumn Header="Price (click to see total)" Binding="{Binding Price, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
<TextBlock Visibility="{Binding TotalsVisibility, UpdateSourceTrigger=PropertyChanged}">
<Run Text="Total price:"></Run>
<Run Text="{Binding TotalValue, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"></Run>
</TextBlock>
</StackPanel>
</Grid></Window>
2. View models and models code:
public class MainViewModel:BaseObservableObject
{
private Visibility _visibility;
private ICommand _command;
private Visibility _totalsVisibility;
private double _totalValue;
public MainViewModel()
{
Visibility = Visibility.Collapsed;
TotalsVisibility = Visibility.Collapsed;
DataSource = new ObservableCollection<BaseData>(new List<BaseData>
{
new BaseData {Name = "Uncle Vania", Description = "A.Chekhov, play", Comments = "worth reading", Price = 25},
new BaseData {Name = "Anna Karenine", Description = "L.Tolstoy, roman", Comments = "worth reading", Price = 35},
new BaseData {Name = "The Master and Margarita", Description = "M.Bulgakov, novel", Comments = "worth reading", Price = 56},
});
}
public ICommand Command
{
get
{
return _command ?? (_command = new RelayCommand(VisibilityChangingCommand));
}
}
private void VisibilityChangingCommand()
{
Visibility = Visibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
}
public ObservableCollection<BaseData> DataSource { get; set; }
public Visibility Visibility
{
get { return _visibility; }
set
{
_visibility = value;
OnPropertyChanged();
}
}
public ObservableCollection<BaseData> ColumnCollection
{
get { return DataSource; }
}
public Visibility TotalsVisibility
{
get { return _totalsVisibility; }
set
{
_totalsVisibility = value;
OnPropertyChanged();
}
}
public double TotalValue
{
get { return ColumnCollection.Sum(x => x.Price); }
}
}
public class BaseData:BaseObservableObject
{
private string _name;
private string _description;
private string _comments;
private int _price;
public virtual string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public virtual object Description
{
get { return _description; }
set
{
_description = (string) value;
OnPropertyChanged();
}
}
public string Comments
{
get { return _comments; }
set
{
_comments = value;
OnPropertyChanged();
}
}
public int Price
{
get { return _price; }
set
{
_price = value;
OnPropertyChanged();
}
}
}
3. Behavior code:
public class ColumnSelectingBehavior : Behavior<DataGrid>
{
public static readonly DependencyProperty TotalPresenterVisibilityProperty = DependencyProperty.Register(
"TotalPresenterVisibility", typeof (Visibility), typeof (ColumnSelectingBehavior), new PropertyMetadata(Visibility.Collapsed));
public Visibility TotalPresenterVisibility
{
get { return (Visibility) GetValue(TotalPresenterVisibilityProperty); }
set { SetValue(TotalPresenterVisibilityProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Sorting += AssociatedObjectOnSorting;
}
private void AssociatedObjectOnSorting(object sender, DataGridSortingEventArgs dataGridSortingEventArgs)
{
var columnSortMemberPath = dataGridSortingEventArgs.Column.SortMemberPath;
if(columnSortMemberPath != "Price")
return;
TotalPresenterVisibility = TotalPresenterVisibility == Visibility.Visible
? Visibility.Collapsed
: Visibility.Visible;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Sorting -= AssociatedObjectOnSorting;
}
}
4. BaseObservableObject code (for .net 4.5):
public class BaseObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
var propName = ((MemberExpression)raiser.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
}
I'll glad to help in case you will have problems wit the code. Regards.

Change the value in a cell in a datagrid (binding)

this is my issue.
When I select a single row and I click on the button, the cell must change value, but it doesn't change anything
This is XAML code.
<DataGrid
ItemsSource="{Binding Dati_Viaggio, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectDati_Viaggio, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="False" HorizontalAlignment="Left" Height="119" Margin="10,10,0,0" VerticalAlignment="Top" Width="497">
<DataGrid.Columns>
<DataGridTextColumn x:Name="NumOrd" Binding="{Binding Path=NumOrd, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="NumOrd" Width="150" />
</DataGrid.Columns>
</DataGrid>
and this is c# code
Public ObservableCollection<Model_Ricerca_Dati_Viaggio> Dati_Viaggio
{ get; set; }
private Model_Ricerca_Dati_Viaggio _SelectDati_Viaggio;
public Model_Ricerca_Dati_Viaggio SelectDati_Viaggio {
get { return _SelectDati_Viaggio; }
set {
_SelectDati_Viaggio = value;
OnPropertyChanged("SelectDati_Viaggio");}}
private string _NumOrd { get; set; }
public string NumOrd {
get { return _NumOrd; }
set {
_NumOrd = value;
OnPropertyChanged("NumOrd");}}
Private void Cmd_TrovaExe()
{
SelectDati_Viaggio.NumOrd = Now.#string;
OnPropertyChanged("NumOrd");
OnPropertyChanged("Dati_Viaggio");
OnPropertyChanged("SelectDati_Viaggio");
}
Why the cell doesn't refres after SelectDati_Viaggio.NumOrd = Now.#string; ?
Your class Model_Ricerca_Dati_Viaggio must as well implements the INotifyChangedinterface in order to the changed to be exposed to the UI :
public class Model_Ricerca_Dati_Viaggio:INotifyPropertyChanged
{
private string _numOrd ;
public string NumOrd
{
get
{
return _numOrd;
}
set
{
if (_numOrd == value)
{
return;
}
_numOrd = value;
OnPropertyChanged("NumOrd");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}

WPF DataGrid calculations

I am very new to WPF and C# so please help to solve this problem.
I have a DataGrid, columns as follows: "One", "Two", "Multiply".
The idea that I am entering number "One" or "Two" and get result in the column "Multiply".
When I write my code and debug it, I can see that the value of the property is being re-calculated. However, I would not display in my last column unless I push space bar, or click on my last column.
Here is the code:
public class Numbers : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private double _one;
private double _two;
private double _multiply;
public double One
{
get { return _one; }
set { _one = value; UpdateValue(); }
}
public double Two
{
get { return _two; }
set { _two = value; UpdateValue(); }
}
public double Multiply
{
get { return _multiply; }
set { _multiply = value; UpdateValue(); }
}
public Numbers(double pOne, double pTwo)
{
_one = pOne;
_two = pTwo;
_multiply = GetMultiply();
}
private void UpdateValue()
{
OnPropertyChanged("One");
OnPropertyChanged("Two");
OnPropertyChanged("Multiply");
_multiply = GetMultiply();
}
private double GetMultiply()
{
return _one * _two;
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class Collection : ObservableCollection<Numbers>
{
public ObservableCollection<Numbers> Numbers { get; set; }
public Collection()
{
Numbers = new ObservableCollection<Numbers>();
Numbers.Add(new Numbers(1, 2));
Numbers.Add(new Numbers(2, 2));
}
}
XAML:
<DataGrid x:Name="StockData" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" ItemsSource="{Binding}" AutoGenerateColumns="False" LostFocus="StockData_LostFocus" >
<DataGrid.Columns >
<DataGridTextColumn Header="Number One" Width="100" Binding="{Binding One, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat=\{0:C\}}" />
<DataGridTextColumn Header="Number Two" Width="100" Binding="{Binding Two, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat=\{0:C\}}" />
<DataGridTextColumn Header="Total" Width="100" Binding="{Binding Multiply, BindsDirectlyToSource=True, Mode=TwoWay, StringFormat=\{0:C\}, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
You are setting multiply after raising PropertyChanged event, so UI won't get notification that value has been changed.
Set Multiply and then raise PropertyChanged event:
private void UpdateValue()
{
_multiply = GetMultiply();
OnPropertyChanged("One");
OnPropertyChanged("Two");
OnPropertyChanged("Multiply");
}

How to Change Panels Position in WPF ItemComtrols?

My WPF Application code generates panels on function call defined in .cs file. There is ItemControl used in code to generates these Panels . I want to move Panels Up or Down.
Example: There are three Panels PanelA, PanelB, PanelC now now there is button with each panel to move it up. Now i selected PanleB. i click on that button and PanelB will move up now they should be like PanelB, PanelA, PanelC
.XAML File:
<ItemsControl x:Name="lstItems" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBox x:Name="txtText" Width="300" Height="100" Text="{Binding Text;, Mode=TwoWay}" FontSize="{Binding FontSize, Mode=OneWay}" />
<Slider Minimum="10" Maximum="30" Value="{Binding FontSize, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
.CS File:
public partial class MainWindow : Window
{
protected ObservableCollection<MyPanel> texts = new ObservableCollection<MyPanel>();
public MainWindow()
{
InitializeComponent();
texts.Add(new MyPanel() { Text = "Test 1" });
texts.Add(new MyPanel() { Text = "Test 2" });
lstItems.ItemsSource = texts;
}
}
public class MyPanel : INotifyPropertyChanged
{
private string _id;
private string _text;
private double _fontSize = 10;
public string Id
{
get { return _id; }
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged();
}
}
}
public string Text
{
get { return _text; }
set
{
if (value != _text)
{
_text = value;
NotifyPropertyChanged();
}
}
}
public double FontSize
{
get { return _fontSize; }
set
{
if (value != _fontSize)
{
_fontSize = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
EDIT:
XAML FILE:
<ItemsControl x:Name="lstItemsClassM" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<ComboBox x:Name="cboOccupation" IsEditable="False" HorizontalAlignment="Left"
Text="{Binding Path=Alignment, Mode=TwoWay}"
Margin="4" Width="140">
<ComboBoxItem>Right</ComboBoxItem>
<ComboBoxItem>Left</ComboBoxItem>
</ComboBox>
<Button Content="Move Up" Click="Button_Click_1" Tag="{Binding PKId}"/>
<Button Content="{Binding Alignment, Mode=TwoWay}" Click="Button_Click" Tag="{Binding PKId}" SourceUpdated="Button_SourceUpdated" />
<TextBox x:Name="txtText" Width="300" Height="100" Text="{Binding Text;, Mode=TwoWay}" FontSize="{Binding FontSize, Mode=OneWay}" TextAlignment="{Binding Alignment, Mode=OneWay}" />
<Slider Minimum="10" Maximum="30" Value="{Binding FontSize, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
.CS FILE:
public partial class Window2 : Window
{
protected ObservableCollection<ClassM> texts = new ObservableCollection<ClassM>();
int dv;
public Window2()
{
InitializeComponent();
dv=1;
texts.Add(new ClassM() { PKId=dv, Text = "Test 1" });
dv=2;
texts.Add(new ClassM() { PKId=dv, Text = "Test 2" });
lstItemsClassM.ItemsSource = texts;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var myValue = ((Button)sender).Tag;
foreach (var f in texts.ToList())
{
if (f.PKId.ToString() == myValue.ToString())
{
f._alignment = "Right";
MessageBox.Show(f._alignment);
}
}
}
private void Button_SourceUpdated(object sender, DataTransferEventArgs e)
{
var myValue = ((Button)sender).Tag;
foreach (var f in texts.ToList())
{
if (f.PKId.ToString() == myValue.ToString())
{
f._alignment = "Right";
MessageBox.Show(f._alignment);
}
}
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var myValue = ((Button)sender).Tag;
foreach (var f in texts.ToList())
{
if (f.PKId.ToString() == myValue.ToString())
{
int s = f.PKId + 1;
texts.Move(f.PKId , s);
MessageBox.Show(f.PKId +" &&&&& " + s );
}
}
}
}
public class ClassM : INotifyPropertyChanged
{
private string _id;
private int _pkid;
private string _text;
private double _fontSize = 10;
public bool _isChecked { get; set; }
public string _alignment="Left";
public int PKId
{
get { return _pkid; }
set
{
if (value != _pkid)
{
_pkid = value;
NotifyPropertyChanged();
}
}
}
public string Id
{
get { return _id; }
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged();
}
}
}
public bool IsChecked
{
get { return _isChecked; }
set
{
if (value != _isChecked)
{
_isChecked = value;
NotifyPropertyChanged();
}
}
}
public string Text
{
get { return _text; }
set
{
if (value != _text)
{
_text = value;
NotifyPropertyChanged();
}
}
}
public double FontSize
{
get { return _fontSize; }
set
{
if (value != _fontSize)
{
_fontSize = value;
NotifyPropertyChanged();
}
}
}
public string Alignment
{
get { return _alignment; }
set
{
if (value != _alignment)
{
_alignment = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Use the ObservableCollection's Move method:
private void Swap(MyPanel a, MyPanel b)
{
var indexA = texts.IndexOf(a);
var indexB = texts.IndexOf(b);
texts.Move(a,b);
}
Create the Command in your MainWindow, say MovePanelCommand and bind the Button commmand to this command and send the current item as the command parameter
<StackPanel Orientation="Vertical">
<TextBox x:Name="txtText" Width="300" Height="100" Text="{Binding Text;, Mode=TwoWay}" FontSize="{Binding FontSize, Mode=OneWay}" />
<Slider Minimum="10" Maximum="30" Value="{Binding FontSize, Mode=TwoWay}" />
<Button Command="{Binding DataContext.MovePanelCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParamter="{Binding}"/>
</StackPanel>
Now in command handler you will get your Panel you want to move up
private void MovePanelCommandHandler(object param)
{
MyPanel panel = param as MyPanel;
// Just move the panel one index up with validation if it is a first panel
}

Categories