No padrão MVVM para WPF, manipular diálogos é uma das operações mais complexas. Como seu modelo de visão não sabe nada sobre a visão, a comunicação por diálogo pode ser interessante. Eu posso expor ICommand
que, quando a visualização é chamada, uma caixa de diálogo pode aparecer.
Alguém sabe uma boa maneira de lidar com os resultados dos diálogos? Estou falando de diálogos do Windows, como MessageBox
.
Uma das maneiras pelas quais fizemos isso foi ter um evento no modelo de visualização no qual a visualização seria assinada quando um diálogo fosse necessário.
public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;
Isso é bom, mas significa que a visualização requer código, algo que eu gostaria de evitar.
Respostas:
Sugiro renunciar às caixas de diálogo modais dos anos 90 e, em vez disso, implementar um controle como uma sobreposição (tela + posicionamento absoluto) com visibilidade vinculada a um booleano na VM. Mais próximo de um controle do tipo ajax.
Isso é muito útil:
como em:
Aqui está como eu tenho um implementado como controle de usuário. Clicar no 'x' fecha o controle em uma linha de código no código do controle do usuário por trás. (Como tenho meus modos de exibição em .exe e ViewModels em uma dll, não me sinto mal com o código que manipula a interface do usuário.)
fonte
Você deve usar um mediador para isso. O mediador é um padrão de design comum, também conhecido como Messenger, em algumas de suas implementações. É um paradigma do tipo Register / Notify e permite que o seu ViewModel e Views se comuniquem através de um mecanismo de mensagens com pouco acoplamento.
Você deve conferir o grupo de discípulos do WPF do Google e procurar por mediador. Você ficará muito feliz com as respostas ...
No entanto, você pode começar com isso:
http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/
Aproveitar !
Editar: você pode ver a resposta para esse problema com o MVVM Light Toolkit aqui:
http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338
fonte
Um bom diálogo MVVM deve:
Infelizmente, o WPF não fornece esses recursos. Mostrar uma caixa de diálogo requer uma chamada de code-behind para
ShowDialog()
. A classe Window, que oferece suporte a diálogos, não pode ser declarada em XAML, portanto, não pode ser facilmente vinculada a dadosDataContext
.Para resolver isso, escrevi um controle de stub XAML que fica na árvore lógica e retransmite a ligação de dados para
Window
e manipula a exibição e ocultação da caixa de diálogo. Você pode encontrá-lo aqui: http://www.codeproject.com/KB/WPF/XAMLDialog.aspxÉ realmente simples de usar e não requer alterações estranhas no seu ViewModel e não requer eventos ou mensagens. A chamada básica é assim:
Você provavelmente deseja adicionar um estilo definido
Showing
. Eu explico no meu artigo. Espero que isso ajude você.fonte
"Showing a dialog requires a code-behind"
mmm você pode chamar que, em ViewModelEu uso essa abordagem para diálogos com o MVVM.
Tudo o que tenho a fazer agora é chamar o seguinte no meu modelo de exibição.
fonte
Minha solução atual resolve a maioria dos problemas mencionados, mas é completamente abstraída de itens específicos da plataforma e pode ser reutilizada. Também usei nenhuma vinculação code-behind apenas com DelegateCommands que implementam ICommand. O diálogo é basicamente um modo de exibição - um controle separado que possui seu próprio ViewModel e é mostrado no ViewModel da tela principal, mas acionado a partir da interface do usuário através da ligação DelagateCommand.
Veja a solução completa do Silverlight 4 aqui Diálogos modais com MVVM e Silverlight 4
fonte
Eu realmente lutei com esse conceito por um tempo ao aprender (ainda aprendendo) MVVM. O que eu decidi e o que acho que outros já decidiram, mas que não estava claro para mim, é o seguinte:
Meu pensamento original era que um ViewModel não deveria chamar uma caixa de diálogo diretamente, pois não é necessário decidir como uma caixa de diálogo deve aparecer. Por causa disso, comecei a pensar em como eu poderia passar mensagens como faria no MVP (por exemplo, View.ShowSaveFileDialog ()). No entanto, acho que essa é a abordagem errada.
Não há problema em um ViewModel chamar uma caixa de diálogo diretamente. No entanto, quando você está testando um ViewModel, isso significa que a caixa de diálogo será exibida durante o teste ou falhará totalmente (nunca realmente tentei isso).
Portanto, o que precisa acontecer é que durante o teste, use uma versão de "teste" da sua caixa de diálogo. Isso significa que, para sempre em qualquer caixa de diálogo, você precisa criar uma Interface e simular a resposta da caixa de diálogo ou criar uma simulação de teste que terá um comportamento padrão.
Você já deve estar usando algum tipo de Service Locator ou IoC que você pode configurar para fornecer a versão correta, dependendo do contexto.
Usando essa abordagem, seu ViewModel ainda pode ser testado e, dependendo de como você zomba de suas caixas de diálogo, pode controlar o comportamento.
Espero que isto ajude.
fonte
Há duas boas maneiras de fazer isso: 1) um serviço de diálogo (fácil, limpo) e 2) visualização assistida. A exibição assistida fornece alguns recursos interessantes, mas geralmente não vale a pena.
SERVIÇO DE DIÁLOGO
a) uma interface de serviço de diálogo como via construtor ou algum contêiner de dependência:
interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }
b) Sua implementação do IDialogService deve abrir uma janela (ou injetar algum controle na janela ativa), criar uma exibição correspondente ao nome do tipo dlgVm fornecido (use convenção ou registro de contêiner ou um ContentPresenter com o DataTemplates associados ao tipo). ShowDialogAsync deve criar um TaskCompletionSource e retornar sua propriedade .Task. A própria classe DialogViewModel precisa de um evento que você possa chamar na classe derivada quando desejar fechar e observe na exibição da caixa de diálogo para realmente fechar / ocultar a caixa de diálogo e concluir o TaskCompletionSource.
b) Para usar, basta ligar para aguardar this.DialogService.ShowDialog (myDlgVm) em sua instância de alguma classe derivada de DialogViewModel. Depois de aguardar retorno, consulte as propriedades que você adicionou na sua VM de diálogo para determinar o que aconteceu; você nem precisa de um retorno de chamada.
VIEW ASSISTED
Isso faz com que sua visualização ouça um evento no modelo de visualização. Tudo isso pode ser envolvido em um Blend Behavior para evitar o código por trás e o uso de recursos, se você quiser (FMI, subclasse a classe "Behavior" para ver uma espécie de propriedade anexada ao Blendable em esteróides). Por enquanto, faremos isso manualmente em cada visualização:
a) Crie um OpenXXXXXDialogEvent com uma carga útil personalizada (uma classe derivada de DialogViewModel).
b) Faça com que a visualização assine o evento em seu evento OnDataContextChanged. Certifique-se de ocultar e cancelar a assinatura se o valor antigo! = Null e no evento Descarregado da Janela.
c) Quando o evento for disparado, abra a visualização, que pode estar em um recurso da sua página, ou você poderá localizá-lo por convenção em outro local (como na abordagem do serviço de diálogo).
Essa abordagem é mais flexível, mas requer mais trabalho para usar. Eu não uso muito. A única vantagem interessante é a capacidade de colocar a visualização fisicamente dentro de uma guia, por exemplo. Eu usei um algoritmo para colocá-lo nos limites do controle de usuário atual ou, se não for grande o suficiente, percorrer a árvore visual até encontrar um contêiner grande o suficiente.
Isso permite que as caixas de diálogo fiquem próximas ao local em que são realmente usadas, apenas diminua a parte do aplicativo relacionada à atividade atual e permita que o usuário se mova dentro do aplicativo sem ter que empurrar manualmente as caixas de diálogo, mesmo tendo várias diálogos modais são abertos em diferentes guias ou sub-visualizações.
fonte
Use um comando congelável
fonte
Penso que o manuseio de um diálogo deve ser de responsabilidade da visão, e a visão precisa ter código para dar suporte a isso.
Se você alterar a interação ViewModel - View para manipular caixas de diálogo, o ViewModel dependerá dessa implementação. A maneira mais simples de lidar com esse problema é tornar o View responsável pela execução da tarefa. Se isso significa mostrar uma caixa de diálogo, tudo bem, mas também pode ser uma mensagem de status na barra de status etc.
O que quero dizer é que todo o objetivo do padrão MVVM é separar a lógica de negócios da GUI, portanto, você não deve misturar a lógica da GUI (para exibir um diálogo) na camada de negócios (o ViewModel).
fonte
Uma alternativa interessante é usar controladores responsáveis por mostrar as visualizações (caixas de diálogo).
Como isso funciona é mostrado pelo WPF Application Framework (WAF) .
fonte
Por que não apenas criar um evento na VM e assinar o evento na exibição? Isso manteria a lógica do aplicativo e a exibição separadas e ainda permitiria o uso de uma janela filho para diálogos.
fonte
Eu implementei um comportamento que escuta uma mensagem do ViewModel. É baseado na solução Laurent Bugnion, mas como não usa código por trás e é mais reutilizável, acho que é mais elegante.
Como fazer o WPF se comportar como se o MVVM fosse suportado imediatamente
fonte
Eu acho que a visão poderia ter código para manipular o evento do modelo de visão.
Dependendo do evento / cenário, ele também pode ter um gatilho de evento que se assina para visualizar eventos de modelo e uma ou mais ações para chamar em resposta.
fonte
Eu tive a mesma situação e envolvi a MessageBox em um controle invisível do designer. Os detalhes estão no meu blog
http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx
O mesmo pode ser estendido a qualquer caixa de diálogo modal, controle de busca de arquivos etc.
fonte
Eu rolei meu próprio carregador de janelas descrito em uma resposta a esta pergunta:
Gerenciando várias visualizações WPF em um aplicativo
fonte
Karl Shifflett criou um aplicativo de exemplo para mostrar caixas de diálogo usando a abordagem de serviço e a abordagem Prism InteractionRequest.
Eu gosto da abordagem de serviço - é menos flexível, portanto é menos provável que os usuários quebrem algo :) Também é consistente com a parte WinForms do meu aplicativo (MessageBox.Show). Mas se você planeja mostrar muitas caixas de diálogo diferentes, o InteractionRequest é um melhor caminho a percorrer.
http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/
fonte
Sei que é uma pergunta antiga, mas quando fiz essa pesquisa, encontrei muitas perguntas relacionadas, mas não encontrei uma resposta muito clara. Então, eu faço minha própria implementação de uma caixa de diálogo / messagebox / popin e a compartilho!
Eu acho que é "prova MVVM" e tento torná-lo simples e adequado, mas sou novo no WPF, portanto, fique à vontade para comentar ou até fazer solicitação de recebimento.
https://github.com/Plasma-Paris/Plasma.WpfUtils
Você pode usá-lo assim:
Ou assim, se você quiser popin mais sofisticado:
E está mostrando coisas assim:
fonte
A abordagem padrão
Depois de passar anos lidando com esse problema no WPF, finalmente descobri a maneira padrão de implementar diálogos no WPF. Aqui estão as vantagens dessa abordagem:
Então, qual é a chave? É DI + IoC .
Aqui está como isso funciona. Estou usando o MVVM Light, mas essa abordagem também pode ser estendida a outras estruturas:
Adicione uma interface IDialogService ao projeto da VM:
Exponha uma propriedade estática pública do
IDialogService
tipo no seuViewModelLocator
, mas deixe a parte de registro para a camada Visualizar executar. Esta é a chave .Adicione uma implementação dessa interface no projeto App.
ShowMessage
,AskBooleanQuestion
etc.), outras são específicas para este projeto e usamWindow
s personalizados . Você pode adicionar mais janelas personalizadas da mesma maneira. A chave é manter elementos específicos da interface do usuário na camada Exibir e apenas expor os dados retornados usando POCOs na camada VM .Execute o registro IoC da sua interface na camada View usando esta classe. Você pode fazer isso no construtor da visualização principal (após a
InitializeComponent()
chamada):Ai está. Agora você tem acesso a todas as suas funcionalidades de diálogo nas camadas VM e View. Sua camada de VM pode chamar essas funções assim:
Outras vantagens gratuitas
IDialogService
no seu projeto de Teste e registrar essa classe no IoC no construtor de sua classe de teste.Microsoft.Win32
acessar as caixas de diálogo Abrir e Salvar. Eu os deixei de fora porque também há uma versão WinForms dessas caixas de diálogo disponível, além de alguém querer criar sua própria versão. Observe também que alguns dos identificadores usadosDialogPresenter
são nomes de minhas próprias janelas (por exemploSettingsWindow
). Você precisará removê-los da interface e da implementação ou fornecer suas próprias janelas.DispatcherHelper.Initialize()
início do ciclo de vida do seu aplicativo.Exceto pelo
DialogPresenter
que é injetado na camada View, outros ViewModals devem ser registradosViewModelLocator
e, em seguida, uma propriedade estática pública desse tipo deve ser exposta para a camada View consumir. Algo assim:Na maioria das vezes, suas caixas de diálogo não devem ter nenhum código por trás de coisas como ligação ou configuração do DataContext etc. Você nem deve passar as coisas como parâmetros do construtor. O XAML pode fazer tudo isso por você, assim:
DataContext
dessa maneira oferece todos os tipos de benefícios em tempo de design, como Intellisense e preenchimento automático.Espero que ajude a todos.
fonte
Eu estava pensando em um problema semelhante ao perguntar como deveria ser o modelo de exibição de uma tarefa ou diálogo .
Minha solução atual é assim:
Quando o modelo de visualização decide que a entrada do usuário é necessária, ele abre uma instância
SelectionTaskModel
com as opções possíveis para o usuário. A infraestrutura cuida da exibição da visualização correspondente, que em tempo oportuno chamará aChoose()
função com a escolha do usuário.fonte
Eu lutei com o mesmo problema. Eu criei uma maneira de intercomunicar entre o View e o ViewModel. Você pode iniciar o envio de uma mensagem do ViewModel para o View para solicitar que ele mostre uma caixa de mensagens e ele informará o resultado. Em seguida, o ViewModel pode responder ao resultado retornado da exibição.
Eu demonstro isso no meu blog :
fonte
Escrevi um artigo bastante abrangente sobre esse mesmo tópico e também desenvolvi uma biblioteca pop-in para MVVM Dialogs. A aderência estrita ao MVVM não é apenas possível, mas muito limpa quando implementada corretamente, e pode ser facilmente estendida a bibliotecas de terceiros que não aderem a elas mesmas:
https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM
fonte
Desculpe, mas eu tenho que concordar. Passei por várias das soluções sugeridas antes de encontrar o espaço para nome Prism.Wpf.Interactivity no projeto Prism. Você pode usar solicitações de interação e ação da janela pop-up para rolar uma janela personalizada ou, para necessidades mais simples, são criadas pop-ups de Notificação e Confirmação. Isso cria janelas verdadeiras e é gerenciado como tal. você pode passar um objeto de contexto com quaisquer dependências necessárias na caixa de diálogo. Usamos essa solução no meu trabalho desde que a encontrei. Temos vários desenvolvedores seniores aqui e ninguém criou nada melhor. Nossa solução anterior foi o serviço de diálogo em uma sobreposição e o uso de uma classe de apresentador para que isso acontecesse, mas era necessário ter fábricas para todos os modelos de exibição de diálogo etc.
Isso não é trivial, mas também não é super complicado. E é incorporado ao Prism e, portanto, é a melhor (ou melhor) prática do IMHO.
Meus 2 centavos!
fonte
EDIT: sim, eu concordo que esta não é uma abordagem correta do MVVM e agora estou usando algo semelhante ao sugerido por blindmeis.
Uma das maneiras que você pode fazer isso é
No seu Main View Model (onde você abre o modal):
E no seu Modal Window View / ViewModel:
XAML:
ViewModel:
ou semelhante ao postado aqui WPF MVVM: Como fechar uma janela
fonte