Re[10]: [WPF] События от ViewModel к View
От: Fortnum  
Дата: 22.10.10 09:10
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>И проблема в том, что если ты подписываешься к статическому объекту, то ты обязан от него во время отвязаться. Иначе утечка памяти. Согласись — не слишком приятная обязанность, хотя и не смертельная.

GZ>Есть стандартный INotify — не хватает возможностей биндинга, реализую свой через свои события.

Так я же не утверждаю, что на INotify надо забить! Я как раз те случаи, когда его не хватает и рассматриваю. Так что ответ "хватает и INotify" не подходит, по причине, что не хватает.

GZ>Event Aggregator — решает вышеописанную проблему, но как ты уже заметил, он предназначен для взаимодействия уровня ViewModel и ниже. Но никак не для вьюх.


Нет, ну почему. Теоретически же его можно использовать. Теоретически даже можно сделать специальный медиатор для событий ModelView->View, но проблему это не снимает. Проблему снимает другое — система наподобие Actions в Caliburn. Мне нужно то же самое только для событий ModelView->View и система автоматической подписки-отписки, как ты правильно сказал, чтобы избежать утечки памяти — это то что нужно.

Кстати, ведь какая основная проблема при подписке-отписке на события ModelView при реализации в статическом классе? Проблема в том, где хранить делегаты после подписки, чтобы потом от них отписаться. Для этого надо создать фэйковое Attached Dependency Property и в нем сохранять. Действие довольно шаблонное, проблема именно в том, что каждый раз его реализовывать вручную — проблема (у меня их уже очень много накопилось). Вот это генеральное решение я и ищу. Что, нельзя вместо Commanding/Actions в code-behind View'хи дергать команды/методы ViewModel'и? Можно. Так можно и на биндинг забить. И скатиться обратно к MVC Именно эти заранее реализованные "рюшечки" и делают WPF+MVVM тем, чем они есть сейчас.

GZ>Кстати — Event Aggregator прежде всего не прокидывание состояния, а лишь уведомление прозошедшем событии. Состояние обычно лежит либо во вьювмоделях, если оно не должно быть глобальным, либо в чем то вида репозитория.


Ну это не аксиома. Все зависит от жесткости связи между подписчиком и получателем. Если подписчик получит уведомление о событии после того как состояние изменилось, то состояние надо передавать как раз вместе с событием в Payload. И это тоже мой случай Т.к. у меня группы окон работают в разных потоках, и когда ты в одном окне (где панель навигации) нажимаешь кнопку, то в момент когда окно с ,браузером получит это уведомление, ситуация может радикально измениться и вместо того, чтобы навигицироваться, браузеру может быть уже надо будет сворачивать манатки и ползти на кладбище, если он к моменту уведомления, конечно, еще туда не уполз

GZ>RoutedCommand все таки предназначен токмо для для донесения о события только в свою вьювмодель.


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

GZ>Функциональная модель чаще совсем не совпадает с Visual, что ведет к дополнительным мыслительным процессам.


Да, тут вариантов масса
Re[3]: [WPF] События от ViewModel к View
От: Fortnum  
Дата: 22.10.10 09:29
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>представь, что во View'хе сидит ActiveX Microsoft Word'а


А кстати с вордом-то события ModelView->View не помогут... проще будет его в ModelView прокинуть А состояния, действительно, триггерить из ModelView через INotifyPropertyChanged, типа сгенери такой документ, сгенери другой. Логика создания документа будет на стороне View, а данные будут подсасываться из ModelView... нда... ситуация усложнилась
Re[4]: [WPF] События от ViewModel к View
От: Vladek Россия Github
Дата: 22.10.10 10:15
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Здравствуйте, Fortnum, Вы писали:


F>>представь, что во View'хе сидит ActiveX Microsoft Word'а


F>А кстати с вордом-то события ModelView->View не помогут... проще будет его в ModelView прокинуть А состояния, действительно, триггерить из ModelView через INotifyPropertyChanged, типа сгенери такой документ, сгенери другой. Логика создания документа будет на стороне View, а данные будут подсасываться из ModelView... нда... ситуация усложнилась


Молодец, ещё чуть-чуть и ты изобретёшь обращение контроля. Подсказка: передавать можно не только данные (состояние), но и алгоритмы (поведение).
Re[5]: [WPF] События от ViewModel к View
От: Fortnum  
Дата: 22.10.10 10:35
Оценка:
Здравствуйте, Vladek, Вы писали:

F>>>представь, что во View'хе сидит ActiveX Microsoft Word'а

F>>А кстати с вордом-то события ModelView->View не помогут... проще будет его в ModelView прокинуть А состояния, действительно, триггерить из ModelView через INotifyPropertyChanged, типа сгенери такой документ, сгенери другой. Логика создания документа будет на стороне View, а данные будут подсасываться из ModelView... нда... ситуация усложнилась

V>Молодец, ещё чуть-чуть и ты изобретёшь обращение контроля. Подсказка: передавать можно не только данные (состояние), но и алгоритмы (поведение).


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

Сегодня набросаю и залью проектик с веб-браузером, пообсуждаем?
Re[6]: [WPF] События от ViewModel к View
От: Codechanger Россия  
Дата: 22.10.10 11:03
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Здравствуйте, Vladek, Вы писали:


F>>>>представь, что во View'хе сидит ActiveX Microsoft Word'а

F>>>А кстати с вордом-то события ModelView->View не помогут... проще будет его в ModelView прокинуть А состояния, действительно, триггерить из ModelView через INotifyPropertyChanged, типа сгенери такой документ, сгенери другой. Логика создания документа будет на стороне View, а данные будут подсасываться из ModelView... нда... ситуация усложнилась

V>>Молодец, ещё чуть-чуть и ты изобретёшь обращение контроля. Подсказка: передавать можно не только данные (состояние), но и алгоритмы (поведение).


F> IoC-то он IoC, он в любом случае здесь присутствует, вопрос ведь в какой форме его лучше применить, и во всех таких случаях вообще. А так это все общие слова, в частности про передачу поведения — в отдельности бессмыслица, для начала хотя бы понять с чем эти алгоритмы будут работать.


F>Сегодня набросаю и залью проектик с веб-браузером, пообсуждаем?


Пообсуждаем
Re[6]: [WPF] События от ViewModel к View
От: Vladek Россия Github
Дата: 22.10.10 11:09
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Здравствуйте, Vladek, Вы писали:


F>>>>представь, что во View'хе сидит ActiveX Microsoft Word'а

F>>>А кстати с вордом-то события ModelView->View не помогут... проще будет его в ModelView прокинуть А состояния, действительно, триггерить из ModelView через INotifyPropertyChanged, типа сгенери такой документ, сгенери другой. Логика создания документа будет на стороне View, а данные будут подсасываться из ModelView... нда... ситуация усложнилась

V>>Молодец, ещё чуть-чуть и ты изобретёшь обращение контроля. Подсказка: передавать можно не только данные (состояние), но и алгоритмы (поведение).


F> IoC-то он IoC, он в любом случае здесь присутствует, вопрос ведь в какой форме его лучше применить, и во всех таких случаях вообще. А так это все общие слова, в частности про передачу поведения — в отдельности бессмыслица, для начала хотя бы понять с чем эти алгоритмы будут работать.


Есть три базовых формы: шаблонный метод, события и внедрение зависимостей. Здесь подходят только последние два. Механизм событий почти полностью заменён поддержкой INotifyPropertyChanged, свойствами зависимостей и триггерами данных. Внедрение зависимостей тоже вполне применимо, только придётся писать какой-то код во View. Я предпочитаю много/вообще кода не писать во View, поэтому использую INotifyPropertyChanged. В любом случае, ничто не мешает передавать во View алгоритмы (объкты-стратегии, делегаты), которые View будет вызывать и которые будут делать всю работу по обновлению View — объём кода в самом View будет минимален.

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

Для тестирования мне было бы достаточно замокировать фабрику и интерфейс браузерного окна.

F>Сегодня набросаю и залью проектик с веб-браузером, пообсуждаем?


Конечно.
Re[7]: [WPF] События от ViewModel к View
От: Fortnum  
Дата: 22.10.10 11:33
Оценка:
Здравствуйте, Vladek, Вы писали:

V>ничто не мешает передавать во View алгоритмы (объкты-стратегии, делегаты), которые View будет вызывать и которые будут делать всю работу по обновлению View — объём кода в самом View будет минимален.


ОК, объем кода будет минимален, но "алгоритмы по обновлению View", если они 'знают' о типах данных View, должны содержаться во View. Разве не так, или я чего-то не догоняю?

V>Браузерные окна создавал бы внутри ViewModel через абстрактную фабрику, которая бы и возвращала интерфейс для управления браузерным окном.


Так этот интерфейс по управлению браузером что-то должно реализовывать. По сути это что-то будет представлять собой адаптер между ModelView и типом данных уровня View (элементом WebBrowser'а), и тут уже не важно, отдельная фабрика генерит этот адаптер или сам View — главное, что со стороны View все равно присутствует специально написанная для этого сущность-адаптер. А реализованный интерфейс на адаптер во View оказывается в руках ModelView или адаптер во View держит в своих руках интерфейс с сигналами от ModelView — те же яйца вид сбоку — ничего не меняет по сути. Цель в другом — исключить написание этого адаптера, перенести его в XAML, так же как и со стандартным биндингом. Ведь если бы не было INotifyPropertyChanged/INotifyCollectionChanged и стандартных потребителей этих интерфейсов со стороны View, разве был бы возможен MVVM? Нет. Для каждой пары ModelView-View вам пришлось бы писать свой адаптер, гоняющий данные туда-сюда, и вы пришли бы к паттерну MVP.

PS. Кстати, View может подписываться на события ModelView через паттерн WeakReference.
Re: Пример с WebBrowser
От: Fortnum  
Дата: 22.10.10 13:51
Оценка:
Ну вот, сделал первую итерацию примера с WebBrowser'ом. В солюшене два проекта (1) .Core — без связи VM->View, и (2) .NotifyOnly — связь VM->View на уровне INotifyPropertyChanged, как было предложено Vladek'ом здесь
Автор: Vladek
Дата: 22.10.10
и поддержано GlebZ здесь
Автор: GlebZ
Дата: 22.10.10
.

Vladek, GlebZ, проверяйте, правильно понял вашу идею?
Re[2]: Пример с WebBrowser
От: Fortnum  
Дата: 22.10.10 15:07
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Ну вот, сделал первую итерацию примера с WebBrowser'ом. В солюшене два проекта (1) .Core — без связи VM->View, и (2) .NotifyOnly — связь VM->View на уровне INotifyPropertyChanged, как было предложено Vladek'ом здесь
Автор: Vladek
Дата: 22.10.10
и поддержано GlebZ здесь
Автор: GlebZ
Дата: 22.10.10
.

F>Vladek, GlebZ, проверяйте, правильно понял вашу идею?

Добавил здесь (3) .VMEvents — связь VM->View осуществляется через интерфейс IWebBrowserController, который реализуется ModelView'юхой. Ссылки на события ModelView'юхи держатся жесткие, поэтому отписка идет обязательная, но уже появляется возможность для генерализации, т.к. код подписки-отписки уже стандартизуем. Собственно, сейчас у меня все таким образом и сделано.

Кстати, вопрос по .NotifyOnly: а кто должен возвращать NavigationState в Undefined после выполнения действия? Добавление к NavigationStateProperty FrameworkPropertyMetadataOptions.BindsTwoWayByDefault ничего не дает — менять его значение обратно на Undefined из NavigationState_Changed нельзя, разве что только при помощи Dispatcher.BeginInvoke, но так делать нельзя. Сделал обратную смену в ModelView'юхе в NavigationState.set сразу после вызова OnPropertyChanged.
Re[3]: Пример с WebBrowser
От: Fortnum  
Дата: 22.10.10 16:10
Оценка:
А передавать в VM интерфейс из View — это вообще изврат получается. Во-первых, передавать надо WeakReference, т.к. нет гарантии, что после разъединения View & VM, VM освободит эту ссылку. Во-вторых, следовательно, переданную в VM ссылку на прокси объект надо где-то хранить, чтобы в определенный момент ее задиспозить. Диспозить надо, потому что View & VM1 могут разъединиться, View сцепится с VM2, а VM1 тем не менее ссылку на переданный ему из View интерфейс продолжит хранить... в общем, этот метод годится только для очень простых и доверительных способов общения VM c View. Зачем тогда создавать интерфейс — непонятно. Сразу в VM экземпляр View и передавать. Или же непонятно ради чего устраивать гемор с прокси-объектами, WeakReference'ами, IDisposable и т.п. Плохой способ.

В общем, мой способ с интерфейсами самый лучший, надо только его обобщить, чтобы подписка-отписка автоматически происходила по соглашениям каким-нибудь.
Re: [WPF] События от ViewModel к View
От: Vasvasvas2004  
Дата: 22.10.10 19:40
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>События от View к ViewModel — это команды. А как общепринято делать наоборот, если инициатор события ModelView? Есть какие-нибудь распространенные фреймворки, решающие эту проблему (в Caliburn не нашел)?


F>Я сейчас делаю кажется тупо: создаю интерфейс IMyInterface с событиями и состоянием, а также соответствующий ему Attached Property. В последнем в DataContextChanged пытаюсь получить у ModelView этот интерфейс, привести состояние View к текущему состоянию ModelView и подписываюсь на события. Уже такая ооочень нехилая библиотека этих интерфейсов/attached propert'ей нарисовалась, хочется создать генеральное решение.



Если я правильно понимаю, то это больше похоже на а-ля дистрибутивное приложение. В дистрибутивном приложении 2 клиента используют параметр сервера. В простейшем варианте один клиент поменял параметр, тогда и другой клиент должен поменять тоже. Или скажем сервер поменял сам и тогда оба клиента тоже должны поменять.
Так что модель должна одна и как быть сервером с первичными параметрами. Есть 2 варианта. В первом варианте каждый параметр сервера обеспечивает подписку и тогда каждый клиент подписывается на событие. У параметра массив всех клиентов. Клиент подписывается, а фактически параметр заносит в свой массив этого клиента. Когда параметр меняется, то проходится по всему массиву ...
Re[6]: [WPF] События от ViewModel к View
От: Fortnum  
Дата: 23.10.10 00:51
Оценка:
Здравствуйте, GlebZ, Вы писали:

F>>Как вы относитесь к описанному выше?

GZ>Положительно, но есть один вопрос. Вьюха всегда знает о ВьювМодели. Это обратная зависимость вредна. Подкладываение различных вьювмоделей под одну и ту же вьюху — процесс весьма редкий. Даже скажу чрезвычайно редкий. Поэтому непонятно зачем вывод эвентов через интерфейс, а не напрямую.

Кажись вник в Prism — во всех примерах либо Вид держит ссылку на Презентер, либо Презентер держит ссылку на вид. Тупо как поле/свойство. А я, блин, зачем-то все время в голове отсекаю такие варианты — действительно, все время пытаюсь заставить себя писать контролы так, чтобы МодельВид под ними мог в любой момен измениться, и наоборот. А они рождаются и умирают вместе. А в процессе жизни тупо друг на друга держат _жесткие_ ссылки! Пусть даже и через интерфейс. Ну, может, я еще столкнусь, где эта смена будет необходима, но вот эта твоя фраза оказалась ключом, и теперь я все понял как надо делать. В таком разе, действительно, МодельВид тупо держит ссылку на Вид и может вызывать всякие его методы через интерфейс, да и хоть бы напрямую А я, блин, все со своими подписками-отписками голову себе ломаю

PS. И все, перехожу на Prism, потому что контейнер с сервисами — это круто. Сам до такого не додумался почему-то
Re: [WPF] События от ViewModel к View
От: Fortnum  
Дата: 25.10.10 11:55
Оценка: 20 (2)
Вот что нашел по теме. Именно это я и искал. Именно этого мне и не хватало. Хотелось ознакомиться, что уже мир придумал в части генерального решения этой проблемы, и убедиться, что моя идея не противоречит принятым канонам и не выльется потом в нечто ужасное.

<MediaElement x:Name="mediaPlayer" IsMuted="False" AutoPlay="True">
  <fxui:Interaction.Triggers>
    <fxui:ModelEventTrigger EventName="AudioStreamLoaded">
      <local:PlayWaveAudio />
    </fxui:ModelEventTrigger>
  </fxui:Interaction.Triggers>
</MediaElement>




Triggers

Sometimes a view model needs to raise notifications, that a view should handle and do something in response, esp. when these can't be modeled as properties and property change notifications.

For example, in the application here, the view model raises an event to indicate the audio stream for the translated text has been loaded. This could not be modeled as a property, since the MediaElement doesn't have a property of type MediaStreamSource. It instead has a method called SetSource. So something in the view needs to call that method.

Enter triggers and actions. The combination basically is a construct that lets you say, "when this happens, do that". In the code above I've attached a ModelEventTrigger to the MediaElement. The trigger listens to the Model, and subscribes to the specified event, AudioStreamLoaded. When that event is raised by the view model, the trigger invokes its action(s). In this case the PlayWaveAudio action is invoked, which receives the audio stream from the event, creates a MediaStreamSource after parsing the wav data returned from the translation service, and calls on the associated MediaElement's SetSource method to play the audible version of the translated text.

Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.