Reputation: 6091
I have a property, bound to a TextBox
, which is only allowed to be a positive number (i.e. x > 0
). Inspired by this post I have decided to implement this using IDataErrorInfo interface.
Following the instructions in that post, I can get a tool-tip warning to show, if the input cannot be validated. But I would like to have the validation warning shown in a seperate TextBlock
, just below the input TextBox
.
XAML:
<!-- ValidatingControl Style -->
<Style TargetType="{x:Type FrameworkElement}" x:Key="ValidatingControl">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding
Path=(Validation.Errors)[0].ErrorContent,
RelativeSource={x:Static RelativeSource.Self}}" />
</Trigger>
</Style.Triggers>
</Style>
(...)
<TextBlock Text="Product price:" />
<TextBox
Name="Price"
DataContext="{Binding SelectedProduct}"
Style="{StaticResource ValidatingControl}"
Text="{Binding Path=Price,
StringFormat=N0,
ConverterCulture=da-DK,
Mode=TwoWay,
ValidatesOnDataErrors=True}"/>
<!-- This should contain validation warning -->
<TextBlock Margin="0 0 0 10" />
Binding property (C#):
public class ProductModel : IDataErrorInfo
{
public decimal Price { get; set; }
(...)
// Implementation of IDataErrorInfo
string IDataErrorInfo.Error
{
get { return null; }
}
string IDataErrorInfo.this[string columnName]
{
get
{
if (columnName == "Price")
{
// Validate property and return a string if there is an error
if (Price < 0)
return "Cannot be negative.";
}
// If there's no error, null gets returned
return null;
}
}
}
Upvotes: 0
Views: 106
Reputation: 169280
You need to define the indexer as public property, implement the INotifyPropertyChanged
interface and raise a change notification for the indexer when the Price
property is set. This should work:
public class ProductModel : IDataErrorInfo, INotifyPropertyChanged
{
private decimal _price;
public decimal Price
{
get { return _price; }
set
{
_price = value;
NotifyPropertyChanged();
NotifyPropertyChanged("Item[]");
}
}
string IDataErrorInfo.Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
if (columnName == "Price")
{
// Validate property and return a string if there is an error
if (Price < 0)
return "Cannot be negative.";
}
// If there's no error, null gets returned
return null;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<!-- This should contain validation warning -->
<TextBlock DataContext="{Binding SelectedProduct}" Margin="0 0 0 10" Text="{Binding [Price]}" />
Upvotes: 1
Reputation: 930
You can bind your textblock to IDataError interfaces indexer property.
Here modified code
<StackPanel>
<TextBlock Text="Product price:" />
<TextBox Name="Price" Text="{Binding Path=Price, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, ValidatesOnDataErrors=True}"/>
<!-- This should contain validation warning -->
<TextBlock Margin="0 0 0 10" Text="{Binding [Price]}" />
</StackPanel>
Also you need to do some modifications to your view model.
class ViewModel : IDataErrorInfo, INotifyPropertyChanged
{
decimal _price;
public decimal Price
{
get => _price;
set
{
_price = value;
RaisePropertyChanged(nameof(Price));
RaisePropertyChanged("Item[]");
}
}
// Implementation of IDataErrorInfo
string IDataErrorInfo.Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
if (columnName == "Price")
{
// Validate property and return a string if there is an error
if (Price < 0)
return "Cannot be negative.";
}
// If there's no error, null gets returned
return null;
}
}
void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Note, that indexer property for IDataErrorInfo interface is implemented implicitly, otherwise WPF system for some reason can't bind to it.
Also take a look to the INotifyPropertyChanged interface implementation for Price property, especially on the RaisePropertyChanged("Item[]");
line. Without this line WPF's system binding system will not know, if there is an error.
Upvotes: 1