2011-02-24 143 views
33

所以我有一個WPF DataGrid,它綁定到ObservableCollection。該集合通過IDataErrorInfo對其成員進行驗證。如果我以某種方式編輯某個單元格以使其無效,然後在按下回車鍵之前遠離它,然後返回並使其有效,單元格將停止顯示無效,但是,「!」在該行的頭部仍然存在,並且ToolTip將引用先前的無效值。WPF DataGrid驗證錯誤不清除

回答

20

不使用Mode=TwoWay對於DataGridTextColumns解決了該問題的一個版本,但似乎這個問題也可能因爲其他原因而出現。

(任何人誰具有良好的解釋,爲什麼不使用Mode=TwoWay的解決了這個擺在首位大概是接近解決這一問題)

同樣的事情只是發生在我身上了DataGridComboBoxColumn如此我試圖深入一點。

問題不在於Control中的Binding,它在DataGridHeaderBorder內顯示ErrorTemplate。它將Visibility綁定到Validation.HasError,作爲祖先DataGridRow(正如它應該做的那樣)並且該部分正在工作。

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

的問題是驗證錯誤不會從DataGridRow清除一旦被解決。在我的版本的問題中,DataGridRow從0錯誤開始。當我輸入一個無效值時,它有1個錯誤,所以非常好。但是當我解決這個錯誤時,它跳到了3個錯誤,所有這些錯誤都是一樣的。

在這裏,我試圖用一個DataTriggerValidationErrorTemplate設置爲{x:Null}如果Validation.Errors.Count沒有1.對於第一次迭代偉大的工作來解決它,但一旦我清除錯誤,第二次就又回來了。它不再有3個錯誤,它有7個!經過幾次迭代後,它大於10.

我也嘗試通過在BindingExpressions上做UpdateSourceUpdateTarget手動清除錯誤,但沒有骰子。 Validation.ClearInvalid也沒有任何影響。並希望通過該工具包中的源代碼並沒有得到我在任何地方:)

所以我沒有這個什麼好的解決方案,但我想我應該反正後我發現..

我唯一的「解決辦法」至今只是隱藏ErrorTemplateDataGridRowHeader

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

+1這實在是煩人:( – surfen 2011-12-11 23:55:53

+0

如果我不使用'模式= TwoWay'文格不可編輯了和'DataGridTextColumn.EditingElementStyle'是永遠 – bitbonk 2014-04-15 14:03:59

1

嘗試從每個綁定元素中刪除DataGridTextColumns中的每個Mode=TwoWay

0

如果您看到類似Meleak錯誤越來越多,我很想知道你的錯誤收集如何被填充。在Meleaks版本中,他解決了無效數據後發現了三個錯誤(以及更多)。

在我的數據驗證代碼中,我刪除了以前的特定錯誤實例,然後在每次數據更改時重新添加。作爲參考,在這裏是一個示例:

的驗證水暖

#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 

A樓盤正在驗證

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 

所以,當屬性更改處理程序運行時,如果在ValidationErrors存在錯誤字典中,將其刪除,然後檢查該值,如果不符合要求,則會向字典中添加錯誤。這有助於確保在該實體驗證錯誤字典中僅存在任何錯誤的一個實例。

+0

集合中的所有錯誤都是完全相同的,一旦DataGridCell的內容出現驗證錯誤,它們就會在DataGrid內部填充到DataGridRow。不幸的是,您無法執行任何操作這個集合,但看看它在我的版本中的錯誤只是來自視圖模型'IDataErrorInfo'的實現。爲了深入瞭解問題所在,您可能需要訪問源代碼(而不是工具包中的源代碼他們在這方面的工作方式不同)。反射器雖然可以工作.. – 2011-09-22 21:45:30

+0

嗯,是的,我沒有在ViewModel之前實現IDataErrorInfo。我在我的數據對象上做這件事,以便他們可以報告他們自己的驗證狀態(而不是從長遠來看真的很重要)。所以DataGrid填充了這些錯誤,並且它們從DataGridCell傳遞給它?我沒有聽說過這個(我絕不是專家)。這是香草.NET DataGrid嗎? – CodeWarrior 2011-09-23 14:00:50

0

我的情況是這樣的:

  1. 模型實現了基於WPF DataGrid Practical Examples -Validation with IDataErrorInfoIDataErrorInfo
  2. 定製行驗證規則,即結合使用IDataErrorInfo的從模型的所有錯誤。

    <DataGrid.RowValidationRules> 
        <local:RowDataInfoValidationRule ValidationStep="UpdatedValue" /> 
    </DataGrid.RowValidationRules> 
    
  3. ValidatesOnDataErrors=TrueValidatesOnExceptions=TrueNotifyOnValidationError=True內的結合(我開始)

這導致了多個訪問我的驗證引擎和eventualy在不一致的狀態我DataGrid(上排的錯誤通知即使在行有效時也是如此)。

的解決方案是從結合(點3)

我建議通過Clearing a DataGrid row validation error讀取太除去開關。

0

我的解決方法是從每個數據網格列表中的綁定聲明中刪除屬性UpdateSourceTrigger =「LostFocus」

1

我的解決方法是不使用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媒體資源相關聯的錯誤消息。

2

我的解決辦法來實現自定義行確認反饋,根據要自定義行確認反饋部分類似於this page。行錯誤然後適當地消失。

(我還加RowHeaderWidth="20"DataGrid定義,避免了表移到第一時間感嘆號出現的權利。)

1

在我的情況下,它的工作一切都很好,當我們使用DataGrid WPF3.5版本。我們升級到4.0,然後停止重置。在SO,谷歌等搜索後,我碰巧遇到了我的解決方案。 設置UpdateSourceTrigger = PropertyChanged在DataGridTextColumn中的綁定爲我解決了它。

我剛剛意識到紅色感嘆號不會清除將其設置爲正確的值。

0

在我來說,我不得不從綁定定義

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"> 

驗證: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; 
    } 
} 

而且你數據對象,我不使用IDataErrorInfoINotifyDataErrorInfo和我的解決辦法是改變我的綁定必須實現IDataErrorInfo的

0

UpdateSourceTrigger="PropertyChanged"UpdateSourceTrigger="LostFocus"這是唯一的東西

如果你是usin摹ValidationRules在DataGrid列確定指標和你需要的驗證規則時曾經屬性的變化(在UI或屬性)看看你的ValidationRule

XAML設定例ValidatesOnTargetUpdated="True"運行:

<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> 
1

我與RowHeader錯誤模板不會消失相同的問題。我正在使用INotifyDataErrorInfo。在Fredrik Hedblad的研究之後,我提出了一個解決方法;我已經修改了DataGridRowHeader模板使用MultiBinding爲ValidationErrorTemplate知名度:

<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屬性。

3

我找到了適合我的最佳答案。請清除您的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的方法,方式你喜歡

0

好了,經過一番工作協同的解決方案的改變工作f或者我,我這是怎麼實現的吧:

<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幫助這裏

+2

WRT你的「編輯」...如果你有一個新的問題,請點擊[問問題](// stackoverflow.com/questions/ask)按鈕。您始終可以參考此答案的上下文......但將您的代碼包含在新問題中。 – Mogsdad 2015-09-01 21:13:10

0

我已經使用這個技術,它與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; 
}