WPF - Apply a converter to all textboxes
Using WPF, I want to apply a converter within the binding of the Text property within all my TextBoxes.
The following works for a single TextBox:<TextBox Style="{StaticResource TextBoxStyleBase2}"
Text="{Binding Text, Converter={StaticReso开发者_如何转开发urce MyConverter}}">
</TextBox>
However, our TextBoxes uses a style with a Control Template that looks like this:
<Grid>
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{StaticResource DefaultCornerRadius}">
<Grid>
<Border BorderThickness="1">
<ScrollViewer x:Name="PART_ContentHost" Margin="0"/>
</Border>
</Grid>
</Border>
</Grid>
How can I apply my converter using this Template? Thanks!
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;
精彩评论