Is it possible to make something to set StringFormat of Binding property for all (or part of) TextBoxes in project? Yes, I can write something like
<TextBox x:Name="SetPosition" Style="{StaticResource TextBoxSmallStyle}" Text="{Binding SetPosition, Mode=TwoWay, StringFormat='{}{0:#0.0}'}" />
but setting it for many identical TextBoxes it too boring ))) As you see I am used Style which include Heigth, Width e.t.c. I can't override Binding property in Style because need to use this Style with any Binding Path properties but Binding overrides only wholly. Are where any workarounds exists? P.S. I am already using not standard TextBox but overrided control for my special functionality. Can I override Binding for using compoment's code-behind, maybe?
Since you said you have already extended TextBox
you can simply enhance that type and modify the existing Binding
on the TextBox.Text
property.
The following example shows how the ExtendedTextBox
allows to provide a string format expression which is assigned to the current Binding
.
Any locally set Binding.StringFormat
values are overwritten with the ExtendedTextBox.TextStringFormat
property value.
If you also want to apply the string formatting when there is no Binding
set on the Text
property, you would have to monitor the Text
property for changes.
public class ExtendedTextBox : TextBox
{
public string TextStringFormat
{
get => (string)GetValue(TextStringFormatProperty);
set => SetValue(TextStringFormatProperty, value);
}
public static readonly DependencyProperty TextStringFormatProperty = DependencyProperty.Register(
"TextStringFormat",
typeof(string),
typeof(ExtendedTextBox),
new PropertyMetadata(default(string), OnTextStringFormatChanged));
private static void OnTextStringFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var extendedTextBox = (ExtendedTextBox)d;
string oldFormatString = (string)e.OldValue;
string newFormatString = (string)e.NewValue;
if (!string.IsNullOrWhiteSpace(newFormatString))
{
extendedTextBox.ApplyBindingStringFormat();
}
else if (!string.IsNullOrWhiteSpace(oldFormatString))
{
extendedTextBox.ClearTextPropertyFormat();
}
}
private bool isFormattingTextProperty;
public ExtendedTextBox()
{
this.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
=> FormatTextProperty();
protected override void OnTextChanged(TextChangedEventArgs e)
{
if (this.isFormattingTextProperty)
{
return;
}
base.OnTextChanged(e);
BindingBase textBinding = BindingOperations.GetBindingBase(this, TextBox.TextProperty);
if (textBinding is null)
{
// No binding attached. Format numeric text directly.
FormatTextProperty();
}
}
private void FormatTextProperty()
{
if (string.IsNullOrWhiteSpace(this.TextStringFormat))
{
return;
}
if (!string.IsNullOrWhiteSpace(this.Text))
{
string formattedText;
if (double.TryParse(this.Text, CultureInfo.CurrentCulture, out double number))
{
formattedText = string.Format(CultureInfo.CurrentCulture, this.TextStringFormat, number);
}
else
{
formattedText = string.Format(CultureInfo.CurrentCulture, this.TextStringFormat, this.Text);
}
this.isFormattingTextProperty = true;
int currentCaretIndex = this.CaretIndex;
SetCurrentValue(TextBox.TextProperty, formattedText);
this.CaretIndex = currentCaretIndex;
this.isFormattingTextProperty = false;
}
}
// Binding attached. Apply formatting via data binding.
private void ApplyBindingStringFormat()
{
BindingBase textBinding = BindingOperations.GetBindingBase(this, TextBox.TextProperty);
if (textBinding is null)
{
return;
}
if (string.IsNullOrWhiteSpace(textBinding.StringFormat))
{
textBinding = CloneObject(textBinding);
textBinding.StringFormat = this.TextStringFormat;
_ = SetBinding(TextBox.TextProperty, textBinding);
}
}
private void ClearTextPropertyFormat()
{
BindingBase textBinding = BindingOperations.GetBindingBase(this, TextBox.TextProperty);
if (textBinding is null)
{
return;
}
textBinding = CloneObject(textBinding);
if (!string.IsNullOrWhiteSpace(textBinding.StringFormat))
{
textBinding.StringFormat = null;
_ = SetBinding(TextBox.TextProperty, textBinding);
}
}
private TObject CloneObject<TObject>(TObject objectToClone)
{
string xamlObject = XamlWriter.Save(objectToClone);
return (TObject)XamlReader.Parse(xamlObject);
}
}
App.xaml
Define a global Style
that defines the actual string format:
<Style TargetType="ExtendedTextBox">
<Setter Property="TextStringFormat"
Value="{}{0:#0.0}" />
</Style>