Единицы WPF и Code-Behind

Недавно я обнаружил, что WPF поддерживает различные единицы измерения в XAML. Помимо DIP-модулей по умолчанию, есть также поддержка пикселей, дюймов и сантиметров (насколько я знаю). Это позволяет дизайнеру написать XAML, например:

<Canvas>
    <Line X1="0cm" X2="3cm" Y1="1cm" Y2="3cm" Stroke="Black"/>
</Canvas>

Однако вы не можете привязать эти значения. Представьте, что у нас есть свойство ViewModel с свойством Dimension, которое является строкой, например "7 см". Следующее не будет работать:

<Button Width="{Binding Dimension}">Test</Button>

Отключается форматированиеException. Аналогично, при создании FrameworkElement в кодировке, например:

Canvas1.Children.Add(new Button() { Width = "3cm", Content = "Test"});

Ошибка компиляции из-за исключения в конструкторе/везде, где вы пытаетесь создать элемент управления.

Интересно:

  • Можно ли использовать натуральные единицы (в моем случае метрические сантиметры) в коде?
  • Как?
  • Имеется ли полный список устройств, поддерживаемых WPF/XAML?

EDIT:

Ниже приведена ссылка с комментария, которая решает этот вопрос:

http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.width.aspx

Ответы

Ответ 1

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

button.Width = (double)new LengthConverter().ConvertFrom("2cm");

XAML-парсер использует TypeConverters для преобразования строк в нужные значения в страницы Width, вы можете увидеть, что она имеет TypeConverterAttribute, указав, что LengthConverter. Этот атрибут можно использовать для локального переопределения того, как должно обрабатываться свойство, но оно также может применяться на уровне класса, поэтому, если класс Employee имеет этот атрибут, указывающий, что должен использоваться EmployeeConverter, указанный конвертер будет использоваться по умолчанию для свойств типа Employee.

Я немного удивлен, что привязка не применяется к этому конвертеру типов, но использование его внутри IValueConverter было бы достаточно простым, на самом деле вы могли бы создать расширение расширений, которое создает IValueConverter из преобразователя типов, чтобы сделать его общепринятым.

Ответ 2

Для 1) и 2) Поскольку DIP являются независимыми от устройства, вы можете вычислить любую "естественную единицу" без проблем. Исключение составляют (неестественные:) пиксели, зависящие от устройства.

Создайте ValueConverter, который преобразует строковые значения в двойные, где в строке могут быть указаны ваши нужные единицы. Затем вы можете использовать ValueConverter в своем Binding и вычислить в ViewModel в "естественных единицах".

Для 3) и реализации: я быстро искал и ничего не нашел, но, возможно, будет более глубокий поиск. В противном случае создание собственного конвертера не должно быть очень сложным. Здесь информация о конверсии:

96DIP == 1inch == 2.54cm;
1cm == 37.8DIP;
1zoll == 1inch == 2.54cm;