web-dev-qa-db-ja.com

WPFDataGrid検証エラーがクリアされない

したがって、DataGridにバインドされているWPFObservableCollectionがあります。コレクションには、IDataErrorInfoを介してメンバーの検証があります。セルを無効になるように編集し、Enterキーを押す前にタブで離してから戻って有効にすると、セルは無効で表示されなくなりますが、「!」行の先頭は引き続き存在し、ToolTipは前の無効な値を参照します。

36
s73v3r

DataGridTextColumnsMode=TwoWayを使用しないと、問題の1つのバージョンが解決されますが、他の理由でもこの問題がどこからともなく発生する可能性があるようです。

Mode=TwoWayを使用しない理由について十分な説明がある人は、そもそもこれを解決するので、おそらくこの問題の解決策に近いでしょう)

同じことがDataGridComboBoxColumnで起こったので、もう少し深く掘ってみました。

問題は、Binding内にControlを表示するErrorTemplate内のDataGridHeaderBorderではありません。祖先VisibilityDataGridRowValidation.HasErrorにバインドしており(正確に実行する必要があります)、その部分は機能しています。

Visibility="{Binding (Validation.HasError),
                     Converter={StaticResource bool2VisibilityConverter},
                     RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"/>

問題は、検証エラーが解決されると、DataGridRowからクリアされないことです。私のバージョンの問題では、DataGridRowは0エラーで始まりました。無効な値を入力すると、エラーが1つ発生しましたが、これまでのところ問題ありません。しかし、私がエラーを解決したとき、それは最大3つのエラーに跳ね上がり、それらはすべて同じでした。

ここでは、{x:Null}が1でない場合、DataTriggerValidation.Errors.Countに設定するValidationErrorTemplateを使用して解決しようとしました。最初の反復ではうまく機能しましたが、一度はうまくいきました。それが戻った2回目のエラーをクリアしました。もう3つのエラーはなく、7つありました。さらに数回繰り返した後、それは10を超えていました。

また、UpdateSourceUpdateTargetBindingExpressionsを実行してエラーを手動でクリアしようとしましたが、サイコロは使用しませんでした。 Validation.ClearInvalidも効果がありませんでした。そして、ツールキットのソースコードを調べても、どこにも行きませんでした:)

だから私はこれに対する良い解決策を持っていませんが、とにかく私の発見を投稿するべきだと思いました。

これまでの私の唯一の「回避策」は、ErrorTemplateDataGridRowHeaderを非表示にすることです。

<DataGrid ...>
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Setter Property="ValidationErrorTemplate" Value="{x:Null}"/>
        </Style>
    </DataGrid.RowStyle>
    <!-- ... -->
</DataGrid>
23
Fredrik Hedblad

自分に合ったベストアンサーを見つけました。 DataGridRowValidationErrorTemplateをクリアするだけです。

  1. コード内

    YourGrid.RowValidationErrorTemplate = new ControlTemplate();
    
  2. Xamlで

    <DataGrid.RowValidationErrorTemplate>
        <ControlTemplate>
        </ControlTemplate>
    </DataGrid.RowValidationErrorTemplate>`
    
  3. 次に、独自の行検証エラーテンプレートを作成します。

    データ項目が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);
    }
    
  4. 独自のIsValidメソッドを好きなように書いてください

4
MSL

私の解決策は、 このページの下にあるようなカスタム行検証フィードバックを実装することでした(= /// =)行検証フィードバックをカスタマイズするにはセクション。その後、行エラーは適切に消えます。

(感嘆符が最初に表示されたときにテーブルが右にシフトしないように、DataGrid定義にRowHeaderWidth="20"も追加しました。)

2
Conrad

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プロパティが更新されるようにしました。

2
ChangedDaily

私の回避策は、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プロパティからのエラーメッセージを表示するようにします。

1
synergetic

各Binding要素から各DataGridTextColumnsMode=TwoWayを削除してみてください。

1
Priyank

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つだけ存在するようになります。

1
CodeWarrior

私の場合、バインディング定義から削除する必要がありました

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を実装する必要があります

0
Evalds Urtans

私の回避策は、各datagridcolumnのバインディング宣言からプロパティpdateSourceTrigger = "LostFocus"を削除することでした。

0
Eduardo Brites

私は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>
0
MoMo

さて、いくつかの作業の後、シナジェティクスのソリューションの変更が私のために働いた後、これは私がそれを実装した方法です:

    <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ヘルプはこちら

0
Houdini

私のシナリオは次のようなものでした:

  1. モデルの実装IDataErrorInfo
  2. WPF DataGridの実用例-IDataErrorInfoを使用した検証 に基づくカスタム行検証ルール。IDataErrorInfoを使用してモデルからのすべてのエラーを結合します。

    <DataGrid.RowValidationRules>
        <local:RowDataInfoValidationRule ValidationStep="UpdatedValue" />
    </DataGrid.RowValidationRules>
    
  3. ValidatesOnDataErrors=TrueValidatesOnExceptions=TrueNotifyOnValidationError=Trueバインディング内(私が始めた)

これにより、検証エンジンへの複数のアクセスが発生し、最終的にDataGridが一貫性のない状態になりました(行が有効な場合でも行ヘッダーのエラー通知)。

解決策は、バインディングからスイッチを削除することでした(ポイント3)。

DataGrid行検証エラーのクリア も読むことをお勧めします。

0
surfen

私の場合、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;
}       
0
Nandy