Я смотрю на инициализацию членов универсальных типов, объявленных в XAML. Это нацелено на поддержку (уменьшенная) обобщенных шаблонов в WPF 4 и будущем Silverlight. (Я пробовал приведенные ниже сценарии с использованием x:TypeArguments
и XamlReader.Load
в VS2010 Beta 2, но для простоты буду использовать TestClassInt32 : TestClass<int> { }
, так как он работает так же, как и с использованием универсального типа напрямую, но его проще протестировать с помощью скомпилированного xaml сегодня.)
Вот типы тестов, которые я использую.
public class TestClass<T> {
[TypeConverter( typeof(StringListToItemsConverter) )]
public IEnumerable<T> Items { get; set; }
public TestProperty<T> Property { get; set; }
}
[TypeConverter( typeof(TestPropertyConverter) )]
public struct TestProperty<T> {
public TestProperty( T value ) : this() { Value = value; }
public T Value { get; }
}
Вот пример сценария.
<StackPanel>
<StackPanel.DataContext>
<test:TestClassInt32 Items="1,2,3" Property="6" />
</StackPanel.DataContext>
<TextBox Text="{Binding Property.Value}" />
<ItemsControl ItemsSource="{Binding Items}" />
</StackPanel>
Когда я жестко кодирую typeof(int)
в преобразователях для этого примера, все работает нормально, но этот подход, очевидно, не работает для TestClass<double>
или TestClass<DateTime>
. Проблема в том, что метод TypeConverter.ConvertFrom
не имеет доступа к типу назначения, только к типу источника. (Это не было проблемой, когда TypeConverter
был создан в .NET 1.0, потому что тип назначения не мог быть параметризован, но сейчас это нежелательное ограничение.)
Вот подходы, которые я рассмотрел, чтобы обойти эту проблему:
- Сделать конвертер типов общим; например
[TypeConverter( typeof(TestPropertyConverter<T>) )]
- .NET не поддерживает параметры типа в атрибутах
- Пусть
TypeConverter
возвращает промежуточный тип, который либо реализует IConvertible.ToType
, либо имеет TypeConvert
, который может ConvertTo
тип назначения
- Парсер XAML выполняет только одношаговое преобразование: если возвращаемый объект не может быть назначен месту назначения, он генерирует исключение
- Определите пользовательский дескриптор типа, который будет возвращать соответствующий конвертер на основе фактического типа
- WPF игнорирует дескрипторы пользовательских типов, а
TypeDescriptor
даже не существует в Silverlight
Вот альтернативы, которые я предложил для «обхода» этой проблемы:
- Требовать, чтобы тип назначения был встроен в строку; например
"sys:Double 1,2,3"
- В Silverlight необходимо жестко закодировать фиксированный набор поддерживаемых типов (в WPF можно использовать интерфейс
IXamlTypeResolver
для получения фактического типа, которому "sys:Double"
соответствует)
- Написать пользовательское расширение разметки, которое принимает параметр типа; например
"{List Type={x:Type sys:Double}, Values=1,2,3}"
- Silverlight не поддерживает настраиваемые расширения разметки (также не поддерживается расширение разметки
x:Type
, но вы можете использовать жестко заданный подход из варианта 1)
- Создайте тип оболочки, который принимает параметр типа и переопределяет все универсальные члены как
object
, перенаправляя все обращения к элементам в базовый строго типизированный объект
- Возможно, но создает очень плохой пользовательский интерфейс (приходится приводить для получения базового универсального объекта, все равно приходится использовать жестко закодированный список для параметра типа в Silverlight, приходится кэшировать назначения членов до тех пор, пока не будет назначен аргумент типа, и т. Д. и т. д .; обычно теряет большинство преимуществ строгой типизации с помощью дженериков)
Был бы рад услышать любые другие идеи для решения этой проблемы сегодня или в WPF 4.