I've one ViewModel that has a property. This property is a "PhysicalValue". This PhysicalValue is a class that is composed from a Value and an Unit.
public Class MyViewModel:INotifyPropertyChanged:IDataErrorInfo {
public PhysicalValue Target {get => _target; set => {_target = value; NotifyPropertyChanged("Target");}}
public string this[string columnName]
{
get
{
if (columnName == "Target")
{
if(_target.Value>5000){
return "out of spec value";
}
}
return String.Empty;
}
}
}
I've one control that should edit the value of this PhysicalValue:
[...]
<dxe:TextEdit EditValue="{Binding Target.Value, ValidatesOnDataErrors =true}"></dxe:TextEdit>
[...]
But I've no error for it(probably because it search for Target.Value
for an error. I've tried another approach, bind directly to Target
, but this doesn't work, because I need the convertBack to known which Unit was used originally to rebuild a PhysicalValue.
How would you solve this?
The EditValue property of your TextEdit control is bound to PhysicalValue.Value
. When you set Binding.ValidatesOnDataErrors
to true, the binding engine checks if the class that has the target property implements IDataErrorInfo
.
So for this to work the PhysicalValue
class needs to implement IDataErrorInfo
:
class PhysicalValue : IDataErrorInfo
{
public double Value { get; set; }
public object Unit { get; set; }
public string Error => this["Value"] + this["Unit"];
public PhysicalValue(int v, object u)
{
Value = v;
Unit = u;
}
public string this[string columnName]
{
get
{
if (columnName == "Value")
{
if (Value > 5000)
{
return "out of spec value";
}
}
return String.Empty;
}
}
}
If the PhysicalValue class can not hold the validation logic or can not implement the IDataErrorInfo
interface you could create a proxy class that handles the validation and bind to that instead. Here is a small example:
class PhysicalValueValidator : IDataErrorInfo
{
private readonly PhysicalValue _physicalValue;
private double _maxValue;
public double Value
{
get { return _physicalValue.Value; }
set { _physicalValue.Value = value; }
}
public PhysicalValueValidator(PhysicalValue pv)
{
_physicalValue = pv;
_maxValue = 5000;
}
public void SetMaxValue(double maxValue)
{
_maxValue = maxValue;
}
public string this[string columnName]
{
get
{
if (columnName == "Value")
{
if (Value > _maxValue)
{
return "out of spec value";
}
}
return String.Empty;
}
}
public string Error => this["Value"];
}
The PhysicalValueValidator
class exposes the SetMaxValue(..)
method to update the validation logic from your panes viewmodel. The viewmodel could look like this:
class MyViewModel
{
private PhysicalValue _target;
public PhysicalValueValidator TargetValidator { get; }
public MyViewModel()
{
_target = new PhysicalValue(5, 10);
TargetValidator = new PhysicalValueValidator(_target);
// update validation Logic...
TargetValidator.SetMaxValue(6000);
}
}
In your xaml code, bind to TargetValidator.Value instead of Target.Value.
For more information on IDataErrorInfo check this link: IDataErrorInfo