C# 在datagrid中编辑当前选定的项
嘿嘿, 我正在用MVVM创建一个简单的应用程序,偶然发现了一个我发现很难解决的问题。在我的应用程序中,我有datagrid和两个控件来编辑datagrid中当前选定的项。在我的ViewModel中,我有C# 在datagrid中编辑当前选定的项,c#,mvvm,binding,datagrid,C#,Mvvm,Binding,Datagrid,嘿嘿, 我正在用MVVM创建一个简单的应用程序,偶然发现了一个我发现很难解决的问题。在我的应用程序中,我有datagrid和两个控件来编辑datagrid中当前选定的项。在我的ViewModel中,我有CurrentSequence属性,该属性保存colorsettingsSequenceSequence对象(这些对象的集合用作datagrid的DataContext) 这里是xaml: <DataGrid AutoGenerateColumns="False" ItemsSource="
CurrentSequence
属性,该属性保存colorsettingsSequenceSequence
对象(这些对象的集合用作datagrid的DataContext)
这里是xaml:
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Path=ColorSettingsSequences}"
SelectedItem="{Binding Path=CurrentSequence, Mode=TwoWay}">
.... more things here ...
</DataGrid>
<StackPanel Grid.Column="0" Grid.Row="0">
<Grid>
<Label Content="Start temperature (°C)" Height="28" HorizontalAlignment="Left" x:Name="lblSeqStartTemp" VerticalAlignment="Top" />
<TextBox Height="23" Margin="0,28,10,0" x:Name="tbSeqStartTemp" VerticalAlignment="Top" Text="{Binding Path=CurrentSequence.StartTemp}" />
</Grid>
<Grid>
<Label Content="Start color" Height="28" HorizontalAlignment="Left" x:Name="lblSeqHue" VerticalAlignment="Top" />
<xctk:ColorPicker Margin="0,28,10,0" x:Name="clrpSeqHue" SelectedColor="{Binding Path=CurrentSequence.StartHue, Converter={StaticResource hueToColor}, ConverterParameter=False}" ShowStandardColors="False" />
</Grid>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="0">
<Grid>
<Label Content="End temperature (°C)" Height="28" HorizontalAlignment="Left" x:Name="lblSeqEndTemp" VerticalAlignment="Top" />
<TextBox Height="23" Margin="0,28,10,0" x:Name="tbSeqEndTemp" VerticalAlignment="Top" Text="{Binding Path=CurrentSequence.EndTemp}" />
</Grid>
<Grid>
<Label Content="End color" Height="28" HorizontalAlignment="Left" x:Name="lblSeqEndHue" VerticalAlignment="Top" />
<xctk:ColorPicker Margin="0,28,10,0" x:Name="clrpSeqEndHue" SelectedColor="{Binding Path=CurrentSequence.EndHue, Converter={StaticResource hueToColor}, ConverterParameter=False}" ShowStandardColors="False" />
</Grid>
</StackPanel>
这很好,但当我想添加验证时,问题就来了。我想分别验证StartTemp
和EndTemp
,并给出不同的错误。如何分解colorsettingsSequenceSequence
对象,以便在编辑数据网格中更新的一个值时绑定仍能正常工作
以下是我尝试的内容,我创建了2个新属性,并将我的验证添加到这些属性中:
private String _currentSequenceStartTemp;
public String CurrentSequenceStartTemp
{
get
{
return _currentSequenceStartTemp;
}
set
{
this._currentSequenceStartTemp = value;
CurrentSequence.StartTemp = value;
RaisePropertyChanged("CurrentSequenceStartTemp");
Validator.Validate(() => CurrentSequenceStartTemp);
ValidateCommand.Execute(null);
}
}
private String _currentSequenceEndTemp;
public String CurrentSequenceEndTemp
{
get
{
return _currentSequenceEndTemp;
}
set
{
this._currentSequenceEndTemp = value;
CurrentSequence.EndTemp = value;
RaisePropertyChanged("CurrentSequenceEndTemp");
Validator.Validate(() => CurrentSequenceEndTemp);
ValidateCommand.Execute(null);
}
}
我只是将文本框绑定到这些值,而不是直接绑定到CurrentSequence
。我还添加了在setters中设置CurrentSequence值的功能,希望我的更改能够一直推回到原始集合,并在datagrid中进行更改。那没有发生。。
当更改CurrentSequence时,我也会更改这些属性的值:
private ColorSettingsSequencesSequence _currentSequence;
public ColorSettingsSequencesSequence CurrentSequence
{
get
{
return this._currentSequence;
}
set
{
this._currentSequence = value;
RaisePropertyChanged("CurrentSequence");
if (value != null)
{
CurrentSequenceStartTemp = value.StartTemp;
CurrentSequenceEndTemp = value.EndTemp;
}
else
{
CurrentSequenceStartTemp = String.Empty;
CurrentSequenceEndTemp = String.Empty;
}
}
}
如果我理解正确,您的问题是即使验证失败,您也希望提交属性值。如果我的假设是错误的,那么解决方案就更简单了,基本上是sine在他的评论中暗示的,即您只需要在
颜色设置序列序列
类中实现INotifyPropertyChanged
我无法从你的帖子中推断出你采用了什么样的验证方式,但以下是我的做法。即使文本框中的验证失败,更新数据网格的关键是ValidationStep=“UpdatedValue”
验证规则的一部分(以及规则的实现)
视图:
ValidationRule(另请参见):
公共类TempValidationRule:ValidationRule
{
//默认值
私有内部最小温度=-273;
私有内部温度_最大温度=2500;
公共int最小温度
{
获取{return\u minimumTemp;}
设置{u minimumTemp=value;}
}
公共int最大温度
{
获取{return\u maximumTemp;}
设置{u maximumTemp=value;}
}
公共覆盖验证结果验证(对象值,System.Globalization.CultureInfo CultureInfo)
{
字符串错误=null;
字符串s=GetBoundValue(值)作为字符串;
如果(!string.IsNullOrEmpty)
{
内部温度;
如果(!int.TryParse,out temp))
error=“无有效整数”;
否则如果(温度>此最大温度)
error=string.Format(“温度太高,最大值为{0}.”,this.MaximumTemp);
否则如果(温度<此最低温度)
error=string.Format(“温度过低。最小值为{0}.”,this.MinimumTemp);
}
返回新的ValidationResult(string.IsNullOrEmpty(error),error);
}
私有对象GetBoundValue(对象值)
{
if(值为BindingExpression)
{
//ValidationStep为UpdatedValue或CommittedValue(设置后验证)
//需要从BindingExpression中提取值。
BindingExpression绑定=(BindingExpression)值;
//获取绑定对象和属性的名称
字符串resolvedPropertyName=binding.GetType().GetProperty(“ResolvedSourcePropertyName”,BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).GetValue(binding,null).ToString();
object resolvedSource=binding.GetType().GetProperty(“resolvedSource”,BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance”).GetValue(binding,null);
//提取属性的值
object propertyValue=resolvedSource.GetType().GetProperty(resolvedPropertyName).GetValue(resolvedSource,null);
返回属性值;
}
其他的
{
返回值;
}
}
}
我重现了你的问题。但是我找不到任何问题。一切正常
- 分别验证
和StartTemp
EndTemp
- 如果更新了一个值,则还应更新datagrid
公共类ViewModelBase:INotifyPropertyChanged
{
#INotifyPropertyChanged的区域实现
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged(字符串propertyName)
{
OnPropertyChanged(新PropertyChangedEventArgs(propertyName));
}
PropertyChanged上受保护的虚拟无效(PropertyChangedEventArgs args)
{
var handler=PropertyChanged;
if(处理程序!=null)
处理程序(this,args);
}
#端区
}
颜色设置序列序列类
公共类颜色设置序列:ViewModelBase,IDataErrorInfo
{
#区域声明
私有字符串startColor;
私有字符串起始温度;
私有字符串结束温度;
#端区
#区域属性
///
///获取或设置开始颜色。
///
///
///开始颜色。
///
公共字符串StartColor
{
得到
{
返回此.startColor;
private ColorSettingsSequencesSequence _currentSequence;
public ColorSettingsSequencesSequence CurrentSequence
{
get
{
return this._currentSequence;
}
set
{
this._currentSequence = value;
RaisePropertyChanged("CurrentSequence");
if (value != null)
{
CurrentSequenceStartTemp = value.StartTemp;
CurrentSequenceEndTemp = value.EndTemp;
}
else
{
CurrentSequenceStartTemp = String.Empty;
CurrentSequenceEndTemp = String.Empty;
}
}
}
<UserControl x:Class="WpfApplication1.DemoValidation"
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:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:DemoValidationViewModel />
</UserControl.DataContext>
<UserControl.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder Name="ph" />
</Border>
<Border BorderBrush="LightGray" BorderThickness="1" Background="Beige">
<TextBlock Foreground="Red" FontSize="12" Margin="5"
Text="{Binding ElementName=ph, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
</TextBlock>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<Label Content="Start temperature (°C)" Height="28" HorizontalAlignment="Left" x:Name="lblSeqStartTemp" VerticalAlignment="Top" />
<TextBox Height="23" x:Name="tbSeqStartTemp" VerticalAlignment="Top" >
<TextBox.Text>
<Binding Path="CurrentSequence.StartTemp" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:TempValidationRule MaximumTemp="400" MinimumTemp="-100" ValidationStep="UpdatedValue" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</StackPanel>
<StackPanel Grid.Column="2" Grid.Row="0">
<Label Content="End temperature (°C)" Height="28" HorizontalAlignment="Left" x:Name="lblSeqEndTemp" VerticalAlignment="Top" />
<TextBox Height="23" x:Name="tbSeqEndTemp" VerticalAlignment="Top" >
<TextBox.Text>
<Binding Path="CurrentSequence.EndTemp" UpdateSourceTrigger="PropertyChanged" >
<Binding.ValidationRules>
<local:TempValidationRule MaximumTemp="500" MinimumTemp="100" ValidationStep="UpdatedValue" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</StackPanel>
<DataGrid Grid.Row="2" Grid.ColumnSpan="3" Margin="0,10,0,0"
ItemsSource="{Binding Path=ColorSettingsSequences}"
SelectedItem="{Binding Path=CurrentSequence, Mode=TwoWay}" />
</Grid>
</UserControl>
public class DemoValidationViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private ColorSettingsSequencesSequence _currentSequence;
public ColorSettingsSequencesSequence CurrentSequence
{
get { return this._currentSequence; }
set
{
this._currentSequence = value;
OnPropertyChanged("CurrentSequence");
}
}
public List<ColorSettingsSequencesSequence> ColorSettingsSequences { get; private set; }
public DemoValidationViewModel()
{
// dummy data
this.ColorSettingsSequences = new List<ColorSettingsSequencesSequence>()
{
new ColorSettingsSequencesSequence() { StartTemp = "10", EndTemp = "20" },
new ColorSettingsSequencesSequence() { StartTemp = "20", EndTemp = "30" },
new ColorSettingsSequencesSequence() { StartTemp = "30", EndTemp = "40" }
};
}
}
public class ColorSettingsSequencesSequence : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _startTemp;
public string StartTemp { get { return _startTemp; } set { _startTemp = value; OnPropertyChanged("StartTemp");}}
private string _endTemp;
public string EndTemp { get { return _endTemp; } set { _endTemp = value; OnPropertyChanged("EndTemp"); } }
}
public class TempValidationRule : ValidationRule
{
// default values
private int _minimumTemp = -273;
private int _maximumTemp = 2500;
public int MinimumTemp
{
get { return _minimumTemp; }
set { _minimumTemp = value; }
}
public int MaximumTemp
{
get { return _maximumTemp; }
set { _maximumTemp = value; }
}
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
string error = null;
string s = GetBoundValue(value) as string;
if (!string.IsNullOrEmpty(s))
{
int temp;
if (!int.TryParse(s, out temp))
error = "No valid integer";
else if (temp > this.MaximumTemp)
error = string.Format("Temperature too high. Maximum is {0}.", this.MaximumTemp);
else if (temp < this.MinimumTemp)
error = string.Format("Temperature too low. Minimum is {0}.", this.MinimumTemp);
}
return new ValidationResult(string.IsNullOrEmpty(error), error);
}
private object GetBoundValue(object value)
{
if (value is BindingExpression)
{
// ValidationStep was UpdatedValue or CommittedValue (validate after setting)
// Need to pull the value out of the BindingExpression.
BindingExpression binding = (BindingExpression)value;
// Get the bound object and name of the property
string resolvedPropertyName = binding.GetType().GetProperty("ResolvedSourcePropertyName", BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).GetValue(binding, null).ToString();
object resolvedSource = binding.GetType().GetProperty("ResolvedSource", BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance).GetValue(binding, null);
// Extract the value of the property
object propertyValue = resolvedSource.GetType().GetProperty(resolvedPropertyName).GetValue(resolvedSource, null);
return propertyValue;
}
else
{
return value;
}
}
}
<Window x:Class="DataGridValidation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525">
<Window.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid Name="mainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="149" />
<RowDefinition Height="73" />
<RowDefinition Height="123" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="249*" />
</Grid.ColumnDefinitions>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding ColorSettingsSequences}"
SelectedItem="{Binding CurrentSequence}"
IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Start Color" Binding="{Binding StartColor}" />
<DataGridTextColumn Header="End Color" Binding="{Binding StartTemperature}" />
<DataGridTextColumn Header="End Color" Binding="{Binding EndTemperature}" />
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Column="0" Grid.Row="1">
<Grid>
<Label Content="Start temperature (°C)"
Height="28"
HorizontalAlignment="Left"
x:Name="lblSeqStartTemp"
VerticalAlignment="Top" />
<TextBox Height="23"
Margin="10,28,10,0"
x:Name="tbSeqStartTemp"
VerticalAlignment="Top"
Text="{Binding Path=CurrentSequence.StartTemperature, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"/>
</Grid>
</StackPanel>
<StackPanel Grid.Row="2" Margin="0,0,0,43">
<Grid>
<Label Content="End temperature (°C)"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
<TextBox Height="23"
Margin="10,28,10,0"
x:Name="tbSeqEndTemp"
VerticalAlignment="Top"
Text="{Binding Path=CurrentSequence.EndTemperature, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"/>
</Grid>
</StackPanel>
</Grid>
</Window>