したがって、DataGrid
にバインドされているWPFObservableCollection
があります。コレクションには、IDataErrorInfo
を介してメンバーの検証があります。セルを無効になるように編集し、Enterキーを押す前にタブで離してから戻って有効にすると、セルは無効で表示されなくなりますが、「!」行の先頭は引き続き存在し、ToolTip
は前の無効な値を参照します。
DataGridTextColumns
にMode=TwoWay
を使用しないと、問題の1つのバージョンが解決されますが、他の理由でもこの問題がどこからともなく発生する可能性があるようです。
(Mode=TwoWay
を使用しない理由について十分な説明がある人は、そもそもこれを解決するので、おそらくこの問題の解決策に近いでしょう)
同じことがDataGridComboBoxColumn
で起こったので、もう少し深く掘ってみました。
問題は、Binding
内にControl
を表示するErrorTemplate
内のDataGridHeaderBorder
ではありません。祖先Visibility
のDataGridRow
をValidation.HasError
にバインドしており(正確に実行する必要があります)、その部分は機能しています。
Visibility="{Binding (Validation.HasError),
Converter={StaticResource bool2VisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"/>
問題は、検証エラーが解決されると、DataGridRow
からクリアされないことです。私のバージョンの問題では、DataGridRow
は0エラーで始まりました。無効な値を入力すると、エラーが1つ発生しましたが、これまでのところ問題ありません。しかし、私がエラーを解決したとき、それは最大3つのエラーに跳ね上がり、それらはすべて同じでした。
ここでは、{x:Null}
が1でない場合、DataTrigger
をValidation.Errors.Count
に設定するValidationErrorTemplate
を使用して解決しようとしました。最初の反復ではうまく機能しましたが、一度はうまくいきました。それが戻った2回目のエラーをクリアしました。もう3つのエラーはなく、7つありました。さらに数回繰り返した後、それは10を超えていました。
また、UpdateSource
でUpdateTarget
とBindingExpressions
を実行してエラーを手動でクリアしようとしましたが、サイコロは使用しませんでした。 Validation.ClearInvalid
も効果がありませんでした。そして、ツールキットのソースコードを調べても、どこにも行きませんでした:)
だから私はこれに対する良い解決策を持っていませんが、とにかく私の発見を投稿するべきだと思いました。
これまでの私の唯一の「回避策」は、ErrorTemplate
のDataGridRowHeader
を非表示にすることです。
<DataGrid ...>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ValidationErrorTemplate" Value="{x:Null}"/>
</Style>
</DataGrid.RowStyle>
<!-- ... -->
</DataGrid>
自分に合ったベストアンサーを見つけました。 DataGrid
のRowValidationErrorTemplate
をクリアするだけです。
コード内
YourGrid.RowValidationErrorTemplate = new ControlTemplate();
Xamlで
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>`
次に、独自の行検証エラーテンプレートを作成します。
データ項目がINotifyPropertyChangedの場合
((INotifyPropertyChanged)i).PropertyChanged += this.i_PropertyChanged;`
その後
private void i_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
var row = this.ItemContainerGenerator.ContainerFromItem(sender) as DataGridRow;
if (row == null)
return;
var Errs = IsValid(row);
if (Errs.Count == 0) row.Header = null;
else
{
// Creatr error template
var gg = new Grid { ToolTip = "Error Tooltip" };
var els = new Ellipse { Fill = new SolidColorBrush(Colors.Red), Width = row.FontSize, Height = row.FontSize };
var tb = new TextBlock
{
Text = "!",
Foreground = new SolidColorBrush(Colors.White),
HorizontalAlignment = HorizontalAlignment.Center,
FontWeight = FontWeights.Bold
};
gg.Children.Add(els);
gg.Children.Add(tb);
row.Header = gg;
}
}),
System.Windows.Threading.DispatcherPriority.ApplicationIdle);
}
独自のIsValidメソッドを好きなように書いてください
私の解決策は、 このページの下にあるようなカスタム行検証フィードバックを実装することでした(= /// =)行検証フィードバックをカスタマイズするにはセクション。その後、行エラーは適切に消えます。
(感嘆符が最初に表示されたときにテーブルが右にシフトしないように、DataGrid
定義にRowHeaderWidth="20"
も追加しました。)
RowHeaderエラーテンプレートが消えないという同じ問題があります。 INotifyDataErrorInfoを使用しています。 Fredrik Hedbladによる調査のフォローアップとして、回避策を作成しました。 ValidationErrorTemplateの可視性にMultiBindingを使用するようにDataGridRowHeaderテンプレートを変更しました。
<Style x:Key="DataGridRowHeaderStyle" TargetType="{x:Type DataGridRowHeader}">
<!--<Setter Property="Background" Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly=Brushes:BrushesLibrary1,
ResourceId=HeaderBrush}}"/>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridRowHeader}">
<Grid>
<Microsoft_Windows_Themes:DataGridHeaderBorder BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"
IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}"
IsSelected="{TemplateBinding IsRowSelected}" Orientation="Horizontal"
Padding="{TemplateBinding Padding}" SeparatorBrush="{TemplateBinding SeparatorBrush}"
SeparatorVisibility="{TemplateBinding SeparatorVisibility}">
<StackPanel Orientation="Horizontal">
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"
Width="15"/>
<Control SnapsToDevicePixels="false"
Template="{Binding ValidationErrorTemplate, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}">
<Control.Visibility>
<MultiBinding Converter="{StaticResource ValidationConverter}">
<Binding Path="(Validation.HasError)" RelativeSource="{RelativeSource AncestorType={x:Type DataGridRow}}"/>
<Binding Path="DataContext.HasErrors" RelativeSource="{RelativeSource AncestorType={x:Type DataGridRow}}"/>
</MultiBinding>
</Control.Visibility>
<!-- Original binding below -->
<!--Visibility="{Binding (Validation.HasError), Converter={StaticResource bool2VisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}">-->
</Control>
</StackPanel>
</Microsoft_Windows_Themes:DataGridHeaderBorder>
<Thumb x:Name="PART_TopHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Top"/>
<Thumb x:Name="PART_BottomHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Bottom"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
これは、変更通知付きの「HasErrors」プロパティを持つバインドされたオブジェクトに依存しています。私のプロジェクトでは、アイテムEndEditイベントでHasErrorsのPropertyChangedを発生させることにより、HasErrorsプロパティが更新されるようにしました。
私の回避策は、Validation.Errorsを使用するのではなく、DataGridRow.Itemプロパティを使用することでした。 DataGridがIDataErrorInfoインターフェイスを実装するビジネスオブジェクトにバインドされている場合は、IsNotValidプロパティ(またはIsValid)を追加し、Errorプロパティがオブジェクトに関連付けられているすべてのエラーを返すようにすることができます。次に、DataGridRowHeaderのデフォルトスタイルをカスタマイズします。
<Style x:Key="{x:Type DataGridRowHeader}" TargetType="{x:Type DataGridRowHeader}">
...
<Control SnapsToDevicePixels="false"
Visibility="{Binding RelativeSource={RelativeSource
AncestorType={x:Type DataGridRow}},
Path=Item.IsNotValid, Converter={StaticResource
Bool2VisibilityConverter}}"
Template="{Binding RelativeSource={RelativeSource
AncestorType={x:Type DataGridRow}},
Path=ValidationErrorTemplate}" />
...
</Style>
また、DataGridRowスタイルでValidationErrorTemplateをカスタマイズして、DataGridRow.Item.Errorプロパティからのエラーメッセージを表示するようにします。
各Binding要素から各DataGridTextColumns
のMode=TwoWay
を削除してみてください。
Meleakと同様のエラーが増えている場合は、エラーコレクションがどのように入力されるかを知りたいと思います。 Meleaksバージョンの問題では、無効なデータを解決した後、3つ(およびそれ以上)のエラーが表示されます。
データ検証コードでは、特定のエラーの前のインスタンスを削除し、データが変更されるたびに再度追加します。参考までに、サンプルを次に示します。
#Region " Validation workers "
Private m_validationErrors As New Dictionary(Of String, String)
Private Sub AddError(ByVal ColName As String, ByVal Msg As String)
If Not m_validationErrors.ContainsKey(ColName) Then
m_validationErrors.Add(ColName, Msg)
End If
End Sub
Private Sub RemoveError(ByVal ColName As String)
If m_validationErrors.ContainsKey(ColName) Then
m_validationErrors.Remove(ColName)
End If
End Sub
Public ReadOnly Property [Error]() As String Implements System.ComponentModel.IDataErrorInfo.Error
Get
If m_validationErrors.Count > 0 Then
Return "Shipment data is invalid"
Else
Return Nothing
End If
End Get
End Property
Default Public ReadOnly Property Item(ByVal columnName As String) As String Implements System.ComponentModel.IDataErrorInfo.Item
Get
If m_validationErrors.ContainsKey(columnName) Then
Return m_validationErrors(columnName).ToString
Else
Return Nothing
End If
End Get
End Property
#End Region
Private Sub OnZIPChanged()
Me.RemoveError("Zip")
If _Zip Is Nothing OrElse _Zip.Trim = "" Then
Me.AddError("Zip", "Please enter a Zip Code")
Else
Select Case _Zip.Length
Case 5
Case 10
Case Else
Me.AddError("Zip", "Please enter a Zip Code")
End Select
End If
OnPropertyChanged("CanShip")
End Sub
したがって、プロパティChangedハンドラーを実行すると、ValidationErrorsディクショナリにエラーが存在する場合は削除され、値がチェックされ、要件に一致しない場合はディクショナリにエラーが追加されます。これにより、そのエンティティ検証エラーディクショナリにエラーのインスタンスが1つだけ存在するようになります。
私の場合、バインディング定義から削除する必要がありました
UpdateSourceTrigger=PropertyChanged
私にとっては、次の両方の定義で機能します。
<DataGridTextColumn
Header="Time, min"
x:Name="uiDataGridTextColumnTime"
Width="Auto"
CellStyle="{StaticResource ResourceKey=DataGridCellText}"
IsReadOnly="False">
<DataGridTextColumn.Binding>
<Binding Path="fTime" StringFormat="{}{0:0.00}">
<Binding.ValidationRules>
<Validation:CellDataInfoValidationRule ValidationStep="UpdatedValue"/>
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
そして
<DataGridTextColumn
Header="Time, min"
x:Name="uiDataGridTextColumnTime"
Width="Auto"
CellStyle="{StaticResource ResourceKey=DataGridCellText}"
Binding="{Binding fTime, StringFormat={}\{0:0.00\}, ValidatesOnDataErrors=True}"
IsReadOnly="False">
Validation:CellDataInfoValidationRuleはカスタムクラスであり、ここで取得します
public class CellDataInfoValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
// obtain the bound business object
BindingExpression expression = value as BindingExpression;
IDataErrorInfo info = expression.DataItem as IDataErrorInfo;
// determine the binding path
string boundProperty = expression.ParentBinding.Path.Path;
// obtain any errors relating to this bound property
string error = info[boundProperty];
if (!string.IsNullOrEmpty(error))
{
return new ValidationResult(false, error);
}
return ValidationResult.ValidResult;
}
}
そしてあなたのデータオブジェクトはIDataErrorInfoを実装する必要があります
私の回避策は、各datagridcolumnのバインディング宣言からプロパティpdateSourceTrigger = "LostFocus"を削除することでした。
私はIDataErrorInfo
またはINotifyDataErrorInfo
を使用していません。私の解決策は、バインディングをUpdateSourceTrigger="PropertyChanged"
からUpdateSourceTrigger="LostFocus"
に変更することでした。これが唯一のことでした。
DataGrid列の定義でValidationRulesを使用していて、プロパティが(UIまたはプロパティで)変更されるたびに検証ルールを実行する必要がある場合は、ValidationRule
の設定ValidatesOnTargetUpdated="True"
を調べてください。
XAMLの例:
<DataGridTextColumn Header="Name"
CellStyle="{StaticResource DGCellStyle}"
ElementStyle="{StaticResource DGTextColValidationStyle}"
EditingElementStyle="{StaticResource DGTextColEditValidationStyle}">
<DataGridTextColumn.Binding>
<Binding Path="Name" UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<ValidationResource:YourValidationRule ValidationStep="UpdatedValue" ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
さて、いくつかの作業の後、シナジェティクスのソリューションの変更が私のために働いた後、これは私がそれを実装した方法です:
<Style x:Key="{x:Type DataGridRowHeader}" TargetType="{x:Type DataGridRowHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Control SnapsToDevicePixels="true"
Visibility="{Binding RelativeSource={RelativeSource
AncestorType={x:Type DataGridRow}},
Path=Item.HasErrors, Converter={StaticResource
BooleanToVisibilityConverter }}"
Template="{Binding RelativeSource={RelativeSource
AncestorType={x:Type DataGridRow}},
Path=ValidationErrorTemplate}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
BooleanToVisibilityConverterを参照することを忘れないでください:
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
行のスタイルは(まだ)デフォルトのスタイルほど良くはありません。私はそれに取り組んでいます。
編集:Plzヘルプはこちら
私のシナリオは次のようなものでした:
IDataErrorInfo
WPF DataGridの実用例-IDataErrorInfoを使用した検証 に基づくカスタム行検証ルール。IDataErrorInfoを使用してモデルからのすべてのエラーを結合します。
<DataGrid.RowValidationRules>
<local:RowDataInfoValidationRule ValidationStep="UpdatedValue" />
</DataGrid.RowValidationRules>
ValidatesOnDataErrors=True
、ValidatesOnExceptions=True
、NotifyOnValidationError=True
バインディング内(私が始めた)
これにより、検証エンジンへの複数のアクセスが発生し、最終的にDataGrid
が一貫性のない状態になりました(行が有効な場合でも行ヘッダーのエラー通知)。
解決策は、バインディングからスイッチを削除することでした(ポイント3)。
DataGrid行検証エラーのクリア も読むことをお勧めします。
私の場合、DataGrid WPF3.5バージョンを使用していたときは、すべてうまく機能しました。 4.0にアップグレードした後、リセットが停止しました。 SO、グーグルなどで検索した後、私は自分の解決策を偶然見つけました。 DataGridTextColumnのBindingにpdateSourceTrigger = PropertyChangedを設定すると、修正されました。
正しい値に設定しても、赤い感嘆符がクリアされないことに気づきました。
RowValidationRulesを廃止し、代わりにビューモデルでプロパティ検証を使用するこの手法を使用しました。これには、静的変数とデータ注釈が必要です。
//uses Prism.MVVM for BindableBase and INotifyDataErrorInfo
private static int _xxStartNo;
private static int _xxEndNo;
// in property getter/setter
private int _startNo;
[CustomValidation(typeof(YourModel), "ValidateStartNoRange")]
public int StartNo
{
get
{
_xxStartNo=_startNo;
return _startNo;
}
set
{
..........
ValidateProperty("StartNo")
}
}
.......
public static ValidationResult ValidateStartNoRange(int number)
{
if(number > _xxEndNo)
{
return ValidationResult("Start No must be less than End No.";
}
return ValidationResult.Success;
}