Любая попытка изменить 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 и всех стилей, и
- Необходимость скопировать свойства связывания в новую привязку для изменения конвертера, поскольку оригинальное связывание закрыто
чтобы прикрепить это свойство к элементу внутри ControlTemplate вместо того, чтобы делать это в стиле, и тот же код используется, за исключением исходного объекта, переданного в PropertyChangedCallback отливают var element = (FrameworkElement)obj;
и внутри действия Dispatcher.BeginInvoke фактическое TextBox находится с var box = (TextBox)element.TemplatedParent;
Большое спасибо! Это отлично поработало. Я только должен был изменить копию новой привязки, поскольку Source и RelativeSource не могут быть установлены одновременно (даже жесткие они установлены в NULL). Поэтому я добавил некоторые проверки перед их назначением. Если NULL, я не устанавливаю свойство. – joerage
Спасибо за отзыв. Я обновил свой ответ, чтобы сделать то же самое. –