I'm trying to make a UserControl based on a TextBox, and when it loose focus the Text is errased if it doesn't match a Regex.
My problem is the following : I have binded the Text property of the TextBox with a DependencyProperty named Text in my UserControl, but when i'm writing wrong Text in the TextBox then make it loose focus, it doesn't do anything.
UserControl XAML :
<Grid>
<TextBox VerticalContentAlignment="Center" Text="{Binding Text, UpdateSourceTrigger=LostFocus, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" FontFamily="{Binding FontFamily, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" FontSize="{Binding FontSize, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" />
</Grid>
UserControl Code Behind CS (with DependencyProperty) :
// Text of the FormatBox
public static readonly DependencyProperty CS_EXAMPLETEXT_PROPERTY = DependencyProperty.Register(nameof(Text), typeof(String), typeof(FormatBox));
public string Text
{
get { return (string)GetValue(CS_EXAMPLETEXT_PROPERTY); }
set {
if (Regex.IsMatch(value ?? "", RegexString ?? "")) SetValue(CS_EXAMPLETEXT_PROPERTY, value);
else SetValue(CS_EXAMPLETEXT_PROPERTY, "");
}
}
MainWindows XAML :
<!-- Test FormatBox -->
<controls:FormatBox Grid.Row="3" FontFamily="Calibri" FontSize="16" RegexString="^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$" />
But if I try to do the same thing with a normal property and implement INotifyPropertyChanged it works like a charm.
UserControl Code Behind CS (with normal property) :
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private Dictionary<string, object> _propertyValues = new Dictionary<string, object>();
protected T GetProperty<T>([CallerMemberName] string propertyName = null)
{
if (_propertyValues.ContainsKey(propertyName)) return (T)_propertyValues[propertyName];
return default(T);
}
protected bool SetProperty<T>(T newValue, [CallerMemberName] string propertyName = null)
{
T current = GetProperty<T>(propertyName);
if (!EqualityComparer<T>.Default.Equals(current, newValue))
{
_propertyValues[propertyName] = newValue;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
return true;
}
return false;
}
#endregion
// Text of the FormatBox
public string Text
{
get { return GetProperty<string>(); }
set {
if (Regex.IsMatch(value ?? "", RegexString ?? "")) SetProperty<string>(value);
else SetProperty<string>("");
}
}
Could you help me making it work with a DependencyProperty ?
CodePudding user response:
I hope I understand your question correctly. By "it doesn't do anything", if you meant why the conditional code you have included in the .Net Wrapper properties of Dependency property is not executed, please be aware that the .Net Wrapper Properties would be bypassed in runtime.
You can read more on this on Xaml Loading and Dependency Properties .
Coming back to the custom logic you need to implement, this is precisely why the CoerceValueCallback exist.The CoerceValueCallback is executed as one of the last steps in the Property Value Evaluation and is ideal for such corrections in the value.
For example,
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text),
typeof(string),
typeof(SampleControl),
new PropertyMetadata(
string.Empty, null, new CoerceValueCallback(OnTextValueCoerce)));
private static object OnTextValueCoerce(DependencyObject d, object baseValue)
{
if(baseValue is string val && d is SampleControl ctrl)
{
if (Regex.IsMatch(val ?? string.Empty, ctrl.RegexString ?? string.Empty))
return baseValue;
else
return string.Empty;
}
return string.Empty;
}