2009-03-24 3 views
1

Я хотел бы реализовать часть ViewModel шаблона MVVM WPF без ссылки на сборки WPF. Проблематичной частью является командная маршрутизация, которая требует, чтобы ViewModels реализовывали свойства типа ICommand, так что командные привязки могут работать.Ввод свойств в классы .NET после компиляции

Теперь, я могу избежать ICommand и просто объявить свойства object. Все еще работает, вот и все. Но что меня беспокоит, Мне еще нужно объявить их, и я действительно этого не хочу, потому что они чувствуют себя как код котельной плиты.

Мои ViewModels в настоящее время выглядит следующим образом:

public class HelloWorldViewModel : ViewModel 
{ 
    [BoundProperty] 
    public string Name { get; set; } 

    [CommandHandler("SayHello")] 
    public bool CanSayHello() 
    { 
     return Name != "" && Name != null; 
    } 

    [CommandHandler("SayHello")] 
    public void SayHello() 
    { 
     View.ShowMessage("Hello, {0}!", Name); 
    } 

    public object SayHello { get; private set; } 
} 

CommandHandlerAttribute обеспечивает обнаружение выполнения обработчиков команд (в Action и дополнительный Func<bool>), в то время как BoundPropertyAttribute действительно аспект, который внедряется в собственность сеттер и звонки INotifyPropertyChanged. Я сопровождаю это, используя время компиляции IL weaver.

В идеале, я хотел бы также сделать последнюю строку (свойство SayHello) тоже неявной. Не было бы смысла иметь его там в источнике, если бы не требование WPF.

Так что, естественно, я думаю об использовании аспекта CommandHandlerAttribute для ввода необходимого ИЛ в класс и, по существу, , создающего свойство после компиляции. Это довольно сложно, хотя хороший искатель IL (например, PostSharp) может пройти долгий путь, чтобы сделать его проще.

Прежде чем я отправимся в это путешествие, я хотел бы услышать, что вы все думаете о моем подходе. Это звучит? Есть ли способ лучше? Как вы это сделаете?

ответ

3

Для меня это звучит слишком умно. Слишком много «магии». В частности, мне не нравятся магические строки и другие аспекты вашего CommandHandlerAttribute. Тем не менее, если бы я пошел по этому пути, я бы использовал нечто похожее на EventAggregator, но для команд. IOW, SayHello вообще не существовало бы на вашем ViewModel. То, что когда-либо создавало магию, создает привязки команд к SayHell() и CanSayHello(), вместо этого найдет команду в глобальном CommandAggregator. До тех пор, пока мы используем магические строки для этого, команды в CommandAggregator могут создаваться лениво, поэтому для вас не требуется кодирование «котельной пластины». Осталось только создать некоторую магию XAML (расширение разметки), чтобы указать команду на ICommandSource.

<Button Command="{my:AggregateCommand SayHello}"/> 
0

Мое личное мнение в том, что это интересно, но я бы вообще этого избегал.

Избегание кода котельной плиты (или кода, который напоминает код плиты котла) имеет последствия. Это может показаться хорошей идеей, поскольку вы постоянно не перепечатываете вещи, но в конечном итоге вы делаете ее менее читаемой и понятной.

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

1

Некоторое время после игры с Prism, но прежде, чем я буду казаться материалом MVVM, я придумал стратегию, которую я до сих пор считаю некоторая валидность:

Я создал реализацию интерфейса ICommand на основе отражения. Конструктор принял целевой объект и имя операции. Используя отражение, код искал метод с именем «[операция]», свойство или метод с именем «Can [operation]» или «[operation] Enabled» и событие с именем «Can [operation] Changed» или «[ operation] Enabled Changed ". Только первый был необходим, но отраженный метод/свойство/событие были подключены к довольно простой реализации интерфейса ICommand.

Затем я создал реализацию IValueConverter, которая создаст экземпляр предыдущего класса, передав значение, которое будет преобразовано в качестве целевого объекта, и параметр преобразователя, являющийся именем операции.

Учитывая приведенные выше компоненты, я смог, например, связать свойство команды кнопки непосредственно с источником операции (вместе с указанием преобразователя) и установить для свойства Button ButtonParameter имя операции , Таким образом, я получил декларативную привязку команды без источника команды, имеющего плотское знание чего-либо WPF.

+0

Я не люблю использовать отражение с названиями «волшебными» метода и свойства. Я часто встречался в прошлом, я переименовываю метод, а затем неожиданный бит UI перестает работать без компилятора, дающего мне ошибку. Однако мне нравится ваш базовый дизайн, как насчет лямбда-выражений для подключения к сети? –

+0

Мне очень нравится идея лямбда. Возможно, я все же предпочел бы открыть свойство/событие «включено» через отражение (по крайней мере, по умолчанию), поскольку это упростит интерфейс. –

0

Лучший способ в вашем случае - прокси или декоратор. Я думаю. Вы можете создавать объекты с низким уровнем, которые обернуты/украшены элементами элементов интерфейса UI/WPF во время выполнения. Это самый простой, но эффективный способ сэкономить ваше время и не беспокоить каркасы, инъекции и т. Д.

Единственное, что вам нужно, - это создать небольшую инфраструктуру, чтобы обернуть ваши объекты соответствующими декораторами.

2

я совет Вы, чтобы увидеть, как это было реализовано, и это поможет:

"Kind Of Magic" Effortless INotifyPropertyChanged

[http://visualstudiogallery.msdn.microsoft.com/ d5cd6aa1-57a5-4aaa-a2be-969c6db7f88a] [1]

в качестве примера для добавления его к одному свойству:

[Magic] 
public string Name { get { return _name; } set { _name = value; } } 
string _name; 

Другой пример для добавления его ко всем свойствам класса:

[Magic] 
public class MyViewModel: INotifyPropertyChanged 
{ 
    public string Name { get; set; } 
    public string LastName { get; set; } 
    ..... 
} 
Смежные вопросы