Estou tentando aprender o WPF e o problema do MVVM, mas tive um problema. Esta questão é semelhante, mas não é a mesma que esta (manipulação de diálogos no wpf-with-mvvm) ...
Eu tenho um formulário "Login" escrito usando o padrão MVVM.
Este formulário possui um ViewModel que contém o nome de usuário e a senha, que são vinculados à exibição no XAML usando ligações de dados normais. Ele também possui um comando "Login", que é vinculado ao botão "Login" no formulário, usando uma ligação de dados normal.
Quando o comando "Login" é acionado, ele invoca uma função no ViewModel que dispara e envia dados pela rede para efetuar login. Quando essa função é concluída, existem 2 ações:
O login foi inválido - apenas mostramos uma MessageBox e está tudo bem
O login foi válido, precisamos fechar o formulário de Login e retornar como verdadeiro
DialogResult
...
O problema é que o ViewModel não sabe nada sobre a exibição real, então como ele pode fechar a exibição e dizer-lhe para retornar um determinado DialogResult? Eu poderia colocar algum código no CodeBehind e / ou passar o View até o ViewModel, mas parece que isso iria anular completamente todo o ponto do MVVM ...
Atualizar
No final, acabei de violar a "pureza" do padrão MVVM e o View publicou um Closed
evento e expôs um Close
método. O ViewModel então chamaria view.Close
. A visualização é conhecida apenas por meio de uma interface e conectada por meio de um contêiner IOC, de modo que nenhuma testabilidade ou manutenção são perdidas.
Parece bastante tolo que a resposta aceita seja de -5 votos! Embora eu esteja ciente dos bons sentimentos que alguém obtém ao resolver um problema enquanto é "puro", certamente não sou o único que pensa que 200 linhas de eventos, comandos e comportamentos apenas para evitar um método de uma linha. o nome de "padrões" e "pureza" é um pouco ridículo ...
Close
método simples ainda é a melhor solução. Tudo o resto em outros diálogos mais complexos é MVVM e ligação de dados, mas ele só parecia bobo para implementar as enormes "soluções" aqui em vez de apenas um método simples ...Respostas:
Fui inspirado pela resposta de Thejuan para escrever uma propriedade anexa mais simples. Sem estilos, sem gatilhos; em vez disso, você pode fazer isso:
Isso é quase tão limpo como se a equipe do WPF tivesse acertado e tornado o DialogResult uma propriedade de dependência em primeiro lugar. Basta colocar um
bool? DialogResult
propriedade no seu ViewModel e implementar INotifyPropertyChanged, e pronto, seu ViewModel pode fechar a janela (e definir seu DialogResult) apenas definindo uma propriedade. MVVM como deveria ser.Aqui está o código para DialogCloser:
Eu também postei isso no meu blog .
fonte
Na minha perspectiva, a pergunta é muito boa, pois a mesma abordagem seria usada não apenas para a janela "Login", mas para qualquer tipo de janela. Analisei muitas sugestões e nenhuma está correta para mim. Revise minha sugestão que foi retirada do artigo do padrão de design do MVVM .
Cada classe ViewModel deve herdar da
WorkspaceViewModel
que possui oRequestClose
evento e aCloseCommand
propriedade doICommand
tipo. A implementação padrão daCloseCommand
propriedade aumentará oRequestClose
evento.Para fechar a janela, o
OnLoaded
método da sua janela deve ser substituído:ou
OnStartup
método do seu aplicativo:Eu acho que a implementação de
RequestClose
eventos eCloseCommand
propriedades naWorkspaceViewModel
são bastante claras, mas mostrarei que elas são consistentes:E o código fonte do
RelayCommand
:PS: Não me trate mal por essas fontes! Se eu os tivesse ontem, isso me salvaria algumas horas ...
PPS Quaisquer comentários ou sugestões são bem-vindos.
fonte
customer.RequestClose
no código por trás do seu arquivo XAML não viola o padrão MVVM? Você também poderia vincular oClick
manipulador de eventos no botão "Fechar", pois, ao mesmo tempo, tocou o código e fez umthis.Close()
! Certo?Eu usei comportamentos anexados para fechar a janela. Vincule uma propriedade "signal" no seu ViewModel ao comportamento anexado (na verdade, uso um gatilho) Quando definido como true, o comportamento fecha a janela.
http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/
fonte
Há muitos comentários discutindo os prós e contras da MVVM aqui. Para mim, eu concordo com Nir; é uma questão de usar o padrão adequadamente e o MVVM nem sempre se encaixa. As pessoas parecem estar dispostas a sacrificar todos os princípios mais importantes do design de software, APENAS para ajustá-lo ao MVVM.
Dito isto, acho que o seu caso poderia se encaixar bem com um pouco de refatoração.
Na maioria dos casos, o WPF permite que você aguarde SEM múltiplos
Window
s. Talvez você possa tentar usarFrame
s ePage
s em vez do Windows comDialogResult
s.No seu caso, minha sugestão seria
LoginFormViewModel
manipular oe,LoginCommand
se o login for inválido, defina uma propriedadeLoginFormViewModel
com um valor apropriado (false
ou algum valor de enumeração comoUserAuthenticationStates.FailedAuthentication
). Você faria o mesmo para um login bem-sucedido (true
ou algum outro valor de enumeração). Você usaria umDataTrigger
que responde aos vários estados de autenticação do usuário e poderia usar um simplesSetter
para alterar aSource
propriedade doFrame
.Tendo sua janela de login retornando,
DialogResult
acho que é onde você está ficando confuso; issoDialogResult
é realmente uma propriedade do seu ViewModel. Na minha experiência reconhecidamente limitada com o WPF, quando algo não parece certo, normalmente porque estou pensando em termos de como eu teria feito a mesma coisa no WinForms.Espero que ajude.
fonte
Supondo que sua caixa de diálogo de login seja a primeira janela criada, tente isso dentro da classe LoginViewModel:
fonte
Esta é uma solução simples e limpa - Você adiciona um evento ao ViewModel e instrui a Janela a fechar-se quando esse evento é acionado.
Para mais detalhes, consulte minha postagem no blog, Fechar janela no ViewModel .
XAML:
ViewModel:
Nota: O exemplo usa o Prism
DelegateCommand
(consulte Prism: Commanding ), mas qualquerICommand
implementação pode ser usada para esse assunto.Você pode usar comportamentos deste pacote oficial.
fonte
A maneira como eu lidaria com isso é adicionar um manipulador de eventos no meu ViewModel. Quando o usuário efetuava login com êxito, eu acionava o evento. Na minha opinião, eu me apegaria a esse evento e, quando disparasse, fecharia a janela.
fonte
Aqui está o que eu fiz inicialmente, o que funciona, no entanto, parece bastante demorado e feio (estática global, nada é bom)
1: App.xaml.cs
2: LoginForm.xaml
3: LoginForm.xaml.cs
4: LoginFormViewModel.cs
Mais tarde, em seguida, removi todo esse código e apenas
LoginFormViewModel
chamei o método Close na exibição. Acabou sendo muito melhor e mais fácil de seguir. IMHO, o objetivo dos padrões é oferecer às pessoas uma maneira mais fácil de entender o que seu aplicativo está fazendo e, neste caso, o MVVM estava tornando muito mais difícil de entender do que se eu não o tivesse usado, e agora era um anti- padrão.fonte
Para sua informação, encontrei o mesmo problema e acho que descobri uma solução alternativa que não requer dados globais nem estáticos, embora possa não ser a melhor resposta. Eu deixo vocês decidirem por si mesmos.
No meu caso, o ViewModel que instancia a janela a ser exibida (vamos chamá-la de ViewModelMain) também conhece o LoginFormViewModel (usando a situação acima como exemplo).
Então, o que fiz foi criar uma propriedade no LoginFormViewModel que era do tipo ICommand (vamos chamá-lo de CloseWindowCommand). Em seguida, antes de chamar .ShowDialog () na janela, defino a propriedade CloseWindowCommand no LoginFormViewModel para o método window.Close () da janela que instanciei. Então, dentro do LoginFormViewModel, tudo o que preciso fazer é chamar CloseWindowCommand.Execute () para fechar a janela.
É um pouco de uma solução alternativa / hack, suponho, mas funciona bem sem realmente quebrar o padrão MVVM.
Sinta-se livre para criticar esse processo o quanto quiser, eu posso aceitar! :)
fonte
Provavelmente é muito tarde, mas me deparei com o mesmo problema e encontrei uma solução que funciona para mim.
Não consigo descobrir como criar um aplicativo sem caixas de diálogo (talvez seja apenas um bloqueio mental). Então, eu estava em um impasse com o MVVM e mostrando um diálogo. Então me deparei com este artigo do CodeProject:
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
Que é um UserControl que basicamente permite que uma janela esteja dentro da árvore visual de outra janela (não permitida no xaml). Também expõe um DependencyProperty booleano chamado IsShowing.
Você pode definir um estilo como, normalmente em um dicionário com recursos, que basicamente exibe a caixa de diálogo sempre que a propriedade Content do controle! = Null via triggers:
Na visão em que você deseja exibir a caixa de diálogo, basta ter este:
E no seu ViewModel, tudo o que você precisa fazer é definir a propriedade como um valor (Nota: a classe ViewModel deve suportar INotifyPropertyChanged para que o modo de exibição saiba que algo aconteceu).
igual a:
Para combinar o ViewModel com o View, você deve ter algo parecido com isto em um dicionário de recursos:
Com tudo isso, você obtém um código de uma linha para mostrar o diálogo. O problema é que você não pode realmente fechar a caixa de diálogo apenas com o código acima. É por isso que você deve inserir um evento em uma classe base do ViewModel que o DisplayViewModel herda e, em vez do código acima, escreva
Em seguida, você pode lidar com o resultado da caixa de diálogo através do retorno de chamada.
Isso pode parecer um pouco complexo, mas uma vez estabelecidas as bases, é bem direto. Novamente, esta é a minha implementação, tenho certeza de que existem outras :)
Espero que isso ajude, isso me salvou.
fonte
Ok, então esta pergunta tem quase 6 anos e ainda não consigo encontrar aqui o que eu acho que é a resposta adequada, então permita-me compartilhar meus "2 centavos" ...
Na verdade, eu tenho duas maneiras de fazer isso, a primeira é a mais simples ... a segunda à direita, então, se você está procurando a correta, pule a primeira e pule para a segunda :
1. Rápido e fácil (mas não completo)
Se eu tenho apenas um projeto pequeno, às vezes apenas crio uma CloseWindowAction no ViewModel:
E quem criar o View, ou no código do View atrás, apenas defino o método que a ação chamará:
(lembre-se de que o MVVM trata da separação da View e do ViewModel ... o código da View ainda é a View e enquanto houver uma separação adequada, você não estará violando o padrão)
Se algum ViewModel criar uma nova janela:
Ou, se você quiser na sua janela principal, basta colocá-lo sob o construtor do seu View:
quando quiser fechar a janela, basta chamar a Ação no seu ViewModel.
2. O caminho certo
Agora, a maneira correta de fazer isso é usar o Prism (IMHO), e tudo sobre isso pode ser encontrado aqui .
Você pode fazer uma Solicitação de Interação , preenchê-la com quaisquer dados necessários em sua nova Janela, almoçar, fechá-la e até receber dados novamente . Tudo isso encapsulado e aprovado pela MVVM. Você ainda obtém um status de como a Janela foi fechada , como se o Usuário
Canceled
ouAccepted
(botão OK) da Janela e os dados retornassem, se necessário . É um pouco mais complicado e a resposta nº 1, mas é muito mais completa e um padrão recomendado pela Microsoft.O link que eu dei tem todos os trechos de código e exemplos, então não vou me incomodar em colocar nenhum código aqui, basta ler o artigo de download do Prism Quick Start e executá-lo, é muito simples entender um pouco mais detalhadamente fazê-lo funcionar, mas os benefícios são maiores do que apenas fechar uma janela.
fonte
+=
para adicionar um delegado e chamar a ação, ele acionará todos eles .... Ou você precisa criar uma lógica especial em sua VM para que ele saiba qual janela fechar (talvez tenha uma coleção de ações próximas) .... Mas acho que ter várias visualizações vinculadas a uma VM não é uma prática recomendada. é melhor gerenciar ter uma instância de View e uma VM conectadas entre si e talvez uma VM pai que gerencia todas as VMs filhas associadas a todas as Views.fonte
Você pode fazer com que o ViewModel exponha um evento ao qual a View se registra. Então, quando o ViewModel decide sua hora de fechar a exibição, ele dispara o evento que faz com que a exibição seja fechada. Se você deseja que um valor de resultado específico seja devolvido, você terá uma propriedade no ViewModel para isso.
fonte
Apenas para adicionar ao grande número de respostas, quero adicionar o seguinte. Supondo que você tenha um ICommand no seu ViewModel e deseje que esse comando feche sua janela (ou qualquer outra ação), é possível usar algo como o seguinte.
Não é perfeito e pode ser difícil de testar (como é difícil zombar / stub de uma estática), mas é mais limpo (IMHO) do que as outras soluções.
Erick
fonte
Eu implementei a solução de Joe White, mas tive problemas com ocasionais " DialogResult pode ser definido somente depois que o Windows é criado e mostrado como caixa de diálogo erros ".
Eu mantive o ViewModel por perto depois que o View foi fechado e, ocasionalmente, abri um novo View usando a mesma VM. Parece que o fechamento da nova visualização antes da coleta antiga de lixo resultou em DialogResultChanged tentando definir o DialogResult propriedade na janela fechada, provocando o erro.
Minha solução foi alterar DialogResultChanged para verificar a propriedade IsLoaded da janela :
Após fazer essa alteração, todos os anexos às caixas de diálogo fechadas são ignorados.
fonte
Acabei misturando a resposta de Joe White e algum código da resposta de Adam Mills , pois precisava mostrar um controle de usuário em uma janela criada programaticamente. Portanto, o DialogCloser não precisa estar na janela, pode estar no próprio controle do usuário
E o DialogCloser encontrará a janela do controle do usuário se não estiver anexado à própria janela.
fonte
O comportamento é a maneira mais conveniente aqui.
Por um lado, ele pode ser vinculado ao modelo de exibição fornecido (que pode sinalizar "fechar o formulário!")
Por outro lado, ele tem acesso ao próprio formulário para que possa se inscrever nos eventos específicos do formulário necessários ou mostrar a caixa de diálogo de confirmação ou qualquer outra coisa.
Escrever o comportamento necessário pode ser visto entediante desde a primeira vez. No entanto, a partir de agora, você poderá reutilizá-lo em todos os formulários necessários, usando o snippet XAML de uma linha exata. E, se necessário, você pode extraí-lo como uma montagem separada para que possa ser incluído em qualquer próximo projeto que desejar.
fonte
Por que não passar a janela como um parâmetro de comando?
C #:
XAML:
fonte
Window
tipo que não seja MVVM "puro". Veja esta resposta, onde a VM não está restrita a umWindow
objeto.Outra solução é criar propriedade com INotifyPropertyChanged no modelo de exibição como DialogResult e, em seguida, no código atrás, escreva isto:
O fragmento mais importante é
_someViewModel_PropertyChanged
.DialogResultPropertyName
pode ser uma string pública pública emSomeViewModel
.Eu uso esse tipo de truque para fazer algumas alterações no View Controls, caso isso seja difícil de fazer no ViewModel. OnPropertyChanged no ViewModel, você pode fazer o que quiser no ViewModel. O ViewModel ainda é 'testável por unidade' e algumas pequenas linhas de código no código por trás não fazem diferença.
fonte
Eu iria desta maneira:
fonte
Eu li todas as respostas, mas devo dizer que a maioria delas não é boa o suficiente ou pior.
Você poderia lidar com isso com a classe DialogService, cuja responsabilidade é mostrar a janela e retornar o resultado da mesma. Eu tenho criar projeto de exemplo que demonstra a sua implementação e uso.
aqui estão as partes mais importantes:
Isso não é apenas mais simples? mais simples, mais legível e por último mas não menos fácil de depurar do que o EventAggregator ou outras soluções semelhantes?
Como você pode ver, nos meus modelos de exibição, usei a primeira abordagem do ViewModel descrita no meu post aqui: Práticas recomendadas para chamar o View do ViewModel no WPF
Obviamente, no mundo real, é
DialogService.ShowDialog
preciso ter mais opções para configurar a caixa de diálogo, por exemplo, botões e comandos que devem ser executados. Há uma maneira diferente de fazer isso, mas está fora do escopo :)fonte
Embora isso não responda à pergunta de como fazer isso por meio do viewmodel, isso mostra como fazer isso usando apenas XAML + o SDK de combinação.
Eu escolhi fazer o download e usar dois arquivos do SDK do Blend, os quais você pode usar como pacote da Microsoft através do NuGet. Os arquivos são:
System.Windows.Interactivity.dll e Microsoft.Expression.Interactions.dll
O Microsoft.Expression.Interactions.dll oferece ótimos recursos, como a capacidade de definir propriedades ou chamar um método no seu modelo de exibição ou outro destino, além de ter outros widgets internos.
Alguns XAML:
Observe que, se você está apenas adotando um comportamento simples de OK / Cancel, pode se safar usando as propriedades IsDefault e IsCancel, desde que a janela seja mostrada com Window.ShowDialog ().
Pessoalmente, tive problemas com um botão que tinha a propriedade IsDefault definida como true, mas estava oculta quando a página é carregada. Não parecia querer jogar muito bem depois que foi mostrado, então estou definindo a propriedade Window.DialogResult, como mostrado acima, e funciona para mim.
fonte
Aqui está a solução simples e sem erros (com código fonte), está funcionando para mim.
Derive seu ViewModel de
INotifyPropertyChanged
Crie uma propriedade observável CloseDialog no ViewModel
}
Anexar um manipulador no modo de exibição para esta alteração de propriedade
Agora você está quase pronto. No manipulador de eventos, faça
DialogResult = true
fonte
Crie um
Dependency Property
em seuView
/ qualquerUserControl
(ouWindow
você deseja fechar). Como abaixo:E vincule-o a partir da propriedade do seu ViewModel :
Propriedade em
VeiwModel
:Agora, inicie a operação de fechamento alterando o
CloseWindow
valor no ViewModel. :)fonte
Onde você precisa fechar a janela, basta colocar isso no viewmodel:
ta-da
fonte
É o bastante!
fonte