How can I stop IDataErrorInfo from firing for new

2019-06-07 15:11发布

问题:

I am developing a WPF application (using MVVM) and have implemented IDataErrorInfo on my ViewModel. Part of my validation checks that mandatory fields have been entered. This works perfectly for editing existing records but doesn't provide a nice user experience when adding new records. As soon as I load the new view, the mandatory fields are highlighted as invalid.

Is there a (preferably non-hacky) solution to this? It seems a pretty standard thing to want, so I'm hoping I'm missing something simple.

回答1:

Use a flag that indicates whether the record is new. Check this flag in your implementation of IDataErrorInfo, and set the flag to true the first time the user tries to validate the input.



回答2:

Instead of using "ValidatesOnDataErrors=True" you can add DataErrorValidationRule manually, with ValidatesOnTargetUpdated attribute:

<TextBox>
   <TextBox.Text>
     <Binding Path="..." ValidatesOnExceptions="true">
        <Binding.ValidationRules>
            <DataErrorValidationRule ValidatesOnTargetUpdated="false"/>
        </Binding.ValidationRules>
     </Binding>
   </TextBox.Text>
</TextBox>


回答3:

I generally have a sub on my entity or dataclass that removes all entries from my Error dictionary after initialization. I call it after instancing.

You could go a step further and have an initialized field (boolean) that gets set to False for New objects, True for existing objects.

You don't want the user presented with a bunch of error/validation templates just because they have generated a new object and haven't populated it with data yet.

ViewModel Example

In this example I have the Property from my ViewModel. When the Shipment Object gets loaded, the property calls the Clear method on the error dictionary. If the Shipment is a persisted record from the database, it should have valid data as it had to be validated in order to be saved to the DB. if it is new, the entries get deleted and the user is presented with a fresh form ready for input.

Public Property ShipmentRecord() As Shipment
    Get
        Return _shpShipmentRecord
    End Get
    Set(ByVal value As Shipment)
        _shpShipmentRecord = value
        ShipmentRecord.m_dirtyFields.Clear()
        OnPropertyChanged("ShipmentRecord")
    End Set
End Property

Unfortunately this was the first example I could find, and it accesses the m_dirtyFields dictionary directly (it was originally private, but I set it as Friend.) Under most circumstances I would create a method to access the dictionary instead.