MVVM newbie looking for a bit of guidance about datagrid binding. I have difficulty with setting up the properties where I can see that the contents of a datagrid cell has changed. I started out simple and am trying to set up an updateable country list with currency codes. Is there a way I can bind my grid column cells to properties in the viewmodel? So far I have just introduced a property for CurrencyCode, but I never execute the setter. What am I doing wrong?
Model:
using System.Linq;
using System.Collections.ObjectModel;
namespace AdminTool.DataModel.Repositories
{
public class AccountingRepository : BaseRepository
{
private AdminToolEntities entities = new AdminToolEntities();
// Country List
public ObservableCollection<CountryData> GetCountryList()
{
var result = (from c in entities.Countries
from u in entities.Currencies.Where(u => u.ID == c.CurrencyID).DefaultIfEmpty()
select new CountryData { IsChanged = false,
ID = c.ID,
CountryName = c.CountryName,
CountryCodeISO = c.CountryCodeISO,
CurrencyCode = u.CurrencyCode})
.OrderBy(o => o.CountryName)
.ToList();
return new ObservableCollection<CountryData>(result);
}
public void SaveCountryList(ObservableCollection<CountryData> countryList)
{
var result = (from l in countryList
from c in entities.Countries.Where(c => c.ID == l.ID)
from u in entities.Currencies.Where(u => u.CurrencyCode == l.CurrencyCode).DefaultIfEmpty()
where l.IsChanged
select l)
.ToList();
foreach (CountryData cd in result)
{
Country c = (Country)entities.Countries.Where(l => l.ID == cd.ID).FirstOrDefault();
if (c == null) // new entry
{
c = new Country();
entities.Countries.Add(c);
}
c.CountryName = cd.CountryName;
c.CountryCodeISO = cd.CountryCodeISO;
c.Currency = entities.Currencies.Where(u => u.CurrencyCode == cd.CurrencyCode).FirstOrDefault();
}
entities.SaveChanges();
}
}
/// <summary>
/// Data structures
/// </summary>
public class CountryData
{
public int ID { get; set; }
public string CountryName { get; set; }
public string CountryCodeISO { get; set; }
public string CurrencyCode { get; set; }
public bool IsChanged { get; set; }
}
}
View:
<Window x:Class="AdminTool.Desktop.View.CountryList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:AdminTool.Desktop.ViewModel"
Title="AdminTool" Height="600" Width="500">
<Window.DataContext>
<vm:AccountingViewModel/>
</Window.DataContext>
<Grid>
<DataGrid Name="dgCountryList"
ItemsSource="{Binding CountryList, Mode=TwoWay}"
SelectedItem="{Binding Path=SelectedCountry, Mode=TwoWay}"
HorizontalAlignment="Left" VerticalAlignment="Top" Height="450" Width="450" Margin="20,20,0,0"
AutoGenerateColumns="False"
SelectionMode="Single"
SelectionUnit="FullRow"
GridLinesVisibility="All"
CanUserDeleteRows="True"
CanUserAddRows="True">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChanged}" Value="true" >
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<!--<DataGridTextColumn Header="ID" Binding="{Binding ID}" Width="50" />-->
<DataGridTextColumn Header="Country" Binding="{Binding Path=CountryName, Mode=TwoWay}" Width="250" />
<DataGridTextColumn Header="ISO Code" Binding="{Binding Path=CountryCodeISO, Mode=TwoWay}" Width="80" />
<DataGridTextColumn Header="Currency" Binding="{Binding Path=CurrencyCode, Mode=TwoWay}" Width="80" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Save" Command="{Binding Path=SaveCommand}" IsEnabled="{Binding Path=UpdateButtonEnabled}" HorizontalAlignment="Left" Margin="223,512,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Cancel" Command="{Binding Path=CancelCommand}" HorizontalAlignment="Left" Margin="343,512,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
ViewModel:
using System.Collections.ObjectModel;
using AdminTool.DataModel.Repositories;
using AdminTool.Helpers;
namespace AdminTool.Desktop.ViewModel
{
public class AccountingViewModel : ViewModelBase
{
private ObservableCollection<CountryData> _countryList;
public ObservableCollection<CountryData> CountryList
{
get { return _countryList; }
set
{
_countryList = value;
RaisePropertyChanged("CountryList");
}
}
private CountryData _selectedCountry;
public CountryData SelectedCountry
{
get { return _selectedCountry; }
set
{
_selectedCountry = value;
RaisePropertyChanged("SelectedCountry");
}
}
private string currencyCode;
public string CurrencyCode
{
get { return currencyCode; }
set {
currencyCode = value;
SelectedCountry.IsChanged = true;
}
}
private bool updateButtonEnabled = false;
public bool UpdateButtonEnabled
{
get { return updateButtonEnabled; }
set { updateButtonEnabled = value;
RaisePropertyChanged("UpdateButtonEnabled");
}
}
#region Commands -----------------------------------------------------------------------------------
public RelayCommand SaveCommand { get; set; }
public RelayCommand CancelCommand { get; set; }
#endregion
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public AccountingViewModel()
{
SaveCommand = new RelayCommand(Save);
CancelCommand = new RelayCommand(Cancel);
AccountingRepository repo = new AccountingRepository();
CountryList = repo.GetCountryList();
}
#region Public methods -----------------------------------------------------------------------------
void Save(object parameter)
{
AccountingRepository repo = new AccountingRepository();
repo.SaveCountryList(CountryList);
}
void Cancel(object parameter)
{
}
#endregion
}
}
Thanks in advance for any help.
you have to handle the property changed event:
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
You need to make changes in model. Implement notify interface as given below.
Model - With name property being updated
public class CountryData : INotifyPropertyChanged
{
private string countryName;
public string CountryName
{
get { return countryName; }
set
{
countryName = value;
RaisePropertyChanged("CountryName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
}
View - Use UpdateSourceTrigger=PropertyChanged with Bindings in grid.
<Grid>
<DataGrid Name="dgCountryList"
ItemsSource="{Binding CountryList, Mode=TwoWay}"
SelectedItem="{Binding Path=SelectedCountry, Mode=TwoWay}"
HorizontalAlignment="Left" VerticalAlignment="Top" Height="450" Width="450" Margin="20,20,0,0"
AutoGenerateColumns="False"
SelectionMode="Single"
SelectionUnit="FullRow"
GridLinesVisibility="All"
CanUserDeleteRows="True"
CanUserAddRows="True">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChanged}" Value="true" >
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<!--<DataGridTextColumn Header="ID" Binding="{Binding ID}" Width="50" />-->
<DataGridTextColumn Header="Country" Binding="{Binding Path=CountryName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="250" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Save" Command="{Binding Path=SaveCommand}" IsEnabled="{Binding Path=UpdateButtonEnabled}" HorizontalAlignment="Left" Margin="223,512,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Cancel" Command="{Binding Path=CancelCommand}" HorizontalAlignment="Left" Margin="343,512,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
Related
I have a class Employee :Person
Class Employee
[Serializable]
public enum Education
{
secondary, specialized_secondary, high
}
[Serializable]
public enum MarriageStatus
{
single, married, divorced
}
[Serializable]
public class Employee : Person, INotifyPropertyChanged
{
private Education _teaching;
private MarriageStatus _status;
private string _photoPath;
public Education Teaching
{
get { return _teaching; }
set
{
_teaching = value;
OnPropertyChanged("Teaching");
}
}
public MarriageStatus Status
{
get { return _status; }
set
{
_status = value;
OnPropertyChanged("Status");
}
}
public string PhotoPath
{
get { return _photoPath; }
set
{
_photoPath = value;
OnPropertyChanged("Status");
}
}
private ObservableCollection<Employee> _employeesList = null;
public new event PropertyChangedEventHandler PropertyChanged;
public new void OnPropertyChanged([CallerMemberName]string prop = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
public ObservableCollection<Employee> EmployeesList
{
get
{
if (_employeesList != null)
{
return _employeesList;
}
_employeesList = new ObservableCollection<Employee>();
_employeesList.Add(new Employee()
{
Id = 1,
FirstName = "Igor",
LastName = "Krivonos",
DateBirthday = new DateTime(1999, 8, 15),
INN = "111111111",
Teaching = Education.high,
Status = MarriageStatus.married,
PhotoPath = "Photo/IgorKrivonos.jpg"
});
return _employeesList;
}
}
}
I have realized INotifyPropety event but I am not sure about this. I am new in this field and trying everything that might help. All my Employee informaiton is shown in DataGrid When I change any cell there it will not show in debuger any changes. I want to serialize it to save changes. Why my changing is not working? Sorry for my English.
Here is my xaml code:
Window x:Class="Employee_DataGrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Employee_DataGrid"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:model="clr-namespace:Employee_DataGrid.Model"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<model:Employee x:Key="employees"></model:Employee>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="status">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="model:MarriageStatus" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="education">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="model:Education" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!--Button for serialization-->
<Button Grid.Column="1" Click="Button_Click" >
<TextBlock TextAlignment="Center" FontSize="35" FontFamily="TimesNewRoman" FontWeight="Bold" Width="30" TextWrapping="Wrap">Save Data</TextBlock>
</Button>
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False"
ItemsSource="{Binding Source={StaticResource employees}, Path=EmployeesList}">
<!--DataGrid Columns-->
<DataGrid.Columns>
<!--DataGridTextColumn for Full names and Inn-->
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"></DataGridTextColumn>
<DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"></DataGridTextColumn>
<DataGridTextColumn Header="INN" Binding="{Binding INN}"></DataGridTextColumn>
<!--DataGridComboBoxColumn for Marriage Status-->
<DataGridComboBoxColumn Header="Status"
ItemsSource="{Binding Source={StaticResource status}}"
SelectedValueBinding="{Binding Status}" >
</DataGridComboBoxColumn>
<!--DataGridTemplateColumn for Birthday-->
<DataGridTemplateColumn Header="Date Birthday">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding DateBirthday, StringFormat='MM.dd.yyyy'}"></DatePicker>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--DataGridComboBoxColumn for Education-->
<DataGridComboBoxColumn Header="Education"
ItemsSource="{Binding Source={StaticResource education}}"
SelectedValueBinding="{Binding Teaching}" >
</DataGridComboBoxColumn>
<!--DataGridTemplateColumn for Photos-->
<DataGridTemplateColumn Header="Employees Photos">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image DockPanel.Dock="Right"
HorizontalAlignment="Right"
Width="70"
Source="{Binding Path=PhotoPath}"></Image>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
I inherit some propeties from Person Class Here is my Person Class:
[Serializable]
public class Person: INotifyPropertyChanged
{
private int _id;
private string _firstName;
private string _lastName;
private System.DateTime _dateBirthday;
private string _inn;
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
public System.DateTime DateBirthday
{
get { return _dateBirthday; }
set
{
_dateBirthday = value;
OnPropertyChanged("DateBirthday");
}
}
public string INN
{
get { return _inn; }
set
{
_inn = value;
OnPropertyChanged("INN");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
Here I have realized INotifyPropety as well.
Main Window class
[Serializable]
public partial class MainWindow : Window
{
public Employee Employee { get; set; }
public MainWindow()
{
InitializeComponent();
Employee = new Employee();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Helpers.Serializing(Employee.EmployeesList, "employees.bin");
}
}
You are creating two instances of Employee, one in the code-behind and another one in the XAML markup.
You should bind to the one that you create in the code-behind:
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False"
ItemsSource="{Binding Path=Employee.EmployeesList,
RelativeSource={RelativeSource AncestorType=Window}}">
...and remove this:
<model:Employee x:Key="employees"></model:Employee>
I am beginner at programing. I developed a WPF application in C# and I use Entity Framework and Devexpress components. I have a GridControl component dgv_SupportComponent. I want to refresh dgv_SupportComponent when I click btn_Void.
XAML markup is :
<Window.Resources>
<dxcore:EntityCollectionViewSource x:Key="EntityCollectionViewSource" Culture="en-US" ContextType="{x:Type HadishDataModelLayer:HadishDataBaseEntities}" CollectionViewType="{x:Type CollectionView}" Path="vw_SupportComponent">
<dxcore:DesignDataManager.DesignData>
<dxcore:DesignDataSettings RowCount="5"/>
</dxcore:DesignDataManager.DesignData>
</dxcore:EntityCollectionViewSource>
</Window.Resources>
<Grid Margin="0,0,-0.4,0" >
<dxg:GridControl x:Name="dgv_SupportComponent" AutoGenerateColumns="None" EnableSmartColumnsGeneration="True" Margin="0,-1,0.4,0.4" SelectionMode="Cell" AllowLiveDataShaping="True" ItemsSource="{Binding Data, Source={StaticResource EntityCollectionViewSource} , UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True }" >
<dxg:GridControl.View>
<dxg:TableView x:Name="tlv_support" ShowTotalSummary="True" AllowEditing="True" AllowPerPixelScrolling="True" RowUpdated="tlv_support_RowUpdated" EditFormPostMode="Immediate" AllowCascadeUpdate="True" AllowGroupSummaryCascadeUpdate="True" ShowAutoFilterRow="True" ShowSearchPanelFindButton="True" ShowSearchPanelMRUButton="True" ShowSearchPanelNavigationButtons="True" SearchPanelAllowFilter="True" SearchColumns="ComponentSt" ShowSearchPanelMode="Never" SearchString="Active" SearchPanelHighlightResults="False" NavigationStyle="Cell" ShowGroupFooters="True" EnableImmediatePosting="True" ShowCriteriaInAutoFilterRow="True" ShowCheckBoxSelectorColumn="True" NewItemRowPosition="Top" IsSynchronizedWithCurrentItem="True" />
</dxg:GridControl.View>
<dxg:GridColumn x:Name="CulComponentID" FieldName="ComponentID" IsSmart="True" AllowEditing="True" />
<dxg:GridColumn FieldName="ComponentName" IsSmart="True" AllowEditing="True" FilterPopupMode="CheckedList" />
<dxg:GridColumn FieldName="ComponentWeight" IsSmart="True" AllowEditing="True" />
<dxg:GridColumn FieldName="ComponentWarehouseName" IsSmart="True" />
<dxg:GridColumn FieldName="ApprovedBy" IsSmart="True"/>
<dxg:GridColumn FieldName="ComponentWaste" />
<dxg:GridColumn FieldName="ComponentSt" />
</dxg:GridControl>
</Grid>
I want to refresh datagrid when I click on btn_Void.
This code has updated data to SQL but datagrid doesn't refresh.
My code is :
private void btn_Void_ItemClick(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e)
{
foreach (int rowHandle in tlv_support.GetSelectedRowHandles())
{
int supid = Convert.ToInt32(dgv_SupportComponent.GetCellValue(rowHandle, "ComponentID"));
db.SPSupportComponentState(supid, HadishLogicLayer.HadishCode.gCompanyCode, false, HadishLogicLayer.HadishCode.gUserID);
}
dgv_SupportComponent.RefreshData(); /// this code dose not refresh datagrid
}
What if you try some MVVM here?
Your Grid
<ListView Name="MyListView" ItemsSource="{Binding AllItems}">
<ListView.View>
<GridView>
<GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id}"/>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Approuved By" DisplayMemberBinding="{Binding Approver}"/>
</GridView>
</ListView.View>
</ListView>
Button
<Button Command="{Binding UpdateDataCommand}" Content="Refresh All" Margin="10" Width="100"/>
ViewModel
public abstract class ViewModelBase : INotifyPropertyChanged
{
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class Item : ViewModelBase, INotifyPropertyChanged
{
private string _approver;
public int Id { get; set; }
public string Name { get; set; }
public string Approver
{
get { return _approver; }
set
{
if (_approver != value)
{
_approver = value;
RaisePropertyChanged(nameof(Approver));
}
}
}
}
public class MyViewModel : ViewModelBase
{
public MyViewModel()
{
UpdateDataCommand = new RelayCommand(_ => UpdateAll());
}
public ObservableCollection<Item> AllItems { get; set; } = new ObservableCollection<Item>();
public ICommand UpdateDataCommand { get; set; }
private void UpdateAll()
{
//Fetch from DB
var items = new[]
{
new Item {Id=1, Name="Item 1", Approver="Lance"},
new Item {Id=2, Name="Item 2", Approver="John"}
};
AllItems.Clear();
foreach (var item in items)
{
AllItems.Add(item);
}
}
}
** RelayCommand ** (credit to Josh Smith)
https://gist.github.com/schuster-rainer/2648922
Now let's assume you need to update only the selected item
<Button Command="{Binding UpdateSelectedCommand}" CommandParameter="{Binding ElementName=MyListView, Path=SelectedItem}" Content="Refresh Selected" Margin="10" Width="100">
<Button.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyListView, Path=SelectedItems.Count}" Value="0">
<Setter Property="Button.IsEnabled" Value="False"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
In the VM constructor:
UpdateSelectedCommand = new RelayCommand(selected => UpdateSelected(selected));
UpdateSelected implementation:
private void UpdateSelected(object selected)
{
var selectedItem = selected as Item;
if (selectedItem != null)
{
var item = AllItems.SingleOrDefault(i => i.Id == selectedItem.Id);
if (item != null)
{
item.Approver = "New Approver";
}
}
}
Voilà!
To refresh GridControl bound to EntityCollectionViewSource, it's necessary to create a new instance of EntityCollectionViewSource. Please take a look at the How to work with WPF DXGrid data support ticket where this topic was discussed.
Im working on a small wpf project to understand c# and mvvm better.
Im currently having a problem, that I dont know how to pass data between two pages.
What I want to do:
I have two pages, they are the content of my MainWindow.
In PageViewCustomers I have a DatagridView(there are acutally two, but I just need one for this task), when I double click on a row, I open PageAddStaticIPAddress. Now I want to be able to access the row I double clicked in PageViewCustomers in the PageAddStaticIPAddress. I thought about passing the selected row as a CommandParameter in the xaml of PageViewCustomers.
But now I dont know how to access it from PageAddStaticIPAddress..
I have seen some solutions, but they all solve it by having Code in the View. I try to have as less code in the view as possible and solve it by only using Bindings,commands and my viewmodels.
Here is my current code:
PageViewCustomers.xaml
<Page x:Class="StaticIPConfiger.PageViewCustomers"
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:StaticIPConfiger"
xmlns:localvm="clr-namespace:StaticIPConfiger.Modelle"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500"
Title="Kundenübersicht">
<Page.DataContext>
<localvm:VModel />
</Page.DataContext>
<Grid DataContext="{Binding}" >
<DataGrid IsReadOnly="True" Name="dgCustomers" ItemsSource="{Binding AlleKunden}" AutoGenerateColumns="False" Margin="0,0,0,197">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=c_name}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="Standort" Binding="{Binding Path=l_name}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding UpdateLocations}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
<DataGrid IsReadOnly="True" Name="dg2Customers" ItemsSource="{Binding AlleStandorte}" AutoGenerateColumns="False" Margin="0,168,0,10" >
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding OpenAddNewIPAddress}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=SelectedItem}"/>
</DataGrid.InputBindings>
<DataGrid.Columns>
<DataGridTextColumn Header="StandortId" Binding="{Binding Path=l_id}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="Standort" Binding="{Binding Path=l_name}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Class VModel.cs (VModel of PageViewCustomers)
namespace StaticIPConfiger.Modelle {
public class VModel : INotifyPropertyChanged {
DataView _alleKunden;
public VModel() {
DataTable dt = new DataTable();
using (SqlConnection connection = new SqlConnection("Data Source=.\\WERBASWEB;Initial Catalog=customer;Persist Security Info=True;User ID=sa;Password=Dotnet123!")) {
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = new SqlCommand("Select c_id,c_name, l_id,l_name, a_town, a_pcode, a_street from dbo.[AllCustomers]", connection);
adapter.Fill(dt);
}
AlleKunden = dt.DefaultView;
AlleStandorte = null;
}
public DataView AlleKunden { get; private set; }
public DataView AlleStandorte { get { return _alleKunden; } private set { _alleKunden = value;
RaisePropertyChanged("AlleStandorte");
} }
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Methods
private void RaisePropertyChanged(string propertyName) {
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Commands
void UpdateLocationView(object parameter) {
DataRowView selectedRow = (DataRowView)parameter;
string customerid = selectedRow.Row[0].ToString();
string locationid = selectedRow.Row[2].ToString();
DataTable dt = SelectStatements.SelectLocationsForCustomer(locationid,customerid);
AlleStandorte = dt.DefaultView;
Console.WriteLine("Test");
}
bool CanUpdateLocationView(object parameter) {
return true;
}
public ICommand UpdateLocations { get { return new RelayCommand<object>(param => this.UpdateLocationView(param), param => this.CanUpdateLocationView(param)); } }
void OpenIPAddress() {
System.Windows.Application.Current.MainWindow.Content = new AddStaticIPAddress();
}
bool CanOpenIPAddress() {
return true;
}
public ICommand OpenAddNewIPAddress { get { return new RelayCommand(OpenIPAddress,CanOpenIPAddress); } }
#endregion
}
Page AddStaticIPAddress:
<Page x:Class="StaticIPConfiger.AddStaticIPAddress"
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:StaticIPConfiger"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500"
Title="AddStaticIPAddress">
<Page.DataContext>
<local:AddCustomerVModel/>
</Page.DataContext>
<Grid>
<Label x:Name="lbl_net" Content="Netz:" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="lbl_subnetmask" Content="Subetnmaske:" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,36,0,0"/>
<Label x:Name="label_ipaddress" Content="IP Addresse:" HorizontalAlignment="Left" Margin="10,62,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="text_net" Text="" HorizontalAlignment="Left" Height="23" Margin="103,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="text_subetnmask" Text="" HorizontalAlignment="Left" Height="23" Margin="103,36,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="text_ipaddress" Text="" HorizontalAlignment="Left" Height="23" Margin="103,62,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label x:Name="lbl_description" Content="Bezeichnung:" HorizontalAlignment="Left" Margin="10,85,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="text_description" Text="" HorizontalAlignment="Left" Height="23" Margin="103,88,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Button x:Name="btn_save_add_ip" Content="IP Addresse hinzufügen" Command ="" HorizontalAlignment="Left" Margin="10,129,0,0" VerticalAlignment="Top" Width="133"/>
<Button x:Name="btn_abort_add_ip" Content="Verwerfen" HorizontalAlignment="Left" Margin="148,129,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
AddIPAddressVModel:
namespace StaticIPConfiger.Modelle {
class AddIPAddressVModel : INotifyPropertyChanged {
IPAddress _ipaddress;
public AddIPAddressVModel() {
_ipaddress = new IPAddress { Net = "", Subetnmask = "", IpAddress = ""};
}
public IPAddress IPAddress {
get { return _ipaddress; }
set { _ipaddress = value; }
}
#region"Get/Set"
public int Id {
get { return IPAddress.Id; }
set {
IPAddress.Id = value;
RaisePropertyChanged("IPAddress");
}
}
public String Net {
get { return IPAddress.Net; }
set {
IPAddress.Net= value;
RaisePropertyChanged("Net");
}
}
public string Subnetmask {
get { return IPAddress.Subetnmask; }
set {
IPAddress.Subetnmask = value;
RaisePropertyChanged("Subetnmask");
}
}
public string IpAddress {
get { return IPAddress.IpAddress; }
set {
IPAddress.IpAddress = value;
RaisePropertyChanged("IPAddress");
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Methods
private void RaisePropertyChanged(string propertyName) {
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
I hope you understand my Problem ;-)
If you have any questions, let me know.
Thanks!
EDIT:
How I open AddIPAddress
I did bind a Command to the DataGrid, where I have a Command OpenAddNewIPAddress:
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding OpenAddNewIPAddress}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=SelectedItem}"/>
</DataGrid.InputBindings>
The command looks like this:
void OpenIPAddress() {
System.Windows.Application.Current.MainWindow.Content = new AddStaticIPAddress();
}
bool CanOpenIPAddress() {
return true;
}
public ICommand OpenAddNewIPAddress { get { return new RelayCommand(OpenIPAddress,CanOpenIPAddress); } }
That another question I had. When I set the content of my MainWindow (System.Windows.Application.Current.MainWindow.Content = new AddStaticIPAddress();) with a new Page, the menubar gets lost... Is there a better way to do this?
You could inject the AddIPAddressVModel with the command parameter. Something like this:
<DataGrid IsReadOnly="True" Name="dg2Customers" ItemsSource="{Binding AlleStandorte}" AutoGenerateColumns="False" Margin="0,168,0,10" >
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding OpenAddNewIPAddress}"CommandParameter="{Binding SelectedItem}"/>
</DataGrid.InputBindings>
<DataGrid.Columns>
<DataGridTextColumn Header="StandortId" Binding="{Binding Path=l_id}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="Standort" Binding="{Binding Path=l_name}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
void OpenIPAddress(object parameter)
{
System.Windows.Application.Current.MainWindow.DataContext = new AddIPAddressVModel(parameter as DataRowView)
System.Windows.Application.Current.MainWindow.Content = new AddStaticIPAddress();
}
bool CanOpenIPAddress(object parameter)
{
return true;
}
public ICommand OpenAddNewIPAddress { get { return new RelayCommand<object>(OpenIPAddress, CanOpenIPAddress); } }
public AddIPAddressVModel(DataRowView drv) {
_ipaddress = new IPAddress { Net = "", Subetnmask = "", IpAddress = ""};
_drv = drv; //keep a reference to the clicked DataRowView
}
Remove this from the AddStaticIPAddress view:
<Page.DataContext>
<local:AddCustomerVModel/>
</Page.DataContext>
The Page should inherit its DataContext. Setting the DataContext in the XAML markup is only useful in very simple scenarios when the view model has no dependencies.
Try to put a field in AddStaticIPAddress:
public DataGridRow Row { get; set; }
So you can access it from VModel.
E.g. you can initialize AddIPAddressVModelon DataGridSelectionChanged action:
private void dgCustomers_OnSelectionChanged(...)
{
AddIPAddressVModel addip = new AddIPAddressVModel();
addip.Row = (DataGridRow) dgCustomers.SelectedItem;
addip.Show();
}
I have 3 columns . One with check box , One with text column and one column with drop down.
I am binding the entire table itemsource to StepTaskViewInfo.CurrentStep.ProjectTasks.Items . StepTaskViewInfo is a variable in my VM and others are nested in it. This works fine .
Only thing that doesnt work is the IsReadOnly property of the FIRST Columns. Am assuming this is some issue because my items source is different and the read only property is different in terms of level of nesting from view model.
For grid :
Items Source = StepTaskViewInfo -> CurrentStep -> ProjectTasks- >Items
For read only propety of each column(which doesnt work) :
IsReadOnly="{Binding StepTaskViewInfo.AreStepsTasksReadonly
StepTaskViewInfo => AreStepsTasksReadonly
<DataGrid RowHeaderWidth="0" x:Name ="TaskDataGrid" Margin="20,0,0,0" ItemsSource="{Binding StepTaskViewInfo.CurrentStep.ProjectTasks.Items}" AutoGenerateColumns="False"
CanUserSortColumns="False" HorizontalAlignment="Left" CanUserAddRows="False" SelectionChanged="TaskRowSelectionChanged"
ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto"
Background="White" BorderThickness ="0"
ScrollViewer.CanContentScroll="True" Height="240">
<DataGrid.Columns>
<DataGridTemplateColumn Width ="60" HeaderStyle="{StaticResource HeaderStyle}" Header="Selected" IsReadOnly="{Binding StepTaskViewInfo.AreStepsTasksReadonly,UpdateSourceTrigger=PropertyChanged }">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding IsSelected,UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsTaskEnabled,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
//Column 2
<DataGridTextColumn HeaderStyle="{StaticResource HeaderStyle}" Header="Tasks" Width ="*" Binding="{Binding Name}" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="AcceptsReturn" Value="true" />
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
//Column 3
<DataGridTemplateColumn HeaderStyle="{StaticResource HeaderStyle}" Header="Status" Width ="130">
<DataGridTemplateColumn.HeaderTemplate >
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="130">
<Label Content ="Status" HorizontalAlignment="Left" Margin ="0,0,0,0"/>
<ComboBox Name ="DefaultStatusComboBox" ItemsSource="{StaticResource Status}" Width="86" DropDownClosed="DefaultStatusComboBox_DropDownClosed" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Status, UpdateSourceTrigger=PropertyChanged}" Height ="26" VerticalAlignment="Top" IsReadOnly ="{Binding StatusIsReadOnly}"
IsEnabled ="{Binding IsSelected}" ItemsSource="{StaticResource Status}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
view model:
public class StepTaskViewModel : INavigationAware, INotifyPropertyChanged
{
private readonly IProjectWorkflowService projectWorkflowService;
private bool isVisible = true;
private readonly IUserService userService;
private string stageId;
private StepTaskViewInfo stepTaskViewInfo;
public StepTaskViewModel(IProjectWorkflowService projectWorkflowService, IUserService userService)
{
this.projectWorkflowService = projectWorkflowService;
this.userService = userService;
StepTaskViewInfo=new StepTaskViewInfo();
}
public StepTaskViewInfo StepTaskViewInfo
{
get { return stepTaskViewInfo; }
set
{
stepTaskViewInfo = value;
OnPropertyChanged();
}
}
// set current step - >load tasks - > set display names for each task --> set drop down source for current step
public string StageId
{
get { return stageId; }
set
{
stageId = value;
StepTaskViewInfo.PeerReview.StageId = stageId;
LoadData();
}
}
#region navigation
public void OnNavigatedTo(NavigationContext navigationContext)
{
StageId =(string) navigationContext.Parameters["StageId"] ;
IsVisible = true;
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
if (!IsVisible)
return;
IsVisible = false;
}
public bool IsVisible
{
get { return isVisible; }
set
{
isVisible = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "" )
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
// called when stage id changes
public void LoadData()
{
var stepTaskViewInfo = projectWorkflowService.LoadProjectStepTaskInfo(StageId);
if (StepTaskViewInfo.CurrentStep != null)
{
StepTaskViewInfo.CurrentStep.ProjectTasks.Items.Clear();
}
StepTaskViewInfo.AllTeamMembers = stepTaskViewInfo.AllTeamMembers;
StepTaskViewInfo.ProjectSteps = stepTaskViewInfo.ProjectSteps;
StepTaskViewInfo.PeerReview = stepTaskViewInfo.PeerReview;
StepTaskViewInfo.AreStepsTasksReadonly = stepTaskViewInfo.AreStepsTasksReadonly;
StepTaskViewInfo.PeerReview.Documents.Items.Add(new ActivityArtifact { FileName = string.Empty });
}
private string GetAliases(ObservableCollection<SelectableTeamMember> selectedStepMembers)
{
string aliases= selectedStepMembers.Aggregate("", (current, member) => current + (member.Alias + ";"));
aliases= aliases.TrimEnd(';');
return aliases;
}
private string GetDisplayNames(ObservableCollection<SelectableTeamMember> selectedStepMembers)
{
string names = selectedStepMembers.Aggregate("", (current, member) => current + (member.Name + ";"));
names= names.TrimEnd(';');
return names;
}
public void AssignResourcesToStep(ObservableCollection<SelectableTeamMember> selectedStepMembers)
{
StepTaskViewInfo.CurrentStep.StepTeamMembers = selectedStepMembers;
StepTaskViewInfo.CurrentStep.Resources = GetAliases(selectedStepMembers);
StepTaskViewInfo.CurrentStep.StepResourceDisplayName = GetDisplayNames(selectedStepMembers);
foreach (var task in StepTaskViewInfo.CurrentStep.ProjectTasks)
{
task.AllTaskTeamMembers = StepTaskViewInfo.CurrentStep.StepTeamMembers;
task.Resources = GetAliases(StepTaskViewInfo.CurrentStep.StepTeamMembers);
task.TaskResourceDisplayName = GetDisplayNames(StepTaskViewInfo.CurrentStep.StepTeamMembers);
}
}
public void AssignResourcesToTask(ObservableCollection<SelectableTeamMember> selectedTaskMembers, string taskId)
{
var task = StepTaskViewInfo.CurrentStep.ProjectTasks.First(st => st.Id == taskId);
task.Resources = GetAliases(selectedTaskMembers);
task.TaskResourceDisplayName = GetDisplayNames(selectedTaskMembers);
}
public void AssignTaskTips(string ttid)
{
string taskTip = projectWorkflowService.GetTaskTip(ttid);
foreach (var task in StepTaskViewInfo.CurrentStep.ProjectTasks)
{
if (task.TemplateTaskId == ttid)
task.TaskTip = taskTip;
}
}
#region peerreview
public void DownloadDocument(string artifactId, string fileName)
{
projectWorkflowService.DownloadActivityArtifact(artifactId, fileName);
}
public void UploadDocument(string artifactId,string file)
{
projectWorkflowService.UploadActivityArtifact(StageId, artifactId, file);
var projectDocuments = projectWorkflowService.LoadPeerReviewDocuments(StageId);
projectDocuments.Items.Add(new ActivityArtifact { FileName = string.Empty });
StepTaskViewInfo.PeerReview.Documents = projectDocuments;
}
private void GetUsers()
{
foreach (ProjectPeerReview t in StepTaskViewInfo.PeerReview.Reviews.Items.ToList())
{
if (string.IsNullOrEmpty(t.Id))
{
if (!string.IsNullOrEmpty(t.Alias))
{
User current = userService.SearchAlias(t.Alias);
if (current == null)
{
MessageBox.Show("Could not find reviewer " + t.Alias);
StepTaskViewInfo.PeerReview.Reviews.Items.Remove(t);
}
else
{
t.Name = current.Name;
}
}
}
}
}
internal User[] GetSearchingUsersName(string name)
{
return userService.Search(name);
}
#endregion
public void UpdateTaskStatus(object selectedValue)
{
foreach (var task in StepTaskViewInfo.CurrentStep.ProjectTasks)
{
task.Status = selectedValue.ToString();
}
}
public void LoadTasksForCurrentStep()
{
StepTaskViewInfo.CurrentStep.ProjectTasks = projectWorkflowService.LoadProjectTasks( StepTaskViewInfo.CurrentStep.Id);
StepTaskViewInfo.UpdateTaskResources();
}
public void SaveCurrentTasksWithStep()
{
if (StepTaskViewInfo.CurrentStep != null)
{
projectWorkflowService.SaveTasksWithStep(StageId, StepTaskViewInfo.CurrentStep, StepTaskViewInfo.CurrentStep.ProjectTasks);
}
}
public bool SaveData()
{
if (StepTaskViewInfo.CurrentStep != null)
{
GetUsers();
return projectWorkflowService.SaveStepTaskViewInfo(StepTaskViewInfo, StageId);
}
return true;
}
}
}
DataGridColumn.IsReadOnly property will make all cells in the column either ReadOnly or not ReadOnly. DataGridCell.IsReadOnly property is not assignable too. What you are trying to do is to make a column readonly if some other property is false, else column should be editable. It is better to use a template column and set the control accordingly. MSDN
For example, if I want user to edit CarNumber property value is HasCar property value is true else not, then if I write :
<DataGridTextColumn Header="CarNumber" Binding="{Binding CarNumber}" IsReadOnly="{Binding HasCar"/>
//
This will still allow all column values to be editable irrespective of HasCar value.
//
If I use Template column and use DataTrigger then I get the desired effect. This wont allow user to type/edit CarNumber if HasCar property is false :
<DataGridTemplateColumn Header="CarNumber" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="TxtCarNumber" Text="{Binding CarNumber}" >
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding HasCar}" Value="False">
<Setter Property="TextBox.Visibility" Value="Hidden"/>
<Setter Property="TextBox.Width" Value="100"/>
</DataTrigger>
<DataTrigger Binding="{Binding HasCar}" Value="True">
<Setter Property="TextBox.Visibility" Value="Visible"/>
<Setter Property="TextBox.Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
In your code do this, as you want your CheckBox to remain visible but not enabled :
<DataGridTemplateColumn Width ="60" HeaderStyle="{StaticResource HeaderStyle}" Header="Selected" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid IsEnabled={Binding StepTaskViewInfo.AreStepsTasksReadonly}>
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center"
IsChecked="{Binding IsSelected,UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding IsTaskEnabled,UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
With ValidatesOnDataErrors=True when I DataGridTextColumn contents invalid value, if I programmingly change the value, it will display the changed value, but until you click into the Column (enter into the edit mode), the value will revert back to the invalid one...
Below is a working sample:
XAML:
<Window x:Class="WpfApplication1.Window13"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window13" Height="300" Width="300">
<Grid>
<DataGrid VerticalAlignment="Top" Margin="0,5" CanUserAddRows="True" AutoGenerateColumns="False" VerticalScrollBarVisibility="Auto" ItemsSource="{Binding Pricelist}" CanUserDeleteRows="True" >
<DataGrid.Columns>
<DataGridTextColumn Header="Price" Width="60" Binding="{Binding Price, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="Correct" Height="23" HorizontalAlignment="Left" Margin="191,226,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
CodeBehind:
using System;
using System.Windows;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace WpfApplication1
{
public partial class Window13 : Window
{
public Window13()
{
InitializeComponent();
Pricelist = new ObservableCollection<MyProduct>();
this.DataContext = this;
}
public ObservableCollection<MyProduct> Pricelist { get; set; }
private void button1_Click(object sender, RoutedEventArgs e)
{
foreach(var p in Pricelist)
{
p.Price = "0";
}
}
}
public class MyProduct:INotifyPropertyChanged,IDataErrorInfo
{
private string _price;
public string Price
{
get
{
return _price;
}
set
{
_price = value;
this.RaisePropertyChanged("Price");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
protected void RaisePropertyChanged(String propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public string Error
{
get
{
return this["Price"];
}
}
public string this[string columnName]
{
get
{
string result = string.Empty;
switch (columnName)
{
case "Price":
{
decimal temdecimal = 0.00m;
if (string.IsNullOrEmpty(Price) || string.IsNullOrWhiteSpace(Price)
|| !decimal.TryParse(Price, out temdecimal))
{
result = "Price is invalid";
}
break;
}
default:
{
break;
}
}
return result;
}
}
}
}
Reproduce:
E.g.: input "ADS" into the Column which is invalid for a number
field, and then change it to "1" use a button, it will display 1
in the column, but as soon as you click the cell and enter into the
edit mode, the value will change back to ADS again.
Current Workaround:
Just to make it clear, the current workaround I have is to remove ValidatesOnDataErrors=True in the DataGridTextColumn.EditingElementStyle:
<DataGridTextColumn Header="Price" Width="60">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Price,UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Text" Value="{Binding Price,UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
Try add Mode=TwoWay to your Binding code, it worked for me!
Binding="{Binding Price, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True,Mode=TwoWay}"