Использование преобразователей значений в WPF без необходимости сначала определять их как ресурсы - PullRequest
19 голосов
/ 21 февраля 2010

Можно ли использовать преобразователи значений, не определяя их заранее как ресурсы?

Прямо сейчас у меня есть

<Window.Resources>
    <local:TrivialFormatter x:Key="trivialFormatter" />
</Window.Resources>

и

<Button Width="{Binding Width, 
               ElementName=textBox1, 
               UpdateSourceTrigger=PropertyChanged, 
               Converter={StaticResource trivialFormatter}}">

Возможно ли, что вместо объявления ресурса trivialFormatter в Window.Resources я мог напрямую ссылаться на него из привязки ширины Button? Что-то вроде

Converter = {local:TrivialFormatter}

Спасибо

Ответы [ 4 ]

24 голосов
/ 21 февраля 2010

В случае синглтонового типа IValueConverter с (например, им не нужно никакого состояния из текущего экземпляра привязки), я использую статические преобразователи, т.е.

Converter={x:Static SomeNamespace:SomeConverter.Instance}

Д-р WPF также написал замечательную статью об использовании расширения разметки, чтобы сделать его более понятным: здесь .

6 голосов
/ 21 февраля 2010

Технически я считаю, что вы можете сделать это, но XAML настолько ужасен, что из-за этого подход «много тривиальных ресурсов» выглядит как гавань простоты и ясности:

<Button Height="50">
  <Button.Width>
    <Binding Path="Width" ElementName="textBox1" UpdateSourceTrigger="PropertyChanged">
      <Binding.Converter>
        <local:TrivialFormatter />
      </Binding.Converter>
    </Binding>
  </Button.Width>
</Button>

Я не проверял это, потому что даже чтение заставляет мои глаза слезиться ...

5 голосов
/ 21 февраля 2010

Я бы определенно рассмотрел предложение Мики, которое предполагает использование статического одноэлементного экземпляра вашего конвертера. Но следует учитывать и то, что если вы используете отдельный шаблон представления, такой как MVVM, вы часто можете избежать требования к преобразователю значений, реализовав преобразование во ViewModel.

Есть много причин, по которым вы можете захотеть сделать это.

С одной стороны, это гораздо более тестируемо. Ваши модульные тесты могут быть уверены, что все, что выходит из ViewModel, будет отображаться пользовательским интерфейсом. Вы можете представить себе тестирование требования, согласно которому значения в долларах должны соответствовать формату валюты текущей культуры, должны использоваться два десятичных знака и т. Д.

Еще одна веская причина в том, что исключения в преобразователях значений не будут рассматриваться как ошибки проверки, которые могут быть огромной проблемой в Silverlight. Даже если вы установите для ValidatesOnExceptions значение true в привязке, если преобразователь значений выдает исключение, Silverlight просто распространит его. Однако, если вы используете ViewModel для преобразования, исключение будет рассматриваться как ошибка проверки.

Недостатком является то, что вы теряете часть «повторного использования» преобразователя значений общего назначения.

2 голосов
/ 21 февраля 2010

Я не знаю способа сделать это так, как вы заявляете, но я просто попробовал это как пример, и это сработало. В вашем файле App.xaml.cs вы можете создать метод, который использует отражение для загрузки конвертеров.

private void Application_Startup(object sender, StartupEventArgs e)
{
    LoadConverters();
}

private void LoadConverters()
{
    foreach(var t in Assembly.GetExecutingAssembly().GetTypes())
    {
        if (t.GetInterfaces().Any(i => i.Name == "IValueConverter"))
        {
            Resources.Add(t.Name, Activator.CreateInstance(t));
        }
    }
}

Тогда вы можете использовать конвертер примерно так же, как я полагаю.

<Button Width="{Binding Width, Converter={StaticResource TrivialFormatter}}" />

Проблема с подходом, который вы предлагаете, заключается в том, что парсер Xaml не знает, когда и сколько экземпляров вашего конвертера нужно создать. Создание его в качестве ресурса обеспечит только один экземпляр.

...