Любая попытка изменить свойства TextBox
изнутри ControlTemplate
будет грязной, поэтому я рекомендую делать это в Style
вместо ControlTemplate
, если это вообще возможно. Я начну с объяснения, как это сделать с Style
, а затем объясню, как адаптировать технику для использования в ControlTemplate
при необходимости.
Что вам нужно сделать, это создать прикрепленное свойство, которое можно использовать следующим образом:
<Style x:Name="TextBoxStyleBase2" TargetType="TextBox">
<Setter Property="local:ConverterInstaller.TextPropetyConverter"
Value="{StaticResource MyConverter}" />
...
</Style>
Класс ConverterInstaller
имеет простое свойство присоединения, которое устанавливает конвертер в любой Binding
, изначально установленный в 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);
}));
}
});
}
Единственная сложность здесь:
- Использование Dispatcher.BeginInvoke для обеспечения обновления привязки после завершения загрузки XAML и применения всех стилей, а также
- Необходимость копирования свойств Binding в новый Binding для изменения конвертера, поскольку исходная Binding запечатана
Чтобы прикрепить это свойство к элементу внутри ControlTemplate вместо того, чтобы делать это в стиле, используется тот же код, за исключением того, что исходный объект, переданный в PropertyChangedCallback, приводится var element = (FrameworkElement)obj;
, а внутри действия Dispatcher.BeginInvoke фактический TextBox найдено с var box = (TextBox)element.TemplatedParent;