WPF - применить конвертер ко всем текстовым полям - PullRequest
2 голосов
/ 31 мая 2010

Используя WPF, я хочу применить конвертер в привязке свойства Text во всех моих текстовых окнах.
Следующее работает для одного TextBox:

<TextBox Style="{StaticResource TextBoxStyleBase2}" 
                             Text="{Binding Text, Converter={StaticResource MyConverter}}">
                    </TextBox>

Однако в наших текстовых полях используется стиль с шаблоном управления, который выглядит следующим образом:

<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>

Как я могу применить мой конвертер, используя этот шаблон? Спасибо!

1 Ответ

2 голосов
/ 05 июня 2010

Любая попытка изменить свойства 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;

...