Tworzę login przy użyciu a, window control
aby umożliwić użytkownikowi zalogowanie się do WPF
aplikacji, którą tworzę.
Do tej pory stworzyłem metodę, która sprawdza, czy użytkownik podał poprawne dane uwierzytelniające dla username
iw password
a textbox
na ekranie logowania, binding
dwa properties
.
Osiągnąłem to, tworząc taką bool
metodę;
public bool CheckLogin()
{
var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault();
if (user == null)
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
else if (this.Username == user.Username || this.Password.ToString() == user.Password)
{
MessageBox.Show("Welcome " + user.Username + ", you have successfully logged in.");
return true;
}
else
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
}
public ICommand ShowLoginCommand
{
get
{
if (this.showLoginCommand == null)
{
this.showLoginCommand = new RelayCommand(this.LoginExecute, null);
}
return this.showLoginCommand;
}
}
private void LoginExecute()
{
this.CheckLogin();
}
Mam też, command
że mam bind
do mojego przycisku w xaml
podobny sposób;
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" />
Kiedy wprowadzam nazwę użytkownika i hasło, wykonuje odpowiedni kod, niezależnie od tego, czy jest poprawny, czy zły. Ale jak mogę zamknąć to okno z ViewModel, jeśli zarówno nazwa użytkownika, jak i hasło są poprawne?
Wcześniej próbowałem użyć a, dialog modal
ale nie wyszło. Co więcej, w moim app.xaml wykonałem coś podobnego do poniższego, który najpierw ładuje stronę logowania, a następnie ładuje rzeczywistą aplikację.
private void ApplicationStart(object sender, StartupEventArgs e)
{
Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
var dialog = new UserView();
if (dialog.ShowDialog() == true)
{
var mainWindow = new MainWindow();
Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Current.MainWindow = mainWindow;
mainWindow.Show();
}
else
{
MessageBox.Show("Unable to load application.", "Error", MessageBoxButton.OK);
Current.Shutdown(-1);
}
}
Pytanie: Jak mogę zamknąć dane logowania Window control
z ViewModel?
Z góry dziękuję.
Odpowiedzi:
Możesz przekazać okno do swojego ViewModel przy użyciu
CommandParameter
. Zobacz mój przykład poniżej.Zaimplementowałem
CloseWindow
metodę, która przyjmuje parametr Windows jako parametr i zamyka go. Okno jest przekazywane do ViewModel za pośrednictwemCommandParameter
. Zauważ, że musisz zdefiniowaćx:Name
dla okna, które powinno być zamknięte. W moim oknie XAML wywołuję tę metodę viaCommand
i przekazuję samo okno jako parametr do ViewModel przy użyciuCommandParameter
.Command="{Binding CloseWindowCommand, Mode=OneWay}" CommandParameter="{Binding ElementName=TestWindow}"
ViewModel
public RelayCommand<Window> CloseWindowCommand { get; private set; } public MainViewModel() { this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow); } private void CloseWindow(Window window) { if (window != null) { window.Close(); } }
Widok
<Window x:Class="ClientLibTestTool.ErrorView" x:Name="TestWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:localization="clr-namespace:ClientLibTestTool.ViewLanguages" DataContext="{Binding Main, Source={StaticResource Locator}}" Title="{x:Static localization:localization.HeaderErrorView}" Height="600" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen"> <Grid> <Button Content="{x:Static localization:localization.ButtonClose}" Height="30" Width="100" Margin="0,0,10,10" IsCancel="True" VerticalAlignment="Bottom" HorizontalAlignment="Right" Command="{Binding CloseWindowCommand, Mode=OneWay}" CommandParameter="{Binding ElementName=TestWindow}"/> </Grid> </Window>
Zauważ, że używam lekkiej struktury MVVM, ale zasada ma zastosowanie do każdej aplikacji wpf.
To rozwiązanie narusza wzorzec MVVM, ponieważ model widoku nie powinien wiedzieć nic o implementacji interfejsu użytkownika. Jeśli chcesz ściśle przestrzegać paradygmatu programowania MVVM, musisz wyodrębnić typ widoku za pomocą interfejsu.
Rozwiązanie zgodne z MVVM (dawna EDIT2)
użytkownik Crono wspomina ważny punkt w sekcji komentarzy:
Możesz to naprawić, wprowadzając interfejs zawierający metodę zamknięcia.
Berło:
public interface ICloseable { void Close(); }
Twój refaktoryzowany ViewModel będzie wyglądał następująco:
ViewModel
public RelayCommand<ICloseable> CloseWindowCommand { get; private set; } public MainViewModel() { this.CloseWindowCommand = new RelayCommand<IClosable>(this.CloseWindow); } private void CloseWindow(ICloseable window) { if (window != null) { window.Close(); } }
Musisz odwołać się i zaimplementować
ICloseable
interfejs w swoim widokuWyświetl (kod za)
public partial class MainWindow : Window, ICloseable { public MainWindow() { InitializeComponent(); } }
Odpowiedź na pierwotne pytanie: (dawniej EDIT1)
Twój przycisk logowania (dodano parametr CommandParameter):
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>
Twój kod:
public RelayCommand<Window> CloseWindowCommand { get; private set; } // the <Window> is important for your solution! public MainViewModel() { //initialize the CloseWindowCommand. Again, mind the <Window> //you don't have to do this in your constructor but it is good practice, thought this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow); } public bool CheckLogin(Window loginWindow) //Added loginWindow Parameter { var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault(); if (user == null) { MessageBox.Show("Unable to Login, incorrect credentials."); return false; } else if (this.Username == user.Username || this.Password.ToString() == user.Password) { MessageBox.Show("Welcome "+ user.Username + ", you have successfully logged in."); this.CloseWindow(loginWindow); //Added call to CloseWindow Method return true; } else { MessageBox.Show("Unable to Login, incorrect credentials."); return false; } } //Added CloseWindow Method private void CloseWindow(Window window) { if (window != null) { window.Close(); } }
źródło
private void LoginExecute(){this.CheckLogin();}
<- CheckLogin musi przyjąć parametr.CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Window
obiektu do modelu widoku przerywa IMHO wzorca MVVM, ponieważ wymusza to na Twojej maszynie wirtualnej, aby wiedziała, w czym jest przeglądana. Co by było, gdyby widok był zamiast tego zadokowaną kartą w interfejsie MDI? Właściwym sposobem na zrobienie tego IMHO jest przekazanie pewnego rodzaju interfejsu IUIHost, który implementuje metodę Close i uzyskanie dowolnego widoku, który ma pokazywać maszynę wirtualną zaimplementowaną.Zwykle umieszczam zdarzenie w modelu widoku, gdy muszę to zrobić, a następnie podłączam je do
Window.Close()
podczas wiązania modelu widoku z oknempublic class LoginViewModel { public event EventHandler OnRequestClose; private void Login() { // Login logic here OnRequestClose(this, new EventArgs()); } }
I podczas tworzenia okna logowania
var vm = new LoginViewModel(); var loginWindow = new LoginWindow { DataContext = vm }; vm.OnRequestClose += (s, e) => loginWindow.Close(); loginWindow.ShowDialog();
źródło
Loaded
,ContentRendered
do okna głównego, usługi dialogowych, etc.), dodając do niego trochę za pośrednictwem przypadku ViewModel jest dość czyste, jak dla mnie. 3 linie kodu tak naprawdę nie wymagają rozwiązania umożliwiającego ponowne użycie. PS: czysty MVVM i tak jest dla nerdów.Pozostając MVVM, myślę, że użycie zachowań z Blend SDK (System.Windows.Interactivity) lub niestandardowego żądania interakcji z Prism może się naprawdę dobrze sprawdzić w tego rodzaju sytuacjach.
Jeśli wybierasz trasę Zachowanie, oto ogólna idea:
public class CloseWindowBehavior : Behavior<Window> { public bool CloseTrigger { get { return (bool)GetValue(CloseTriggerProperty); } set { SetValue(CloseTriggerProperty, value); } } public static readonly DependencyProperty CloseTriggerProperty = DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(CloseWindowBehavior), new PropertyMetadata(false, OnCloseTriggerChanged)); private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var behavior = d as CloseWindowBehavior; if (behavior != null) { behavior.OnCloseTriggerChanged(); } } private void OnCloseTriggerChanged() { // when closetrigger is true, close the window if (this.CloseTrigger) { this.AssociatedObject.Close(); } } }
Następnie w swoim oknie możesz po prostu powiązać CloseTrigger z wartością logiczną, która zostanie ustawiona, gdy chcesz, aby okno zostało zamknięte.
<Window x:Class="TestApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:TestApp" Title="MainWindow" Height="350" Width="525"> <i:Interaction.Behaviors> <local:CloseWindowBehavior CloseTrigger="{Binding CloseTrigger}" /> </i:Interaction.Behaviors> <Grid> </Grid> </Window>
Wreszcie, Twój DataContext / ViewModel miałby właściwość, którą ustawiłeś, gdy chcesz, aby okno zamykało się w następujący sposób:
public class MainWindowViewModel : INotifyPropertyChanged { private bool closeTrigger; /// <summary> /// Gets or Sets if the main window should be closed /// </summary> public bool CloseTrigger { get { return this.closeTrigger; } set { this.closeTrigger = value; RaisePropertyChanged(nameof(CloseTrigger)); } } public MainWindowViewModel() { // just setting for example, close the window CloseTrigger = true; } protected void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public event PropertyChangedEventHandler PropertyChanged; }
(ustaw Window.DataContext = new MainWindowViewModel ())
źródło
boolean
wartością. Kiedy to powiedziałeś, czy chciałeś, żebym stworzył,DataTrigger
aby to osiągnąć?może być późno, ale oto moja odpowiedź
foreach (Window item in Application.Current.Windows) { if (item.DataContext == this) item.Close(); }
źródło
Oto coś, czego użyłem w kilku projektach. Może wyglądać na włamanie, ale działa dobrze.
public class AttachedProperties : DependencyObject //adds a bindable DialogResult to window { public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(AttachedProperties), new PropertyMetaData(default(bool?), OnDialogResultChanged)); public bool? DialogResult { get { return (bool?)GetValue(DialogResultProperty); } set { SetValue(DialogResultProperty, value); } } private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var window = d as Window; if (window == null) return; window.DialogResult = (bool?)e.NewValue; } }
Teraz możesz powiązać
DialogResult
się z maszyną wirtualną i ustawić jej wartość właściwości.Window
Zostanie zamknięty, gdy wartość jest ustawiona.<!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult --> <Window someNs:AttachedProperties.DialogResult={Binding DialogResult} />
To jest streszczenie tego, co dzieje się w naszym środowisku produkcyjnym
<Window x:Class="AC.Frontend.Controls.DialogControl.Dialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:DialogControl="clr-namespace:AC.Frontend.Controls.DialogControl" xmlns:hlp="clr-namespace:AC.Frontend.Helper" MinHeight="150" MinWidth="300" ResizeMode="NoResize" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" Title="{Binding Title}" hlp:AttachedProperties.DialogResult="{Binding DialogResult}" WindowStyle="ToolWindow" ShowInTaskbar="True" Language="{Binding UiCulture, Source={StaticResource Strings}}"> <!-- A lot more stuff here --> </Window>
Jak widać,
xmlns:hlp="clr-namespace:AC.Frontend.Helper"
najpierw deklaruję przestrzeń nazw, a następnie powiązaniehlp:AttachedProperties.DialogResult="{Binding DialogResult}"
.Do
AttachedProperty
wygląda następująco. To nie to samo, co opublikowałem wczoraj, ale IMHO to nie powinno mieć żadnego efektu.public class AttachedProperties { #region DialogResult public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached("DialogResult", typeof (bool?), typeof (AttachedProperties), new PropertyMetadata(default(bool?), OnDialogResultChanged)); private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var wnd = d as Window; if (wnd == null) return; wnd.DialogResult = (bool?) e.NewValue; } public static bool? GetDialogResult(DependencyObject dp) { if (dp == null) throw new ArgumentNullException("dp"); return (bool?)dp.GetValue(DialogResultProperty); } public static void SetDialogResult(DependencyObject dp, object value) { if (dp == null) throw new ArgumentNullException("dp"); dp.SetValue(DialogResultProperty, value); } #endregion }
źródło
<Window />
elemencie, jak zilustrowałem w moim wyciętym. Byłem zbyt leniwy, żeby napisać resztę (deklaracje przestrzeni nazw itp.), Co zwykle jest tam również deklarowane.datatrigger
i przypisać go do przycisku, aby działał? Jeszcze raz przepraszam za pytanie nooby.DataTrigger¬ and setting value
true`?DataContext
zDialog
. Spodziewałbym się, że maszyna wirtualna ustawiona jakoDataContext
zapewnia polecenie, które ustawia właściwośćDialogResult
lub cokolwiek, do czego się zobowiązałeś,true
lubfalse
tak, żeDialog
zamyka się.Łatwy sposób
public interface IRequireViewIdentification { Guid ViewID { get; } }
Zaimplementuj do ViewModel
public class MyViewVM : IRequireViewIdentification { private Guid _viewId; public Guid ViewID { get { return _viewId; } } public MyViewVM() { _viewId = Guid.NewGuid(); } }
Dodaj pomocnika ogólnego menedżera okien
public static class WindowManager { public static void CloseWindow(Guid id) { foreach (Window window in Application.Current.Windows) { var w_id = window.DataContext as IRequireViewIdentification; if (w_id != null && w_id.ViewID.Equals(id)) { window.Close(); } } } }
I zamknij to w ten sposób w ViewModel
WindowManager.CloseWindow(ViewID);
źródło
WindowManager
, które z kolei jest ściśle powiązaneView
(pod względemPresentationFramework
). Byłoby lepiej, gdybyWindowManager
usługa została przekazana do viewmodel za pośrednictwem interfejsu. Wtedy możesz (powiedzmy) łatwo przenieść swoje rozwiązanie na inną platformę.Oto prosty przykład użycia komunikatora MVVM Light Messenger zamiast zdarzenia. Model widoku wysyła komunikat zamknięcia po kliknięciu przycisku:
public MainViewModel() { QuitCommand = new RelayCommand(ExecuteQuitCommand); } public RelayCommand QuitCommand { get; private set; } private void ExecuteQuitCommand() { Messenger.Default.Send<CloseMessage>(new CloseMessage()); }
Następnie jest odbierany w kodzie za oknem.
public Main() { InitializeComponent(); Messenger.Default.Register<CloseMessage>(this, HandleCloseMessage); } private void HandleCloseMessage(CloseMessage closeMessage) { Close(); }
źródło
A co powiesz na to ?
ViewModel:
class ViewModel { public Action CloseAction { get; set; } private void Stuff() { // Do Stuff CloseAction(); // closes the window } }
W swoim ViewModel użyj CloseAction (), aby zamknąć okno, tak jak w powyższym przykładzie.
Widok:
public View() { InitializeComponent(); ViewModel vm = new ViewModel (); // this creates an instance of the ViewModel this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View if (vm.CloseAction == null) vm.CloseAction = new Action(() => this.Close()); }
źródło
Wiem, że to stary post, chyba nikt by tak daleko nie przewinął, wiem, że nie. Więc po godzinach próbowania różnych rzeczy znalazłem tego bloga i koleś go zabił. Najprościej to zrobić, wypróbowałem i działa jak urok.
Blog
W ViewModel:
... public bool CanClose { get; set; } private RelayCommand closeCommand; public ICommand CloseCommand { get { if(closeCommand == null) ( closeCommand = new RelayCommand(param => Close(), param => CanClose); ) } } public void Close() { this.Close(); } ...
Dodaj właściwość Action do ViewModel, ale zdefiniuj ją z pliku związanego z kodem widoku. To pozwoli nam dynamicznie zdefiniować odniesienie w ViewModel, które wskazuje na View.
W ViewModel po prostu dodamy:
public Action CloseAction { get; set; }
W Widoku zdefiniujemy to jako takie:
public View() { InitializeComponent() // this draws the View ViewModel vm = new ViewModel(); // this creates an instance of the ViewModel this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View if ( vm.CloseAction == null ) vm.CloseAction = new Action(() => this.Close()); }
źródło
Możesz utworzyć nową procedurę obsługi zdarzeń w ViewModel w ten sposób.
public event EventHandler RequestClose; protected void OnRequestClose() { if (RequestClose != null) RequestClose(this, EventArgs.Empty); }
Następnie zdefiniuj RelayCommand dla ExitCommand.
private RelayCommand _CloseCommand; public ICommand CloseCommand { get { if(this._CloseCommand==null) this._CloseCommand=new RelayCommand(CloseClick); return this._CloseCommand; } } private void CloseClick(object obj) { OnRequestClose(); }
Następnie w zestawie plików XAML
<Button Command="{Binding CloseCommand}" />
Ustaw DataContext w pliku xaml.cs i zasubskrybuj utworzone przez nas zdarzenie.
public partial class MainWindow : Window { private ViewModel mainViewModel = null; public MainWindow() { InitializeComponent(); mainViewModel = new ViewModel(); this.DataContext = mainViewModel; mainViewModel.RequestClose += delegate(object sender, EventArgs args) { this.Close(); }; } }
źródło
Mój proponowany sposób to zdarzenie Declare w ViewModel i użycie blend InvokeMethodAction, jak poniżej.
Przykładowy ViewModel
public class MainWindowViewModel : BindableBase, ICloseable { public DelegateCommand SomeCommand { get; private set; } #region ICloseable Implementation public event EventHandler CloseRequested; public void RaiseCloseNotification() { var handler = CloseRequested; if (handler != null) { handler.Invoke(this, EventArgs.Empty); } } #endregion public MainWindowViewModel() { SomeCommand = new DelegateCommand(() => { //when you decide to close window RaiseCloseNotification(); }); } }
I Interfejs zamykany jest jak poniżej, ale nie wymaga wykonywania tej czynności. ICloseable pomoże w tworzeniu usługi widoku ogólnego, więc jeśli konstruujesz widok i ViewModel za pomocą iniekcji zależności, to możesz zrobić
internal interface ICloseable { event EventHandler CloseRequested; }
Korzystanie z ICloseable
var viewModel = new MainWindowViewModel(); // As service is generic and don't know whether it can request close event var window = new Window() { Content = new MainView() }; var closeable = viewModel as ICloseable; if (closeable != null) { closeable.CloseRequested += (s, e) => window.Close(); }
A poniżej jest Xaml, możesz użyć tego Xaml, nawet jeśli nie zaimplementujesz interfejsu, będzie on potrzebował tylko Twojego modelu widoku do podniesienia CloseRquested.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPFRx" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" xmlns:ViewModels="clr-namespace:WPFRx.ViewModels" x:Name="window" x:Class="WPFRx.MainWindow" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525" d:DataContext="{d:DesignInstance {x:Type ViewModels:MainWindowViewModel}}"> <i:Interaction.Triggers> <i:EventTrigger SourceObject="{Binding Mode=OneWay}" EventName="CloseRequested" > <ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/> </i:EventTrigger> </i:Interaction.Triggers> <Grid> <Button Content="Some Content" Command="{Binding SomeCommand}" Width="100" Height="25"/> </Grid>
źródło
Możesz użyć
Messenger
z zestawu narzędzi MVVMLight. wViewModel
wyślij wiadomość taką jak ta:Messenger.Default.Send(new NotificationMessage("Close"));
następnie w kodzie systemu Windows za, po
InitializeComponent
, zarejestruj się na tę wiadomość w następujący sposób:Messenger.Default.Register<NotificationMessage>(this, m=>{ if(m.Notification == "Close") { this.Close(); } });
więcej informacji na temat zestawu narzędzi MVVMLight można znaleźć tutaj: Zestaw narzędzi MVVMLight na Codeplex
Zauważ, że w MVVM nie ma reguły „bez kodu związanego z kodem” i możesz rejestrować wiadomości w widoku związanym z kodem.
źródło
To proste. Możesz utworzyć własną klasę ViewModel dla logowania - LoginViewModel. Możesz utworzyć widok var dialog = new UserView (); wewnątrz modelu LoginViewModel. I możesz ustawić Command LoginCommand w przycisku.
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" />
i
<Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" />
Klasa ViewModel:
public class LoginViewModel { Window dialog; public bool ShowLogin() { dialog = new UserView(); dialog.DataContext = this; // set up ViewModel into View if (dialog.ShowDialog() == true) { return true; } return false; } ICommand _loginCommand public ICommand LoginCommand { get { if (_loginCommand == null) _loginCommand = new RelayCommand(param => this.Login()); return _loginCommand; } } public void CloseLoginView() { if (dialog != null) dialog.Close(); } public void Login() { if(CheckLogin()==true) { CloseLoginView(); } else { // write error message } } public bool CheckLogin() { // ... check login code return true; } }
źródło
Object reference not set to an instance of an object.
dla metody CloseLoginView. Jakieś sugestie, jak rozwiązać ten problem?Oto sposób, w jaki zrobiłem to całkiem prosto:
YourWindow.xaml.cs
//In your constructor public YourWindow() { InitializeComponent(); DataContext = new YourWindowViewModel(this); }
YourWindowViewModel.cs
private YourWindow window;//so we can kill the window //In your constructor public YourWindowViewModel(YourWindow window) { this.window = window; } //to close the window public void CloseWindow() { window.Close(); }
Nie widzę nic złego w odpowiedzi, którą wybrałeś, pomyślałem, że może to być prostszy sposób!
źródło
Możesz traktować okno jako usługę (np. Usługę UI) i przekazać się do viewmodelu poprzez interfejs , tak jak:
public interface IMainWindowAccess { void Close(bool result); } public class MainWindow : IMainWindowAccess { // (...) public void Close(bool result) { DialogResult = result; Close(); } } public class MainWindowViewModel { private IMainWindowAccess access; public MainWindowViewModel(IMainWindowAccess access) { this.access = access; } public void DoClose() { access.Close(true); } }
To rozwiązanie ma większość zalet przekazywania samego widoku do viewmodel bez wad polegających na przerywaniu MVVM, ponieważ chociaż fizyczny widok jest przekazywany do viewmodel, ten drugi nadal nie wie o pierwszym, widzi tylko niektóre
IMainWindowAccess
. Na przykład, gdybyśmy chcieli przenieść to rozwiązanie na inną platformę, byłaby to tylko kwestiaIMainWindowAccess
prawidłowego wdrożenia , powiedzmy, plikuActivity
.Publikuję tutaj rozwiązanie, aby zaproponować inne podejście niż zdarzenia (chociaż jest w rzeczywistości bardzo podobne), ponieważ wydaje się nieco prostsze niż zdarzenia do zaimplementowania (dołączanie / odłączanie itp.), Ale nadal ładnie dopasowuje się do wzorca MVVM.
źródło
Możesz zamknąć bieżące okno, używając następującego kodu:
Application.Current.Windows[0].Close();
źródło
System.Environment.Exit (0); w widoku model zadziała.
źródło