DataGridTemplateColumn update - c#

I'm reworking an old control to WPF. I need to make a datagrid which displays a list of alarms. The first column of the datagrid is a datagridtemplate column which holds a flag icon and the date. When the user presses the button the alarms are acknowledged and the flag icon has to disappear. I'm using an MVVM pattern.
The problem is that I cannot seem to update the flag in the datagridtemplate column. When I change the acknowledge status from 0 to 1 (the alarm was ackonwledged), the flag does not disappear. When I reload the control and get the alarms again from the server, the flag does not show up again indicating that its status has actually been updated in the server. So somehow my code is working but my UI is not getting updated. I also tried to change the alarm's description in the same way and that column does get updated. (MainViewModelBase implements INotifyPropertyChanged)
The view
<DataGrid x:Class="Kwa.Presentation.Views.AlarmList.AlarmList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Kwa.Presentation.Views.AlarmList"
xmlns:components="clr-namespace:Kwa.Presentation.Components"
xmlns:converters="clr-namespace:Kwa.Presentation.Converters"
xmlns:Trans="clr-namespace:Kwa.Presentation.Resources"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="750"
ItemsSource="{Binding Alarms}"
SelectedItem="{Binding SelectedAlarm}"
IsSynchronizedWithCurrentItem="True"
CanUserResizeColumns="True" IsReadOnly="True" CanUserReorderColumns="False" CanUserSortColumns="False" SelectionMode="Single" CanUserAddRows="False"
Background="White" RowHeaderWidth="0" AutoGenerateColumns="False" GridLinesVisibility="None" RowHeight="{Binding Rowheight}" FrozenColumnCount = "1"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto"
x:Name="AlarmFramework"
SizeChanged="AlarmFramework_SizeChanged"
>
<DataGrid.Resources>
<converters:AlarmSeverityToColorConverter x:Key="AlarmSeverityToColorConverter"/>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<converters:InvertedBoolToVisibilityConverter x:Key="InvertedBoolToVisibilityConverter"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="{x:Static Trans:TranslatedResources.AlarmColumnHeaderTime}" Width="auto" HeaderStyle="{StaticResource WithButt}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<components:FlagControl VerticalAlignment="Center" Height="15" Width="15" FlagColor="{Binding Severity, Converter={StaticResource AlarmSeverityToColorConverter}}"
Visibility="{Binding AckStatus, Converter={StaticResource InvertedBoolToVisibilityConverter}, Mode=TwoWay}"/>
<TextBlock Text="{Binding DateTimeString}" Padding="10,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="{x:Static Trans:TranslatedResources.AlarmColumnHeaderSeverity}" Binding="{Binding SeverityString}" Width="auto" HeaderStyle="{StaticResource WithoutButt}"/>
<DataGridTextColumn Header="{x:Static Trans:TranslatedResources.AlarmColumnHeaderDescription}" Binding="{Binding Description}" d:Width="400" Width="*" HeaderStyle="{StaticResource WithoutButt}"/>
</DataGrid.Columns>
</DataGrid>
The AlarmList View Model
public class AlarmListViewModel : MainViewModelBase
{
#region Properties
private ObservableCollection<AlarmEntryViewModel> _alarms = new ObservableCollection<AlarmEntryViewModel>();
public ObservableCollection<AlarmEntryViewModel> Alarms
{
get { return _alarms; }
set
{
_alarms = value;
RaisePropertyChanged(() => Alarms);
}
}
private AlarmEntryViewModel _selectedAlarm;
public AlarmEntryViewModel SelectedAlarm
{
get { return _selectedAlarm; }
set
{
_selectedAlarm = value;
RaisePropertyChanged(() => SelectedAlarm);
}
}
private int _acknowledgeAllowed;
public int AcknowledgeAllowed
{
get { return _acknowledgeAllowed; }
set
{
_acknowledgeAllowed = value;
RaisePropertyChanged(() => AcknowledgeAllowed);
}
}
private readonly IActionCommand _acknowledgeCommand;
public IActionCommand AcknowledgeCommand
{
get { return _acknowledgeCommand; }
}
public int MaxAcknowledgedAlarm;
public double RowHeight { get; set; }
public AlarmEntryViewModel AlarmToSend { get; set; }
#endregion
#region Constructor
public AlarmListViewModel()
{
if (!IsInDesignMode)
{
RowHeight = 30;
}
//Add command
_acknowledgeCommand = new ActionCommand<double>(p => Acknowledge(p));
}
#endregion
#region Private Methods
private void Acknowledge(double parameter)
{
if (AcknowledgeAllowed == 1)
{
try
{
//Get Datagrid width
double DatagridWidth = (double)parameter;
//Calculate the amount of fully visible alarms
int AmountVisible = (int)Math.Floor(DatagridWidth / RowHeight);
if (Alarms.Count < AmountVisible)
{
AlarmToSend = Alarms[Alarms.Count - 1];
foreach(AlarmEntryViewModel alarm in Alarms)
{
alarm.AckStatus = true;
alarm.Description = "Iets nieuw";
}
}
else
{
//Send the last visible alarm, 0 based => -1
AlarmToSend = Alarms[AmountVisible - 1];
//Go to next visible alarm
SelectedAlarm = Alarms[AmountVisible];
}
//Acknowledge alarms
_proxy.Send(AlarmToSend.AlarmNumber);
}
catch (Exception ex)
{
_viewManager.ShowDialog(new MessageDialogViewModel()
{
AskAnswer = false,
Text = ex.Message,
Title = TranslatedResources.AlarmAckSendErrorTitle,
});
}
}
else
{
_viewManager.ShowDialog(new MessageDialogViewModel()
{
AskAnswer = false,
Text = TranslatedResources.AlarmAcknErrorMessage,
Title = TranslatedResources.AlarmAcknErrorTitle,
});
}
}
#endregion
The AlarmListEntry view model
public class AlarmEntryViewModel : MainViewModelBase
{
#region Fields
private readonly IViewManager _viewManager;
private readonly IDockManager _dockManager;
private string _description;
#endregion
#region Constructor
public AlarmEntryViewModel()
{
}
#endregion
#region Model & Proxy
private Alarm _model;
public Alarm Model
{
set { _model = value; }
}
public AlarmListServiceProxy Proxy { get; set; }
#endregion
#region Properties
public DateTime Time { get { return _model.Time; } }
public string DateTimeString { get { return _model.Time.ToString("dd/MM/yyyy hh:mm:ss"); } }
public int Severity { get { return _model.Severity; } }
public string SeverityString
{
get
{
if (_model.Severity == 0)
return "Melding";
if (_model.Severity == 1)
return "Waarschuwing";
if (_model.Severity == 2)
return "Fout";
else
return "Niet gekend";
}
}
public string Description
{
get
{
//string substring = _model.Description.Substring(_model.Description.Length - 1);
//if ( substring.Equals("\n"))
//{
// return _model.Description.Substring(0, _model.Description.Length - 1);
//}
//else
// return _model.Description;
return _description;
}
set
{
_description = value;
RaisePropertyChanged(() => Description);
}
}
public int AlarmNumber { get { return _model.Number; } }
public int AcknStatus { get { return _model.AcknStatus; } }
private bool _ackStatus;
public bool AckStatus
{
get
{
if (_model.AcknStatus == 0)
return false;
else
return true;
}
set
{
_ackStatus = value;
RaisePropertyChanged(() => AckStatus);
}
}
#endregion
}

You set _ackStatus in the setter but doesn't return the value of this field from the getter in AlarmEntryViewModel. Do this:
private bool _ackStatus;
public bool AckStatus
{
get
{
return _ackStatus;
}
set
{
_ackStatus = value;
RaisePropertyChanged(() => AckStatus);
}
}
Or set the _model.AcknStatus field in the setter:
private bool _ackStatus;
public bool AckStatus
{
get
{
if (_model.AcknStatus == 0)
return false;
else
return true;
}
set
{
_ackStatus = value;
_model.AcknStatus = value ? 1 : 0;
RaisePropertyChanged(() => AckStatus);
}
}

Related

C#-Accessing .xaml created checkbox/listbox

Is there a way to verify if checkbox and a specific item in a listbox has been selected, when it was created in .xaml?
I am trying to access these elements from a different class rather than the ViewModel.
I want to be able to do something as follows;
if (First_CheckBox.Ischecked && LocationBox.SelectedItem == "FirstValue")
{
do something;
}
else
{
do something else;
}
.XAML Code:
<CheckBox x:Name="First_CheckBox" IsChecked="{Binding Check, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Content="Integral "/>
<ListBox x:Name="LocationBox" ItemsSource="{Binding LocationList}" SelectionMode="Single" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Margin="0,5" Width="100" HorizontalAlignment="Left" BorderThickness="1" Background="#FFF0ECEC">
Current Code:
namespace ValueClearanceCalculator.ViewModels
{
class MainWindowViewModel : CommonBase
{
public delegate void PropertyChangedHandler(object obj);
public static event PropertyChangedHandler MainVMPropertyChanged = delegate { };
public MainWindowViewModel()
{
CalculateAndDrawPatternCommand = new CalculatePatternCommand(this);
}
private public bool _Check;
public bool Check
{
get
{
return _Check;
}
set
{
_Check = value;
RaisePropertyChanged("Check");
_C = _A;
double temp;
bool result = double.TryParse(_C, out temp);
if (result == false) { MessageBox.Show("Can Not Convert A to C"); }
else { FrontPanelVariables.C = temp * 25.4; }
RaisePropertyChanged("C");
_F = ".157";
FrontPanelVariables.F = .157;
RaisePropertyChanged("F");
}
}

CaliburnMicro - Binding list of items to ListBox

I need to create a list that binds with items specified by name, boolean state and color.
Here is my ViewModel:
using Caliburn.Micro;
public class MainWindowViewModel :Screen
{
private List<string> _commandListSource;
private List<CommandItem> _commandsSource;
public List<CommandItem> CommandsSource
{
get
{
return _commandsSource;
}
set
{
_commandsSource = value;
NotifyOfPropertyChange(() => CommandsSource);
}
}
public MainWindowViewModel()
{
_commandListSource = new List<string>();
_commandListSource.Add("A");
_commandListSource.Add("B");
getsource();
NotifyOfPropertyChange(() => CommandsSource);
}
private void getsource()
{
_commandsSource = new List<CommandItem>();
foreach (var x in _commandListSource)
{
var ci = new CommandItem();
ci.CommandName = x;
ci.IsInTheOtherList = true;
_commandsSource.Add(ci);
}
}
}
CommandItem class:
public class CommandItem
{
public string CommandName;
public bool IsInTheOtherList;
public Color BGColor
{
get
{
if (IsInTheOtherList)
return Color.FromRgb(0, 0,255);
return Color.FromRgb(255, 255,0);
}
}
}
Xaml ListBox:
<ListBox x:Name="Source"
ItemsSource="{Binding CommandsSource , NotifyOnSourceUpdated=True}"
HorizontalAlignment="Left"
ScrollViewer.VerticalScrollBarVisibility="Visible"
VerticalAlignment="Stretch" MinWidth="100">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel >
<TextBlock Text="*"/>
<Ellipse Fill="{Binding BGColor}" Width="10" Height="10"/>
<TextBlock Text="{Binding CommandName}"/>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem is that the ListBox displays only * characters
I've solved this by changing the CommandItem class:
public class CommandItemClass : PropertyChangedBase
{
private string _commandName;
private bool _isInTheOtherList;
public SolidColorBrush BGColor
{
get
{
if (IsInTheOtherList)
return new SolidColorBrush(Color.FromRgb(0, 0, 255));
return new SolidColorBrush(Color.FromRgb(255, 255, 0));
}
}
public string CommandName
{
get
{
return _commandName;
}
set
{
_commandName = value;
NotifyOfPropertyChange(()=>CommandName);
}
}
public bool IsInTheOtherList
{
get
{
return _isInTheOtherList;
}
set
{
_isInTheOtherList = value;
NotifyOfPropertyChange(() => IsInTheOtherList);
NotifyOfPropertyChange(()=>BGColor);
}
}
}

MVVM Light UI not updating upon loading settings

I'm experiencing an issue with UI not updating when loading the properties from a Settings file. Beneath the code is shown, I apologize for the Dutch property names, translation is as follows:
Lade: Tray.
Papiergrootte: papersize.
Geselecteerde: selected.
isEersteKeer: isFirstTime.
I have a usercontrol, a printer combobox, to which the viewmodel code (below) is bound.
public class PrinterViewModel : ViewModelBase
{
private ObservableCollection<Printer> _printers;
private Printer _geselecteerdePrinter;
private ObservableCollection<Lade> _lades;
private ObservableCollection<Papiergrootte> _papiergroottes;
private Lade _geselecteerdeLade;
private Papiergrootte _geselecteerdePapiergrootte;
private bool isEersteKeer;
public PrinterViewModel()
{
isEersteKeer = true;
_printers = Printer.GetPrinters();
LoadPrinterSettings();
isEersteKeer = false;
}
public Lade GeselecteerdeLade
{
get { return _geselecteerdeLade; }
set
{
_geselecteerdeLade = value;
RaisePropertyChanged("GeselecteerdeLade");
if (!isEersteKeer)
{
SavePrinterSettings();
}
}
}
public Papiergrootte GeselecteerdePapiergrootte
{
get { return _geselecteerdePapiergrootte; }
set
{
_geselecteerdePapiergrootte = value;
RaisePropertyChanged("GeselecteerdePapiergrootte");
if (!isEersteKeer)
{
SavePrinterSettings();
}
}
}
public ObservableCollection<Lade> Lades
{
get { return _lades; }
set
{
_lades = value;
RaisePropertyChanged("Lades");
if (Lades.Count > 0 && !isEersteKeer)
{
GeselecteerdeLade = Lades[0];
}
}
}
public ObservableCollection<Papiergrootte> Papiergroottes
{
get { return _papiergroottes; }
set
{
_papiergroottes = value;
RaisePropertyChanged("Papiergroottes");
if (Papiergroottes.Count > 0 && !isEersteKeer)
{
GeselecteerdePapiergrootte = Papiergroottes[0];
}
}
}
public ObservableCollection<Printer> Printers
{
get { return _printers; }
set
{
_printers = value;
RaisePropertyChanged("Printers");
}
}
public Printer GeselecteerdePrinter
{
get { return _geselecteerdePrinter; }
set
{
_geselecteerdePrinter = value;
RaisePropertyChanged("GeselecteerdePrinter");
Lades = _geselecteerdePrinter.Lades;
Papiergroottes = _geselecteerdePrinter.PapierGroottes;
if (!isEersteKeer)
{
SavePrinterSettings();
}
}
}
private void SavePrinterSettings()
{
if (GeselecteerdePrinter != null && GeselecteerdeLade != null && GeselecteerdePapiergrootte != null)
{
Settings.Default.Printer1 = GeselecteerdePrinter;
Settings.Default.Lade1 = GeselecteerdeLade;
Settings.Default.Papiergrootte1 = GeselecteerdePapiergrootte;
Settings.Default.Save();
}
}
private void LoadPrinterSettings()
{
GeselecteerdePrinter = Settings.Default.Printer1;
GeselecteerdeLade = Settings.Default.Lade1;
GeselecteerdePapiergrootte = Settings.Default.Papiergrootte1;
}
}
And the XAML of the printer combobox:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:c="clr-namespace:ControlLib.uPrinterCombo"
xmlns:vm="clr-namespace:ControlLib.uPrinterCombo"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" x:Class="ControlLib.uPrinterCombo.uPrinterCombo"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
>
<UserControl.Resources>
<c:PrinterViewModel x:Key="PrinterViewModel"/>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource PrinterViewModel}"/>
</UserControl.DataContext>
<Grid>
<ComboBox x:Name="cmbPrinters" HorizontalAlignment="Left" Margin="17,20,0,0" VerticalAlignment="Top" Width="120" ItemsSource="{Binding Path=Printers, Mode=TwoWay}" DisplayMemberPath="Naam" SelectedItem="{Binding GeselecteerdePrinter}"/>
<ComboBox x:Name="cmbPapierGroottes" HorizontalAlignment="Left" Margin="17,50,0,0" VerticalAlignment="Top" Width="120" ItemsSource="{Binding Path=Papiergroottes, Mode=TwoWay}" DisplayMemberPath="Naam" SelectedItem="{Binding GeselecteerdePapiergrootte}"/>
<ComboBox x:Name="cmbPapierLades" HorizontalAlignment="Left" Margin="17,80,0,0" VerticalAlignment="Top" Width="120" ItemsSource="{Binding Path=Lades, Mode=TwoWay}" DisplayMemberPath="Naam" SelectedItem="{Binding GeselecteerdeLade}"/>
</Grid>
</UserControl>
Thanks in advance to anyone trying to help. I am sure the binding is working, it just doesn't work when I read the properties from the Settings.
The Properties change when loading the Settings, the selectedItem is just not updating eventhough the source of the combobox does update according to the Selected Printer loaded from Settings.
Edit: the code where the Collections are initialised:
namespace ControlLib.uPrinterCombo
{
public class Printer : ObservableObject
{
private string _naam;
private ObservableCollection<Lade> _lades;
private ObservableCollection<Papiergrootte> _papiergroottes;
public string Naam
{
get { return _naam; }
set { Set<string>(() => this.Naam, ref _naam, value); }
}
public ObservableCollection<Lade> Lades
{
get { return _lades; }
set { Set<ObservableCollection<Lade>>(() => this.Lades, ref _lades, value); }
}
public ObservableCollection<Papiergrootte> PapierGroottes
{
get { return _papiergroottes; }
set { Set<ObservableCollection<Papiergrootte>>(() => this.PapierGroottes, ref _papiergroottes, value); }
}
public static ObservableCollection<Printer> GetPrinters()
{
ObservableCollection<Printer> printers = new ObservableCollection<Printer>();
PrintServer localPrintServer = new PrintServer();
PrintQueueCollection printQueues = localPrintServer.GetPrintQueues(new[] { EnumeratedPrintQueueTypes.Local, EnumeratedPrintQueueTypes.Connections });
var printerQueues = (from printer in printQueues
select printer).ToList();
foreach (var printerQueue in printerQueues)
{
Printer p = new Printer();
XmlPrinterUtility util = new XmlPrinterUtility(printerQueue);
ObservableCollection<Lade> lades = new ObservableCollection<Lade>();
ObservableCollection<Papiergrootte> papiergroottes = new ObservableCollection<Papiergrootte>();
foreach (var lade in util.GetPrinterInfo(XmlPrinterUtility.PrinterInfo.papierLade))
{
lades.Add(new Lade(lade.Key, lade.Value));
}
foreach (var papiergrootte in util.GetPrinterInfo(XmlPrinterUtility.PrinterInfo.papierGrootte))
{
papiergroottes.Add(new Papiergrootte(papiergrootte.Key, papiergrootte.Value));
}
p.Lades = lades;
p.PapierGroottes = papiergroottes;
p.Naam = printerQueue.Name;
printers.Add(p);
}
return printers;
}
}
}

wpf Datagrid: throw duplicated entry

In my application I want to validate if the user enter an item which is already exist on the DataGrid when I enter a new Item on the cell. I validate my business object using IDataErrorInfo.
My object is as follows:
class clsProducts : INotifyPropertyChanged, IDataErrorInfo
{
private string _ProductName;
private decimal _PurchaseRate;
private int _AvailableQty;
private int _Qty;
private decimal _Amount;
#region Property Getters and Setters
public string ProductName
{
get { return _ProductName; }
set
{
if (_ProductName != value)
{
_ProductName = value;
OnPropertyChanged("ProductName");
}
}
}
public decimal PurchaseRate
{
get { return _PurchaseRate; }
set
{
_PurchaseRate = value;
OnPropertyChanged("PurchaseRate");
}
}
public int AvailableQty
{
get { return _AvailableQty; }
set
{
_AvailableQty = value;
OnPropertyChanged("AvailableQty");
}
}
public int Qty
{
get { return _Qty; }
set
{
_Qty = value;
this._Amount = this._Qty * this._PurchaseRate;
OnPropertyChanged("Qty");
OnPropertyChanged("Amount");
}
}
public decimal Amount
{
get { return _Amount; }
set
{
_Amount = value;
OnPropertyChanged("Amount");
}
}
#endregion
#region IDataErrorInfo Members
public string Error
{
get
{
StringBuilder error = new StringBuilder();
// iterate over all of the properties
// of this object - aggregating any validation errors
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(this);
foreach (PropertyDescriptor prop in props)
{
string propertyError = this[prop.Name];
if (!string.IsNullOrEmpty(propertyError))
{
error.Append((error.Length != 0 ? ", " : "") + propertyError);
}
}
return error.ToString();
}
}
public string this[string name]
{
get
{
string result = null;
if (name == "ProductName")
{
if (this._ProductName != null)
{
int count = Global.ItemExist(this._ProductName);
if (count == 0)
{
result = "Invalid Product "+this._ProductName;
}
}
}
else if (name == "Qty")
{
if (this._Qty > this._AvailableQty)
{
result = "Qty must be less than Available Qty . Avaialble Qty : " + this._AvailableQty;
}
}
return result;
}
}
#endregion
#region INotifyPropertyChanged Members
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
//// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
My xaml is :
<my:DataGrid Name="dgReceiveInventory" RowStyle="{StaticResource RowStyle}" ItemsSource="{Binding}" GotFocus="dgReceiveInventory_GotFocus" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserSortColumns="False" RowHeight="23" SelectionUnit="Cell" AutoGenerateColumns="False" Margin="12,84,10,52" BeginningEdit="dgReceiveInventory_BeginningEdit">
<my:DataGrid.Columns>
<!--0-Product Column-->
<my:DataGridTemplateColumn Header="Product Name" Width="200">
<my:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource TextBlockInError}" Text="{Binding ProductName,ValidatesOnDataErrors=True}" ></TextBlock>
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
<my:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox x:Name="txtbxProduct" Style="{StaticResource TextBoxInError}" Text="{Binding Path=ProductName,UpdateSourceTrigger=LostFocus,ValidatesOnDataErrors=True}" TextChanged="txtbxProduct_TextChanged" PreviewKeyDown="txtbxProduct_PreviewKeyDown" >
</TextBox>
</DataTemplate>
</my:DataGridTemplateColumn.CellEditingTemplate>
</my:DataGridTemplateColumn>
<!--1-Purchase Rate Column-->
<my:DataGridTextColumn Header="Purchase Rate" Width="100" Binding="{Binding PurchaseRate}" IsReadOnly="True"></my:DataGridTextColumn>
<!--2-Avaialable Qty Column-->
<my:DataGridTextColumn Header="AvailableQty" Binding="{Binding AvailableQty}" IsReadOnly="True" Visibility="Hidden"></my:DataGridTextColumn>
<!--3-Qty Column-->
<my:DataGridTextColumn Header="Qty" Binding="{Binding Qty,ValidatesOnExceptions=True,ValidatesOnDataErrors=True}" EditingElementStyle="{StaticResource TextBoxInError}">
</my:DataGridTextColumn>
<!--4-Amount Column-->
<my:DataGridTextColumn Header="Amount" Width="100" Binding="{Binding Amount}" ></my:DataGridTextColumn>
</my:DataGrid.Columns>
</my:DataGrid>
Now I want to show the user ,if he made a duplicate entry in the datagrid cell how to do this ?
You can't do this functionality in your model or data type class using the IDataErrorInfo interface because you won't have access to the other objects there. Instead, you'll have to do it in your view model. However, you can report the error using that interface. I have extended its functionality by adding a property into my data type base class:
public virtual ObservableCollection<string> ExternalErrors
{
get { return externalErrors; }
}
As you can see, mine deals with multiple errors, but you can easily change this to:
public virtual string ExternalError
{
get { return externalError; }
}
Then I 'plug' this into my Errors property:
public override ObservableCollection<string> Errors
{
get
{
errors = new ObservableCollection<string>();
errors.AddUniqueIfNotEmpty(this["Name"]);
errors.AddUniqueIfNotEmpty(this["EmailAddresses"]);
errors.AddUniqueIfNotEmpty(this["StatementPrefixes"]);
errors.AddRange(ExternalErrors);
return errors;
}
}
Again, I have adapted this interface to return multiple errors, but you can change this to:
public override string Error
{
get
{
error = string.Empty;
if ((error = this["Name"])) != string.Empty) return error;
if ((error = this["EmailAddresses"])) != string.Empty) return error;
if ((error = this["Name"])) != string.Empty) return error;
if (ExternalError != string.Empty) return ExternalError;
return error;
}
}
Incidentally, it is far more efficient to call just the indexers that you are actually validating rather than your example of calling all properties using reflection. However, that is your choice.
So now that we have this ExternalError property, we can use it to display external error messages from your view model (create a class that contains a collection property to bind to the DataGrid.ItemsSource property).
If you are using ICommand objects, then you can put this code into the CanExecute method of your Save command:
public bool CanSave(object parameter)
{
clsProducts instance = (clsProducts)parameter;
instance.ExternalError = YourCollectionProperty.Contains(instance) ?
"The values must be unique" : string.Error;
// Perform your can save checks here
}
Please note that you will need to implement the Equals method in your data type object for this to work. There are many similar ways to achieve this and I trust that from this example you will be able to work one out that works for you.

wpf datagrid :list all errors when save click

I am creating a WPF application that will use IDataErrorInfo data validation, which my business objects implement. Now I want to list all the validation errors to the user in a messagebox when the user clicks the save button. How to acheive this?
My Datagrid is:
<my:DataGrid Name="dgReceiveInventory" RowStyle="{StaticResource RowStyle}" ItemsSource="{Binding}" GotFocus="dgReceiveInventory_GotFocus" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserSortColumns="False" RowHeight="23" SelectionUnit="Cell" AutoGenerateColumns="False" Margin="12,84,10,52" BeginningEdit="dgReceiveInventory_BeginningEdit">
<my:DataGrid.Columns>
<!--0-Product Column-->
<my:DataGridTemplateColumn Header="Product Name" Width="200">
<my:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource TextBlockInError}" Text="{Binding ProductName,ValidatesOnDataErrors=True}" ></TextBlock>
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
<my:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox x:Name="txtbxProduct" Text="{Binding Path=ProductName,UpdateSourceTrigger=LostFocus,ValidatesOnDataErrors=True}" TextChanged="txtbxProduct_TextChanged" PreviewKeyDown="txtbxProduct_PreviewKeyDown" >
</TextBox>
</DataTemplate>
</my:DataGridTemplateColumn.CellEditingTemplate>
</my:DataGridTemplateColumn>
<!--1-Purchase Rate Column-->
<my:DataGridTextColumn Header="Purchase Rate" Width="100" Binding="{Binding PurchaseRate}" IsReadOnly="True"></my:DataGridTextColumn>
<!--2-Avaialable Qty Column-->
<my:DataGridTextColumn Header="Stock" Binding="{Binding AvailableQty}" IsReadOnly="True" Visibility="Hidden"></my:DataGridTextColumn>
<!--4-Amount Column-->
<my:DataGridTextColumn Header="Amount" Width="100" Binding="{Binding Amount}" ></my:DataGridTextColumn>
</my:DataGrid.Columns>
</my:DataGrid>
My Object is:
class clsProducts : INotifyPropertyChanged, IDataErrorInfo
{
private string _ProductName;
private decimal _PurchaseRate;
private int _AvailableQty;
private int _Qty;
private decimal _Amount;
#region Property Getters and Setters
public string ProductName
{
get { return _ProductName; }
set
{
if (_ProductName != value)
{
_ProductName = value;
OnPropertyChanged("ProductName");
}
}
}
public decimal PurchaseRate
{
get { return _PurchaseRate; }
set
{
_PurchaseRate = value;
OnPropertyChanged("PurchaseRate");
}
}
public int AvailableQty
{
get { return _AvailableQty; }
set
{
_AvailableQty = value;
OnPropertyChanged("AvailableQty");
}
}
public int Qty
{
get { return _Qty; }
set
{
_Qty = value;
this._Amount = this._Qty * this._PurchaseRate;
OnPropertyChanged("Qty");
OnPropertyChanged("Amount");
}
}
public decimal Amount
{
get { return _Amount; }
set
{
_Amount = value;
OnPropertyChanged("Amount");
}
}
#endregion
#region IDataErrorInfo Members
public string Error
{
get
{
return "";
}
}
public string this[string name]
{
get
{
string result = null;
if (name == "ProductName")
{
if (this._ProductName != null)
{
int count = Global.ItemExist(this._ProductName);
if (count == 0)
{
result = "Invalid Product";
}
}
}
else if (name == "Qty")
{
if (this._Qty > this._AvailableQty)
{
result = "Qty must be less than Available Qty . Avaialble Qty : " + this._AvailableQty;
}
}
return result;
}
}
#endregion
#region INotifyPropertyChanged Members
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
//// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
I don't clear understand why you want to do so but as example you can enumerate rows and call Validate method on your own like this:
private void Save_Click(object sender, RoutedEventArgs e) {
// create validation object
RowDataInfoValidationRule rule = new RowDataInfoValidationRule();
StringBuilder builder = new StringBuilder();
// enumerate all rows
for (int i = 0; i < dgReceiveInventory.Items.Count; i++) {
DataGridRow row = (DataGridRow) dgReceiveInventory.ItemContainerGenerator.ContainerFromIndex(i);
// validate rule
ValidationResult res = rule.Validate(row.BindingGroup, null);
if (!res.IsValid) {
// collect errors
builder.Append(res.ErrorContent);
}
}
//show message box
MessageBox.Show(builder.ToString());
}
If you have
<DataGrid>
<DataGrid.RowValidationRules>
<local:RowDataInfoValidationRule ValidationStep="UpdatedValue" />
</DataGrid.RowValidationRules>
...
You could use Validation.Error Attached Event
<Window Validation.Error="Window_Error">
to save all validation errors for bindings with NotifyOnValidationError set to true
Text="{Binding ProductName, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"
in a List
public List<ValidationError> ValidationErrors = new List<ValidationError>();
private void Window_Error(object sender, ValidationErrorEventArgs e)
{
if (e.Action == ValidationErrorEventAction.Added)
ValidationErrors.Add(e.Error);
else
ValidationErrors.Remove(e.Error);
}
and then show the list entries in a MessageBox in save button click handler.

Categories

Resources