Использование преобразователей Value в WPF без необходимости сначала определять их как ресурсы

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

Сейчас у меня

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

и

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

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

Converter = {local:TrivialFormatter}

Спасибо

Ответы

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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

Ответ 4

Я не знаю, как это сделать, как вы заявляете, но я просто попробовал это как образец, и он сработал. В файле 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 не знает, когда и сколько экземпляров вашего конвертера нужно создать. Создание его в качестве ресурса обеспечивает только один экземпляр.