Any attempt to modify TextBox
properties from inside the ControlTemplate
will be messy, so I recommend you do it in the Style
instead of the ControlTemplate
if at all possible. I'll start by explaining how to do it with a Style
then explain how to adapt the technique for use in a ControlTemplate
if necessary.
What you need to do is create an attached property that can be used like this:
<Style x:Name="TextBoxStyleBase2" TargetType="TextBox">
<Setter Property="local:ConverterInstaller.TextPropetyConverter"
Value="{StaticResource MyConverter}" />
...
</Style>
The ConverterInstaller
class has a simple attached property that installs the converter into any Binding
initially set on the TextBox
:
public class ConverterInstaller : DependencyObject
{
public static IValueConverter GetTextPropertyConverter(DependencyObject obj) { return (IValueConverter)obj.GetValue(TextPropertyConverterProperty); }
public static void SetTextPropertyConverter(DependencyObject obj, IValueConverter value) { obj.SetValue(TextPropertyConverterProperty, value); }
public static readonly DependencyProperty TextPropertyConverterProperty = DependencyProperty.RegisterAttached("TextPropertyConverter", typeof(IValueConverter), typeof(Converter), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
var box = (TextBox)obj;
box.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
var binding = BindingOperations.GetBinding(box, TextBox.TextProperty);
if(binding==null) return;
var newBinding = new Binding
{
Converter = GetTextPropertyConverter(box),
Path = binding.Path,
Mode = binding.Mode,
StringFormat = binding.StringFormat,
}
if(binding.Source!=null) newBinding.Source = binding.Source;
if(binding.RelativeSource!=null) newBinding.RelativeSource = binding.RelativeSource;
if(binding.ElementName!=null) newBinding.ElementName = binding.ElementName;
BindingOperations.SetBinding(box, TextBox.TextProperty, newBinding);
}));
}
});
}
The only complexity here is:
- The use of Dispatcher.BeginInvoke to ensure the binding update happens after the XAML finishes loading and all styles are applied, and
- The need to copy Binding properties into a new Binding to change the Converter, since the original Binding is sealed
To attach this property to an element inside the ControlTemplate instead of doing it in the style, the same code is used except the original object passed into the PropertyChangedCallback is cast var element = (FrameworkElement)obj;
and inside the Dispatcher.BeginInvoke action the actual TextBox is found with var box = (TextBox)element.TemplatedParent;