ViewModel'den Pencereyi Kapat


97

window controlBir kullanıcının WPFbenim oluşturduğum bir uygulamaya giriş yapmasına izin vermek için a kullanarak bir Giriş oluşturuyorum.

Şimdiye kadar, kullanıcının giriş ekranında usernameve passworda için doğru kimlik bilgilerini girip girmediğini kontrol eden bir yöntem oluşturdum textbox, bindingiki properties.

Ben de bunu bir boolyöntem oluşturarak başardım ;

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();
} 

Bir de bende var commandki gibi bindiçinde xamldüğmem var;

<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" />

Kullanıcı adını ve şifreyi girdiğimde, ister doğru ister yanlış olsun, kendisine ait olan kodu çalıştırır. Ancak hem kullanıcı adı hem de şifre doğru olduğunda bu pencereyi ViewModel'den nasıl kapatabilirim?

Daha önce a kullanmayı denedim dialog modalama pek işe yaramadı. Ayrıca, app.xaml dosyamda, aşağıdaki gibi bir şey yaptım, bu ilk önce giriş sayfasını yükler, sonra doğru olduğunda gerçek uygulamayı yükler.

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);
    }
}

Soru: Window controlViewModel'den Login'i nasıl kapatabilirim ?

Şimdiden teşekkürler.


Yanıtlar:


157

Pencereyi ViewModel'inize CommandParameter. Aşağıdaki Örneğime bakın.

CloseWindowBir Windows'u parametre olarak alan ve kapatan bir Yöntem uyguladım . Pencere, aracılığıyla ViewModel'e geçirilir CommandParameter. x:NameYakın olması gereken pencere için bir tanımlamanız gerektiğini unutmayın . XAML Penceremde bu yöntemi kullanarak bu yöntemi çağırıyorum Commandve pencerenin kendisini parametre olarak ViewModel'e iletiyorum CommandParameter.

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();
    }
}

Görünüm

<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>

MVVM hafif çerçevesini kullanıyorum, ancak ilke her wpf uygulaması için geçerlidir.

Bu çözüm MVVM modelini ihlal ediyor, çünkü görünüm modeli UI Uygulaması hakkında hiçbir şey bilmemelidir. MVVM programlama paradigmasını sıkı bir şekilde takip etmek istiyorsanız, görünümün türünü bir arayüzle soyutlamanız gerekir.

MVVM uyumlu çözüm (Eski EDIT2)

Crono kullanıcısı , yorum bölümünde geçerli bir noktadan bahseder:

Pencere nesnesini görünüm modeline geçirmek, MVVM desenini (IMHO) bozar, çünkü vm'nizi ne görüntülendiğini bilmeye zorlar.

Bir kapatma yöntemi içeren bir arabirim sunarak bunu düzeltebilirsiniz.

Arayüz:

public interface ICloseable
{
    void Close();
}

Yeniden düzenlenen ViewModel'iniz şöyle görünecek:

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();
    }
}

ICloseableArayüzü kendi görünümünüzde referans almanız ve uygulamanız gerekir

Görünüm (Arkasındaki kod)

public partial class MainWindow : Window, ICloseable
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

Orijinal sorunun cevabı: (eski EDIT1)

Oturum Açma Düğmeniz (CommandParameter Eklendi):

<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>

Senin kodun:

 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();
     }
 }

1
@Joel güncellemesi için teşekkürler. Son bir soru, bir Window parametresini alan yöntem nedeniyle ve bu yöntemi komutum içinde çağırdığımda, bir parametre bekler, yöntem için çağrılan yerel bir Window parametresi oluşturur muyum, örn; private void LoginExecute(){this.CheckLogin();}<- CheckLogin bir parametre almalıdır.
WPFNoob

üzgünüm anlamadım, sorunuzu biraz açıklar mısınız?
Joel

15
Pencerelerinize isim vermekten hoşlanmıyorsanız, parametreyi şu şekilde de bağlayabilirsiniz:CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Jacco Dieleman

33
Geçme Windowsizin vm o görüntülenmesiyle kalanlar. Görünüm yerine MDI arabiriminde bir demirledi sekme neydi olmadığını bilmek zorlar çünkü görünümü modeli sonları MVVM desen IMHO nesneyi? Bu IMHO'yu yapmanın doğru yolu, Close yöntemini uygulayan bir tür IUIHost arabirimini geçmek ve vm'nizin uyguladığını gösteren istediğiniz görünüme sahip olmaktır.
Crono

2
Sorun değil çünkü arayüz somut uygulamayı ViewModel'e gizler. ViewModel, Close () yöntemini uygulaması dışında görünüm hakkında hiçbir şey bilmiyor. Bu nedenle, görünüm herhangi bir şey olabilir: bir WPF Penceresi, WinForms Formu, UWP Uygulaması veya hatta bir WPF Izgarası. Görünümü görünüm modelinden ayırır.
Joel

37

Bunu yapmam gerektiğinde genellikle görünüm modeline bir olay koyarım ve ardından Window.Close()görünüm modelini pencereye bağlarken bunu öğesine bağlarım

public class LoginViewModel
{
    public event EventHandler OnRequestClose;

    private void Login()
    {
        // Login logic here
        OnRequestClose(this, new EventArgs());
    }
}

Ve oturum açma penceresini oluştururken

var vm = new LoginViewModel();
var loginWindow = new LoginWindow
{
    DataContext = vm
};
vm.OnRequestClose += (s, e) => loginWindow.Close();

loginWindow.ShowDialog(); 

11
Anonim delege hızlı bir şekilde yazılır, ancak olayın kayıtsız olamayacağına dikkat edilmelidir (bu bir sorun olabilir veya olmayabilir). Genellikle tam teşekküllü bir olay işleyiciyle daha iyi durumda.
Mathieu Guindon

1
En çok bunu beğendim. (Örneğin pencere görüntülerken özel işleme önlemek için yine de zor olduğunu Loaded, ContentRenderedbana gelince oldukça temiz, ana pencerede, iletişim hizmetleri, vb) ViewModel olayları yoluyla kendisine biraz ekleyerek. 3 satır kod gerçekten herhangi bir yeniden kullanılabilirlik çözümüne ihtiyaç duymaz. Not: saf MVVM zaten inekler içindir.
Sinatr

Oğlum bu bana yardımcı oldu.
Dimitri

1
Bu kabul edilen cevaptan çok daha iyidir çünkü MVVM modelini bozmaz.
Spook

35

MVVM olarak kalmak, Blend SDK'dan Behaviors (System.Windows.Interactivity) veya Prism'den özel bir etkileşim isteği kullanmanın bu tür durumlarda gerçekten işe yarayacağını düşünüyorum.

Davranış rotasına gidiyorsanız, işte genel fikir:

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();
        }
    }
}

Ardından pencerenizde, CloseTrigger'ı pencerenin kapanmasını istediğinizde ayarlanacak bir boolean değerine bağlarsınız.

<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>

Son olarak, DataContext / ViewModel'iniz, pencerenin şu şekilde kapanmasını istediğinizde ayarlayacağınız bir özelliğe sahip olacaktır:

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;
}

(Window.DataContext = new MainWindowViewModel () ayarınızı yapın)


@Steve cevabınız için teşekkürler, CloseTrigger'ı bir booleandeğere bağlamadan bahsettiniz . Bunu söylediğinde, benim DataTriggerbunu başarmak için bir yaratmamı mı kastettin ?
WPFNoob

Üzgünüm, daha açık konuşmalıydım - görünüm modelimde (yukarıdaki örnekte, CloseTrigger olarak adlandırılan) doğru olarak ayarlanacak ve davranışı tetikleyecek bir özelliğim olurdu. Cevabı güncelledim
Steve Van Treeck

Bu işe yaradı, ancak uygulamamın yüklenme şeklini değiştirmek zorunda kaldım. Ana uygulamam için bir Pencere kullandığım için, tüm alt pencereleri de öldürdü. Teşekkürler.
WPFNoob

Bir eylemi gerçekleştirmek için bir özelliği true olarak ayarlamak kokulu bir IMO'dur.
Josh Noe

22

geç olabilir ama işte cevabım

foreach (Window item in Application.Current.Windows)
{
    if (item.DataContext == this) item.Close();
}

1
neden gerçek cevap bu değil?
user2529011

1
@ user2529011 en azından bazıları viewmodel'in Application.Current.Windows hakkında hiçbir şey bilmemesi gerektiğinden şikayet ederdi
gusmally

-1. Görünüm modeli, görünüm hakkında hiçbir şey bilmemelidir. Siz de bu konuda arkasındaki koda yazabilirsiniz.
Alejandro

14

İşte birkaç projede kullandığım bir şey. Bilgisayar korsanlığı gibi görünebilir, ancak iyi çalışıyor.

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;
    }
}

Artık DialogResultbir sanal makineye bağlanabilir ve bir özelliğin değerini ayarlayabilirsiniz. WindowDeğer ayarlandığında, kapanacak.

<!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult -->
<Window someNs:AttachedProperties.DialogResult={Binding DialogResult} />

Bu, üretim ortamımızda nelerin çalıştığının bir özetidir

<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>

Gördüğünüz gibi, önce ad alanını xmlns:hlp="clr-namespace:AC.Frontend.Helper"ve ardından bağlayıcılığı beyan ediyorum hlp:AttachedProperties.DialogResult="{Binding DialogResult}".

Bunun AttachedPropertygibi görünüyor. Dün yayınladığımla aynı değil, ancak IMHO'nun herhangi bir etkisi olmamalı.

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
}

Hayır, aptalca bir soru değil. Bağlama bildirimini, <Window />kırptığımda gösterdiğim gibi öğeye koyun . Geri kalanı (ad alanı bildirimleri vb.) Yazmak için çok tembeldim, genellikle orada da bildirilir.
DHN

1
Pls benim düzenlememe bakın. Üretim kodunu gönderdim, bu yüzden çalıştığından eminim. Biraz farklı görünüyor ama dün yayınladığım kod da çalışmalı.
DHN

Bunu açıklığa kavuşturduğunuz için teşekkürler. Yanlış ad alanını çağırdığım ortaya çıktı: S. Sadece oluşturmak gerekiyor mu datatriggerve buna işler yapmak için düğmeye atamak? Nooby sorusu için tekrar özür dilerim.
WPFNoob

Teşekkürler - peki, aptalca ve aptalca görünen ve insanların zamanını boşa harcayan çok fazla soru sorduğumun farkındayım! Ama soruma geri dönersek. Bahsettiğiniz onca şeyden sonra pencereyi nasıl kapatırım? DataTrigger¬ and setting value Doğru ' kullan ?
WPFNoob

1
İşte bu kısım, sana bırakıyorum. o) düşünün DataContextarasında Dialog. VM setinin DataContext, özelliği DialogResultveya bağlı olduğunuz her şeyi ayarlayan trueveya falseböylece Dialogkapanmasını sağlayan bir komut sağlamasını bekliyorum .
DHN

13

Kolay yol

public interface IRequireViewIdentification
{
    Guid ViewID { get; }
}

ViewModel'e uygulayın

public class MyViewVM : IRequireViewIdentification
{
    private Guid _viewId;

    public Guid ViewID
    {
        get { return _viewId; }
    }

    public MyViewVM()
    {
        _viewId = Guid.NewGuid();
    }
}

Genel pencere yöneticisi yardımcısı ekle

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();
            }
        }
    }
}

Ve görünüm modelinde bu şekilde kapatın

WindowManager.CloseWindow(ViewID);

Çok güzel bir çözüm.
DonBoitnott

win public static void CloseWindow (Guid id, bool dialogResult) {foreach (Application.Current.Windows içinde Window window) {var w_id = window.DataContext as IRequireViewIdentification; eğer (w_id! = null && w_id.ViewID.Equals (id)) {window.DialogResult = dialogResult; window.Close (); }}} bunu şöyle çağırın: WindowManager.CloseWindow (_viewId, true);
lebhero

Güzel bir çözüm, yine de görünüm WindowManagermodeli ile View(açısından PresentationFramework) sıkıca bağlı olan görünüm modeli arasında sıkı bir bağlantı kurar . WindowManagerViewmodel'e bir arayüz aracılığıyla bir hizmet aktarılsaydı daha iyi olurdu . O zaman çözümünüzü kolayca farklı bir platforma taşıyabilirsiniz (diyebilirsiniz).
Spook

4

İşte bir olay yerine MVVM Light Messenger'ı kullanan basit bir örnek. Görünüm modeli, bir düğmeye tıklandığında bir kapanma mesajı gönderir:

    public MainViewModel()
    {
        QuitCommand = new RelayCommand(ExecuteQuitCommand);
    }

    public RelayCommand QuitCommand { get; private set; }

    private void ExecuteQuitCommand() 
    {
        Messenger.Default.Send<CloseMessage>(new CloseMessage());
    }

Daha sonra pencerenin arkasındaki kodda alınır.

    public Main()
    {   
        InitializeComponent();
        Messenger.Default.Register<CloseMessage>(this, HandleCloseMessage);
    }

    private void HandleCloseMessage(CloseMessage closeMessage)
    {
        Close();
    }

CloseMessage'ın uygulamasını nerede bulabileceğim konusunda tavsiye verebilir misiniz?
Roman O

CloseMessage, gönderilen mesajın türünü tanımlamak için kullanılan boş bir sınıftır. (Burada gerekli olmayan karmaşık mesaj bilgileri de içerebilir.)
IngoB

4

Nasıl hakkında bu ?

ViewModel:

class ViewModel
{
    public Action CloseAction { get; set; }
    private void Stuff()
    {
       // Do Stuff
       CloseAction(); // closes the window
    }
}

ViewModel'inizde, yukarıdaki örnekte olduğu gibi pencereyi kapatmak için CloseAction () kullanın.

Görünüm:

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());
}

4

Bunun eski bir gönderi olduğunu biliyorum, muhtemelen kimse bu kadar ileri gitmez, yapmadığımı biliyorum. Saatlerce farklı şeyler denedikten sonra bu blogu buldum ve ahbap onu öldürdü. Bunu yapmanın en basit yolu, denedi ve bir cazibe gibi çalışıyor.

Blog

ViewModel'de:

...

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();
}

...

ViewModel'e bir Action özelliği ekleyin, ancak bunu View'un arka plan kod dosyasından tanımlayın. Bu, ViewModel'de View'e işaret eden bir referansı dinamik olarak tanımlamamıza izin verecektir.

ViewModel'de şunları ekleyeceğiz:

public Action CloseAction { get; set; }

Ve Görünümde, bunu şu şekilde tanımlayacağız:

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());
}

Bağlantı kesildi: /
2019'da Monica'yı

@gusmally emin misin? Normalde
Serlok

2

ViewModel'de bunun gibi yeni Olay işleyicisi oluşturabilirsiniz.

public event EventHandler RequestClose;

    protected void OnRequestClose()
    {
        if (RequestClose != null)
            RequestClose(this, EventArgs.Empty);
    }

Ardından Çıkış Komutu için RöleKomutunu Tanımlayın.

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();
    }

Sonra XAML dosya kümesinde

<Button Command="{Binding CloseCommand}" />

DataContext'i xaml.cs Dosyasında ayarlayın ve oluşturduğumuz etkinliğe abone olun.

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(); };
    }
}

Etkinlik yerine MVVM Light Messenger kullandım.
Hamish Gunn

1

Benim önerdiğim yöntem ViewModel'de Declare olayı ve aşağıdaki gibi InvokeMethodAction harmanını kullan.

Örnek Görünüm Modeli

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();
        });
    }
}

Kapatılabilir arayüz aşağıdaki gibidir ancak bu eylemi gerçekleştirmenize gerek yoktur. ICloseable, genel görünüm hizmeti oluşturmada yardımcı olacaktır, bu nedenle, bağımlılık ekleme yoluyla görünümü ve ViewModel'i oluşturursanız, yapabilecekleriniz

internal interface ICloseable
{
    event EventHandler CloseRequested;
}

ICloseable Kullanımı

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();
        }

Ve aşağıda Xaml var, bu xaml'i arayüz uygulamasanız bile kullanabilirsiniz, sadece CloseRquested'ı yükseltmek için görünüm modelinize ihtiyaç duyacaktır.

<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>


1

MessengerMVVMLight araç setinden kullanabilirsiniz . Bunun ViewModelgibi bir mesaj gönderdiğinizde:
Messenger.Default.Send(new NotificationMessage("Close"));
sonra Windows kodunuzda, ardından InitializeComponent, bu mesaj için şu şekilde kayıt olun:

Messenger.Default.Register<NotificationMessage>(this, m=>{
    if(m.Notification == "Close") 
    {
        this.Close();
    }
   });

MVVMLight araç seti hakkında daha fazla bilgiyi burada bulabilirsiniz: Codeplex'te MVVMLight araç seti

MVVM'de "arka planda kod yok" kuralı olmadığına ve arka planda bir görünüm kodundaki mesajlar için kayıt yapabileceğinize dikkat edin.


0

Basit. Login - LoginViewModel için kendi ViewModel sınıfınızı oluşturabilirsiniz. Görünüm var dialog = new UserView (); LoginViewModel'inizin içinde. Ve Command LoginCommand'i butona ayarlayabilirsiniz.

<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" />

ve

<Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" />

ViewModel sınıfı:

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;
    }
}

3
Evet, aynı zamanda geçerli bir çözüm. Ancak, MVVM'ye ve VM'lerin ve görünümlerin ayrıştırılmasına bağlı kalmak istiyorsanız, düzeni bozacaksınız.
DHN

Merhaba @misak - çözümünüzü uygulamaya çalışırken (diğer yanıtlar gibi), Object reference not set to an instance of an object.CloseLoginView yöntemi için bir atar . Bu sorunun nasıl çözüleceğine dair herhangi bir öneriniz var mı?
WPFNoob

@WPFNoob - Bu çözümü tekrar kullandım. Örnek doğru çalışıyor. E-postayla eksiksiz görsel stüdyo çözümü göndermek ister misiniz?
misak

@WPFNoob - Sorunu görüyorum. Var dialog = new UserView (); olarak örnek oluşturuyorsunuz. Var anahtar kelimesini temizle (yerel örnek) LoginViewModel'de genel örneğin üzerine yazıyor
misak

0

Bunu oldukça basit bir şekilde yaptım:

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();
}

Seçtiğiniz cevapta yanlış bir şey görmüyorum, sadece bunun daha basit bir yol olabileceğini düşündüm!


8
Bu, ViewModel'inizin Görünümünüzü bilmesini ve buna referans vermesini gerektirir.
AndrewS

@AndrewS neden bu kadar kötü?
thestephenstanton

9
MVVM modelini izlemek için, ViewModel Görünüm hakkında bilgi sahibi olmamalıdır.
MetalMikester

1
Bunu genişletmek için, MVVM'nin amacı, GUI kod biriminizin çoğunu test edilebilir hale getirmektir. Görüntülerin, birim testini imkansız kılan bir ton bağımlılığı vardır. ViewModel'lar birim test edilebilir olmalıdır, ancak onlara görünüme doğrudan bir bağımlılık verirseniz, olmayacaklar.
ILMTitan

Ve bunu daha da genişletmek için, düzgün yazılmış MVVM, çözümü kolayca farklı bir platforma taşımanıza olanak tanır. Özellikle, görünüm modellerinizi herhangi bir değişiklik yapmadan yeniden kullanabilmelisiniz. Bu durumda, çözümünüzü Android'e taşıdıysanız, işe yaramaz çünkü Android'de bir Pencere kavramı yoktur. MVVM bozma çözümü için -1.
Spook

0

Pencereyi bir hizmet (ör. UI hizmeti) olarak ele alabilir ve kendisini aşağıdaki gibi bir arayüz aracılığıyla görüntüleme modeline geçirebilirsiniz :

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);
    }
}

Bu çözüm, MVVM'yi bozma dezavantajına sahip olmadan görünümün kendisini görünüm modeline geçirmenin pek çok avantajına sahiptir, çünkü fiziksel olarak görünüm görünüm modeline aktarılırken, ikincisi hala birincisini bilmiyor, yalnızca bazılarını görüyor IMainWindowAccess. Örneğin, bu çözümü başka bir platforma taşımak isteseydik, mesele yalnızca IMainWindowAccess, örneğin bir Activity.

Olaylardan farklı bir yaklaşım önermek için çözümü buraya gönderiyorum (aslında çok benzer olsa da), çünkü uygulanacak olaylardan biraz daha basit görünüyor (ekleme / ayırma vb.), Ancak yine de MVVM modeliyle uyumludur.



-7

System.Environment.Exit (0); görünümde model işe yarayacaktır.


6
Hayır olmayacak. Uygulamadan çıkacak ve mevcut pencereyi kapatmayacaktır.
Tilak

bu benim problemimi çözdü, çünkü mainWindow (bana) == uygulamadan çıkılıyor. Bunun dışında önerilen tüm yöntemlerin farklı konulardan çağrıldığında yanıltıcı noktaları vardı; ama bu yaklaşım, arayan ileti dizisinin kim olduğunu gerçekten umursamıyor :) ihtiyacım olan tek şey buydu!
Hamid

BuAHahahAHahahAha üzgünüm dayanamadı
L.Trabacchin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.