Ich möchte ein Login mit einem window control
erstellen, um einem Benutzer das Anmelden bei einer WPF
-Anwendung zu ermöglichen, die ich erstelle.
Bisher habe ich eine Methode erstellt, die prüft, ob der Benutzer die korrekten Anmeldeinformationen für username
und password
in einer textbox
im Anmeldebildschirm eingegeben hat, binding
zwei properties
.
Dies ist mir durch die Erstellung einer bool
-Methode gelungen.
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();
}
Ich habe auch eine command
, die ich bind
zu meinem Button innerhalb der xaml
mag, so wie ich;
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" />
Wenn ich den Benutzernamen und das Passwort eingebe, wird der entsprechende Code ausgeführt, und zwar entweder richtig oder falsch. Aber wie kann ich dieses Fenster aus dem ViewModel schließen, wenn sowohl Benutzername als auch Kennwort korrekt sind?
Ich habe es früher mit einem dialog modal
versucht, aber es hat nicht ganz geklappt. Außerdem habe ich in meiner app.xaml so etwas wie das Folgende getan, das die Anmeldeseite zuerst lädt, dann einmal die wahre Anwendung, dann die eigentliche Anwendung.
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);
}
}
Frage: Wie kann ich den Login Window control
vom ViewModel schließen?
Danke im Voraus.
Sie können das Fenster mit der CommandParameter
an Ihr ViewModel übergeben. Siehe mein Beispiel unten.
Ich habe eine CloseWindow
-Methode implementiert, die einen Windows-Parameter als Parameter verwendet und ihn schließt. Das Fenster wird über CommandParameter
an das ViewModel übergeben. Beachten Sie, dass Sie einen x:Name
für das Fenster definieren müssen, das geschlossen werden soll. In meinem XAML-Fenster rufe ich diese Methode über Command
auf und übergebe das Fenster selbst als Parameter mit CommandParameter
an das ViewModel.
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();
}
}
Aussicht
<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>
Beachten Sie, dass ich das MVVM Light-Framework verwende, das Prinzip jedoch für jede Wpf-Anwendung gilt.
Diese Lösung verstößt gegen das MVVM-Muster, da das Ansichtsmodell nichts über die Implementierung der Benutzeroberfläche wissen sollte. Wenn Sie das Programmierparadigma von MVVM strikt befolgen möchten, müssen Sie den Typ der Ansicht mit einer Schnittstelle abstrahieren.
MVVM-konforme Lösung (Ehemalige EDIT2)
der Benutzer Crono erwähnt einen gültigen Punkt im Kommentarbereich:
Durch das Übergeben des Window-Objekts an das Ansichtsmodell wird das MVVM-Muster zerstört IMHO, weil es Ihre vm zwingt zu wissen, in was es gesehen wird.
Sie können dies beheben, indem Sie eine Schnittstelle einführen, die eine close-Methode enthält.
Schnittstelle:
public interface IClosable
{
void Close();
}
Ihr refactored ViewModel sieht so aus:
ViewModel
public RelayCommand<IClosable> CloseWindowCommand { get; private set; }
public MainViewModel()
{
this.CloseWindowCommand = new RelayCommand<IClosable>(this.CloseWindow);
}
private void CloseWindow(IClosable window)
{
if (window != null)
{
window.Close();
}
}
Sie müssen die IClosable
-Schnittstelle in Ihrer Ansicht referenzieren und implementieren
View (Code hinter)
public partial class MainWindow : Window, IClosable
{
public MainWindow()
{
InitializeComponent();
}
}
Antwort auf die ursprüngliche Frage: (früher EDIT1)
Ihre Login-Schaltfläche (Added CommandParameter):
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>
Dein Code:
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();
}
}
Wenn ich MVVM halte, denke ich, dass die Verwendung von Verhaltensweisen aus dem Blend-SDK (System.Windows.Interactivity) oder einer benutzerdefinierten Interaktionsanfrage von Prism für diese Art von Situation wirklich gut funktionieren könnte.
Wenn Sie die Verhaltensroute gehen, ist hier die allgemeine Idee:
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();
}
}
}
Dann würden Sie in Ihrem Fenster den CloseTrigger einfach an einen booleschen Wert binden, der beim Schließen des Fensters festgelegt würde.
<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>
Schließlich verfügt Ihr DataContext/ViewModel über eine Eigenschaft, die Sie beim Schließen des Fensters wie folgt festgelegt hätten:
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("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;
}
(Setze dein Window.DataContext = new MainWindowViewModel ())
Normalerweise füge ich ein Ereignis in das Ansichtsmodell ein, wenn ich dies tun muss, und binde es dann an die Window.Close()
, wenn das Ansichtsmodell an das Fenster gebunden wird
public class LoginViewModel
{
public event EventHandler OnRequestClose;
private void Login()
{
// Login logic here
OnRequestClose(this, new EventArgs());
}
}
Und beim Erstellen des Login-Fensters
var vm = new LoginViewModel();
var loginWindow = new LoginWindow
{
DataContext = vm
};
vm.OnRequestClose += (s, e) => loginWindow.Close();
loginWindow.ShowDialog();
Nun, hier habe ich etwas in mehreren Projekten verwendet. Es kann wie ein Hack aussehen, aber es funktioniert gut.
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;
}
}
Jetzt können Sie DialogResult
an eine VM binden und den Wert einer Eigenschaft festlegen. Window
wird geschlossen, wenn der Wert festgelegt ist.
<!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult -->
<Window someNs:AttachedProperties.DialogResult={Binding DialogResult} />
Dies ist eine Zusammenfassung dessen, was in unserer Produktionsumgebung läuft
<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>
Wie Sie sehen, deklariere ich zuerst den Namespace xmlns:hlp="clr-namespace:AC.Frontend.Helper"
und danach die Bindung hlp:AttachedProperties.DialogResult="{Binding DialogResult}"
.
Das AttachedProperty
sieht so aus. Es ist nicht dasselbe, was ich gestern gepostet habe, aber meiner Meinung nach sollte es keinen Effekt haben.
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
}
Einfacher Weg
public interface IRequireViewIdentification
{
Guid ViewID { get; }
}
Implementieren Sie in ViewModel
public class MyViewVM : IRequireViewIdentification
{
private Guid _viewId;
public Guid ViewID
{
get { return _viewId; }
}
public MyViewVM()
{
_viewId = Guid.NewGuid();
}
}
Fügen Sie einen allgemeinen Fenster-Manager hinzu
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();
}
}
}
}
Und schließe es so im Viewmodel
WindowManager.CloseWindow(ViewID);
es kann spät sein, aber hier ist meine Antwort
foreach (Window item in Application.Current.Windows)
{
if (item.DataContext == this) item.Close();
}
Wie wäre es mit this ?
ViewModel:
class ViewModel
{
public Action CloseAction { get; set; }
private void Stuff()
{
// Do Stuff
CloseAction(); // closes the window
}
}
Schließen Sie in Ihrem ViewModel das Fenster mit CloseAction () wie im obigen Beispiel.
Aussicht:
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());
}
So habe ich es ganz einfach gemacht:
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();
}
Ich sehe nichts Falsches mit der Antwort, die Sie gewählt haben, ich dachte nur, dass dies eine einfachere Möglichkeit wäre, dies zu tun!
Hier ist ein einfaches Beispiel, bei dem der MVVM Light Messenger anstelle eines Ereignisses verwendet wird. Das Ansichtsmodell sendet eine Schließmeldung, wenn auf eine Schaltfläche geklickt wird:
public MainViewModel()
{
QuitCommand = new RelayCommand(ExecuteQuitCommand);
}
public RelayCommand QuitCommand { get; private set; }
private void ExecuteQuitCommand()
{
Messenger.Default.Send<CloseMessage>(new CloseMessage());
}
Dann wird es im Code hinter dem Fenster empfangen.
public Main()
{
InitializeComponent();
Messenger.Default.Register<CloseMessage>(this, HandleCloseMessage);
}
private void HandleCloseMessage(CloseMessage closeMessage)
{
Close();
}
Sie können auf diese Weise einen neuen Ereignishandler im ViewModel erstellen.
public event EventHandler RequestClose;
protected void OnRequestClose()
{
if (RequestClose != null)
RequestClose(this, EventArgs.Empty);
}
Definieren Sie dann RelayCommand für 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();
}
Dann In XAML-Dateisatz
<Button Command="{Binding CloseCommand}" />
Legen Sie den DataContext in der xaml.cs-Datei fest und abonnieren Sie das von uns erstellte Ereignis.
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(); };
}
}
Sie können Messenger
aus dem MVVMLight-Toolkit verwenden. in deinem ViewModel
sende eine Nachricht wie diese:Messenger.Default.Send(new NotificationMessage("Close"));
Dann in Ihrem Windows-Code dahinter, nach InitializeComponent
, registrieren Sie sich für diese Nachricht wie folgt:
Messenger.Default.Register<NotificationMessage>(this, m=>{
if(m.Notification == "Close")
{
this.Close();
}
});
weitere Informationen zum MVVMLight-Toolkit finden Sie hier: MVVMLight-Toolkit auf Codeplex
Beachten Sie, dass es in MVVM keine "überhaupt keine Code-Behind" -Regel gibt und Sie sich für Nachrichten in einer Code-Behind-Ansicht registrieren können.
Meine angebotene Methode ist Declare event in ViewModel und verwende die Mischung InvokeMethodAction wie folgt.
Beispiel 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();
});
}
}
Die verschließbare Benutzeroberfläche ist wie folgt, muss jedoch nicht ausgeführt werden. ICloseable hilft beim Erstellen eines allgemeinen Ansichtsdienstes. Wenn Sie also View und ViewModel durch Abhängigkeitseingabe erstellen, können Sie Folgendes tun
internal interface ICloseable
{
event EventHandler CloseRequested;
}
Verwendung von ICsableable
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();
}
Und unten ist Xaml. Sie können dieses Xaml auch verwenden, wenn Sie keine Schnittstelle implementieren. Es wird nur Ihr Ansichtsmodell benötigt, um CloseRquested zu erzeugen.
<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>
Sie können das aktuelle Fenster einfach mit dem folgenden Code schließen:
Application.Current.Windows[0].Close();
Ich weiß, das ist ein alter Beitrag, wahrscheinlich würde niemand so weit scrollen, ich weiß, dass ich es nicht getan habe. Nachdem ich also stundenlang verschiedene Sachen ausprobiert hatte, fand ich diesen Blog und mein Typ hat ihn getötet. Die einfachste Art, dies zu tun, ausprobiert und es funktioniert wie ein Zauber.
Es ist ganz einfach .. Sie können Ihre eigene ViewModel-Klasse für Login erstellen - LoginViewModel . Sie können view var erstellen. in Ihrem LoginViewModel. Und Sie können Command LoginCommand in button einrichten.
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" />
und
<Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" />
ViewModel-Klasse:
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;
}
}