ViewModel से विंडो बंद करें


95

Im एक लॉगिन का उपयोग करके window controlएक उपयोगकर्ता को लॉगिन करने की अनुमति देता हैWPF एप्लिकेशन जो मैं बना रहा हूं।

अब तक, मैं एक विधि है कि जाँच करता है कि उपयोगकर्ता के लिए सही साख में प्रवेश किया है बनाया है usernameऔर passwordएक में textboxप्रवेश स्क्रीन पर, bindingदोproperties

मैंने एक boolविधि बनाकर इसे हासिल किया है , जैसे;

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

मेरे पास भी ऐसा है commandकि मैं bindइस xamlतरह से अपने बटन को दबाता हूं ;

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

जब मैं उपयोगकर्ता नाम और पासवर्ड दर्ज करता हूं तो यह विनियोजित कोड निष्पादित करता है, चाहे वह सही हो, या गलत हो। लेकिन मैं इस विंडो को ViewModel से कैसे बंद कर सकता हूं जब उपयोगकर्ता नाम और पासवर्ड दोनों सही हैं?

मैंने पहले प्रयोग करने की कोशिश की है dialog modalलेकिन यह काफी कारगर नहीं रहा। इसके अलावा, मेरे app.xaml के भीतर, मैंने निम्नलिखित की तरह कुछ किया है, जो पहले लॉगिन पृष्ठ को लोड करता है, फिर एक बार सही होने पर वास्तविक एप्लिकेशन को लोड करता है।

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

प्रश्न: मैं Window controlViewModel से लॉगिन कैसे बंद कर सकता हूं ?

अग्रिम में धन्यवाद।


जवाबों:


147

आप अपने ViewModel का उपयोग करके विंडो को पास कर सकते हैं CommandParameter। नीचे मेरा उदाहरण देखें।

मैंने एक CloseWindowतरीका लागू किया है जो एक विंडोज को पैरामीटर के रूप में लेता है और इसे बंद कर देता है। खिड़की ViewModel के माध्यम से पारित कर दिया है CommandParameter। ध्यान दें कि आपको x:Nameखिड़की के लिए परिभाषित करने की आवश्यकता है जो पास होना चाहिए। मेरे XAML विंडो में मैं इस विधि के माध्यम से कॉल करता हूं Commandऔर विंडो का उपयोग करते हुए ViewModel के पैरामीटर के रूप में खुद को पास करता हूं 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();
    }
}

राय

<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 प्रकाश ढांचे का उपयोग कर रहा हूं, लेकिन प्रिंसिपल हर wpf एप्लिकेशन पर लागू होता है।

यह समाधान MVVM पैटर्न का उल्लंघन करता है, क्योंकि व्यू-मॉडल को UI कार्यान्वयन के बारे में कुछ भी पता नहीं होना चाहिए। यदि आप MVVM प्रोग्रामिंग प्रतिमान का सख्ती से पालन करना चाहते हैं, तो आपको इंटरफ़ेस के साथ दृश्य के प्रकार को अमूर्त करना होगा।

MVVM अनुरूप समाधान (पूर्व EDIT2)

उपयोगकर्ता क्रोनो टिप्पणी अनुभाग में एक वैध बिंदु का उल्लेख करता है:

दृश्य मॉडल के लिए विंडो ऑब्जेक्ट को पास करना MVVM पैटर्न IMHO को तोड़ता है, क्योंकि यह आपके vm को यह जानने के लिए मजबूर करता है कि इसमें क्या देखा जा रहा है।

आप एक करीबी विधि से युक्त इंटरफ़ेस को शुरू करके इसे ठीक कर सकते हैं।

इंटरफेस:

public interface ICloseable
{
    void Close();
}

आपका रिफलेक्टेड ViewModel इस तरह दिखेगा:

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

आपको ICloseableअपने दृष्टिकोण में इंटरफ़ेस को संदर्भित और कार्यान्वित करना होगा

देखें (पीछे कोड)

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

मूल प्रश्न का उत्तर: (पूर्व EDIT1)

आपका लॉगिन बटन (जोड़ा गया कमांडपेयर):

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

तुम्हारा कोड:

 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 अपडेट के लिए धन्यवाद। एक आखिरी सवाल, विंडो के एक पैरामीटर में विधि लेने के कारण, और जब मैं अपने कमांड के भीतर उस विधि को कॉल करता हूं, तो यह एक पैरामीटर की उम्मीद करता है, क्या मैं एक स्थानीय विंडो पैरामीटर बनाऊंगा जिसे विधि के लिए कहा जाता है, जैसे; private void LoginExecute(){this.CheckLogin();}<- CheckLogin को एक पैरामेटर में लेने की आवश्यकता है।
WPFNOB

क्षमा करें, मुझे यह नहीं मिला, क्या आप अपने प्रश्न को थोड़ा स्पष्ट कर सकते हैं?
जोएल

14
यदि आपको अपनी खिड़कियों का नामकरण पसंद नहीं है, तो आप इस तरह के पैरामीटर को भी बांध सकते हैं:CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
जैको डाइलेमैन

33
Windowव्यू मॉडल के लिए ऑब्जेक्ट को पास करना MVVM पैटर्न IMHO को तोड़ता है, क्योंकि यह आपके vm को यह जानने के लिए मजबूर करता है कि इसमें क्या देखा जा रहा है। यदि एमडीआई इंटरफेस में इसके बजाय डॉक किया गया टैब क्या होता? इस IMHO को करने का उचित तरीका कुछ प्रकार के IUIHost इंटरफ़ेस को पास करना है जो एक करीबी विधि को लागू करता है, और जो कुछ भी आप अपना vm दिखाना चाहते हैं उसे लागू करें।
क्रोनो

2
यह ठीक है क्योंकि इंटरफ़ेस ठोस कार्यान्वयन को ViewModel को छुपाता है। ViewModel दृश्य के बारे में कुछ भी नहीं जानता है सिवाय इसके कि यह एक बंद () विधि को लागू करता है। इस प्रकार, दृश्य कुछ भी हो सकता है: एक WPF विंडो, WinForms फॉर्म, UWP एप्लिकेशन या यहां तक ​​कि एक WPF ग्रिड। यह दृश्यमॉडल से दृश्य को प्रदर्शित करता है।
जोएल

34

MVVM रहकर, मुझे लगता है कि Blend SDK (System.Windows.Interactivity) से व्यवहार का उपयोग करना या Prism से एक कस्टम इंटरैक्शन अनुरोध इस तरह की स्थिति के लिए वास्तव में अच्छी तरह से काम कर सकता है।

यदि व्यवहार मार्ग जा रहा है, तो यहाँ सामान्य विचार है:

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

तब आपकी विंडो में, आप बस CloseTrigger को एक बूलियन मान से बाँधेंगे जो तब सेट किया जाएगा जब आप विंडो को बंद करना चाहते थे।

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

अंत में, आपके DataContext / ViewModel में एक संपत्ति होगी जिसे आप तब सेट करेंगे जब आप विंडो को इस तरह से बंद करना चाहते थे:

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 () सेट करें)


उत्तर @ धन्यवाद के लिए धन्यवाद, आपने CloseTrigger को एक booleanमान में बांधने के बारे में उल्लेख किया है । जब आपने कहा था कि, क्या आप मेरे लिए DataTriggerइसे प्राप्त करने के लिए एक बनाने का मतलब है?
WPFNoob

क्षमा करें, मुझे और अधिक स्पष्ट होना चाहिए था - मेरे पास अपने व्यूमोडेल (उपरोक्त उदाहरण में, एक जिसे CloseTrigger कहा जाता है) पर एक संपत्ति होगी जो कि सही पर सेट हो जाएगी, जो व्यवहार को ट्रिगर कर देगा। मुझे जवाब मिला
स्टीव वान ट्रीक

इसने काम किया, लेकिन मुझे अपना आवेदन लोड करने का तरीका बदलना पड़ा। क्योंकि मैं अपने मुख्य अनुप्रयोग के लिए एक विंडो का उपयोग कर रहा था, इसने सभी बाल खिड़कियों को अस्वस्थ कर दिया। धन्यवाद।
WPFNoob

एक कार्रवाई करने के लिए एक संपत्ति को सही पर सेट करना बदबूदार आईएमओ है।
जोश नू

33

मैं आमतौर पर दृश्य मॉडल पर एक घटना डालता हूं जब मुझे ऐसा करने की आवश्यकता होती है और फिर Window.Close()खिड़की से दृश्य मॉडल को बांधते समय इसे हुक करता है

public class LoginViewModel
{
    public event EventHandler OnRequestClose;

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

और लॉगिन विंडो बनाते समय

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

loginWindow.ShowDialog(); 

11
बेनामी प्रतिनिधि को जल्दी से लिखा जाता है, लेकिन यह ध्यान देने योग्य है कि घटना को अपंजीकृत नहीं किया जा सकता है (जो एक मुद्दा हो सकता है या नहीं भी हो सकता है)। आमतौर पर एक पूर्ण इवेंट हैंडलर के साथ बेहतर है।
मैथ्यू गुइंडन

मुझे यह सबसे ज्यादा पसंद है। विंडो को प्रदर्शित करते समय विशेष प्रसंस्करण से बचने के लिए वैसे भी कठिन है (उदाहरण के लिए Loaded, ContentRenderedमुख्य विंडो, संवाद सेवाओं, आदि के लिए), ViewModel घटना के माध्यम से इसमें थोड़ा सा जोड़ना मेरे लिए बहुत साफ है। कोड की 3 लाइनों को वास्तव में किसी भी पुन: प्रयोज्य समाधान की आवश्यकता नहीं है। पुनश्च: शुद्ध MVVM वैसे भी नर्ड के लिए है।
सिनट्रेड

लड़के ने मेरी यह मदद की।
दिमित्री

यह स्वीकृत उत्तर से कहीं बेहतर है, क्योंकि यह MVVM पैटर्न को नहीं तोड़ता है।
भूत

22

देर हो सकती है, लेकिन यहाँ मेरा जवाब है

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

1
यह वास्तविक उत्तर क्यों नहीं है?
user2529011

1
@ user2529011 कुछ, कम से कम, शिकायत करेंगे कि
दृश्यमॉडल को

-1। दृश्य मॉडल को दृश्य के बारे में कुछ भी नहीं जानना चाहिए। आप इस मामले के लिए कोड के पीछे भी लिख सकते हैं।
अलेजांद्रो

13

अच्छी तरह से यहाँ कुछ है जो मैंने कई परियोजनाओं में उपयोग किया है। यह हैक की तरह लग सकता है, लेकिन यह ठीक काम करता है।

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

अब आप DialogResultएक वीएम को बांध सकते हैं और एक संपत्ति का मूल्य निर्धारित कर सकते हैं। Windowबंद हो जाएगा, जब मूल्य निर्धारित है।

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

यह हमारे उत्पादन वातावरण में चल रहे एक सार है

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

जैसा कि आप देख सकते हैं, मैं नाम स्थान की घोषणा कर रहा हूं xmlns:hlp="clr-namespace:AC.Frontend.Helper" पहले और बाद में बाइंडिंगhlp:AttachedProperties.DialogResult="{Binding DialogResult}"

इस AttachedPropertyतरह दिखता है। यह वही नहीं है जिसे मैंने कल पोस्ट किया था, लेकिन IMHO पर इसका कोई प्रभाव नहीं होना चाहिए।

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
}

नहीं, यह एक मूर्खतापूर्ण सवाल नहीं है। <Window />जैसा कि मैंने अपने स्निपेट में दर्शाया है, वैसे ही एलिमेंट में बाइंडिंग का ऐलान कर दिया । मैं बाकी (नाम स्थान की घोषणा आदि) लिखने के लिए बहुत आलसी था, जो आमतौर पर वहां भी घोषित होता है।
डीएचएन

1
Pls मेरे संपादन को देखें। मैंने उत्पादन कोड पोस्ट किया है, इसलिए मुझे यकीन है कि यह काम कर रहा है। यह थोड़ा अलग दिखता है, लेकिन मैंने कल जो कोड पोस्ट किया है वह भी काम करना चाहिए।
डीएचएन

इसे साफ करने के लिए धन्यवाद। यह पता चला कि मैं गलत नाम स्थान कह रहा था: एस। क्या मुझे datatriggerइसे बनाने और इसे कार्य करने के लिए बटन को असाइन करने की आवश्यकता है ? फिर से नोबी सवाल के लिए खेद है।
WPFNoob

धन्यवाद - ठीक है, मैं बस मुझे बहुत अधिक सवाल पूछ रहा हूँ जो मूर्खतापूर्ण और बेवकूफ लग सकता है और लोगों का समय बर्बाद कर रहा है! लेकिन अपने सवाल पर वापस जा रहा हूं। आपके द्वारा बताई गई हर बात के बाद, मैं खिड़की को कैसे बंद करूं? एक DataTrigger¬ and setting value सच्चे का उपयोग करें ?
WPFNoob

1
खैर यह हिस्सा है, मैं आपको छोड़ रहा हूं। ; ओ) के बारे में सोचो DataContextकी Dialog। मैं उम्मीद होती है, वीएम सेट है कि के रूप में DataContextएक आदेश है, जो संपत्ति सेट प्रदान करता है DialogResultया जो भी आप करने के लिए बाध्य किया है trueया falseतो यह है कि Dialogबंद करता है।
डीएचएन

13

आसान तरीका

public interface IRequireViewIdentification
{
    Guid ViewID { get; }
}

ViewModel के लिए लागू करें

public class MyViewVM : IRequireViewIdentification
{
    private Guid _viewId;

    public Guid ViewID
    {
        get { return _viewId; }
    }

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

सामान्य विंडो प्रबंधक सहायक जोड़ें

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

और इसे viewmodel में इस तरह से बंद करें

WindowManager.CloseWindow(ViewID);

एक बहुत अच्छा समाधान।
DonBoitnott

मैंने जीत सार्वजनिक स्थैतिक शून्य क्लोज़विंडो (गाइड आईडी, बूल डायलॉगसेट) {foreach (विंडो विंडो में Application.Current.Windows) {var w_id =.DataContext के रूप में IRequireViewIdentification के रूप में जीतते हुए संवाद स्थापित करने के लिए WindowManager को बदल दिया। if (w_id! = null && w_id.ViewID.Equals (id)) {window.DialogResult = dialogResult; window.Close (); }}} इसे कॉल करें: WindowManager.CloseWindow (_viewId, true);
लेबेरो

अच्छा समाधान, हालांकि दृश्यमॉडल के बीच तंग युग्मन बनाता है और WindowManager, जो बदल जाता है के साथ कसकर युग्मित होता है View(संदर्भ में PresentationFramework)। यह बेहतर होगा यदि WindowManagerएक इंटरफ़ेस के माध्यम से viewmodel के लिए एक सेवा पारित की गई थी। तब आप आसानी से एक अलग मंच पर अपने समाधान को स्थानांतरित करने (कहने) में सक्षम होंगे।
भूत

4

यहां एक घटना के बजाय MVVM लाइट मैसेंजर का उपयोग करके एक सरल उदाहरण दिया गया है। जब बटन क्लिक किया जाता है तो दृश्य मॉडल एक निकट संदेश भेजता है:

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

    public RelayCommand QuitCommand { get; private set; }

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

फिर यह विंडो के पीछे कोड में प्राप्त होता है।

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

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

क्या आप सलाह दे सकते हैं, जहां मैं क्लोज़मैसेज का कार्यान्वयन पा सकता हूं?
रोमन ओ

क्लोजमैसेज सिर्फ एक खाली वर्ग है, जिसका उपयोग संदेश भेजे जाने के प्रकार की पहचान करने के लिए किया जाता है। (इसमें जटिल संदेश
इन्फोस

4

इस बारे में कैसे? ?

ViewModel:

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

अपने ViewModel में ऊपर के उदाहरण में विंडो बंद करने के लिए CloseAction () का उपयोग करें।

राय:

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

3

मुझे पता है कि यह एक पुरानी पोस्ट है, शायद कोई भी इसे दूर तक नहीं ले जाएगा, मुझे पता है कि मैंने ऐसा नहीं किया। इसलिए, अलग-अलग सामान आज़माने के बाद, मुझे यह ब्लॉग मिला और दोस्त ने इसे मार दिया। ऐसा करने का सबसे सरल तरीका, इसे आज़माया और यह एक आकर्षण की तरह काम करता है।

ब्लॉग

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

...

ViewModel में एक एक्शन प्रॉपर्टी जोड़ें, लेकिन इसे व्यू के कोड-बैक फ़ाइल से परिभाषित करें। यह हमें ViewModel पर एक संदर्भ को गतिशील रूप से परिभाषित करेगा जो दृश्य को इंगित करता है।

ViewModel पर, हम बस जोड़ देंगे:

public Action CloseAction { get; set; }

और दृश्य पर, हम इसे इस तरह परिभाषित करेंगे:

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

लिंक टूट गया है: /
gusmally

@gusmally क्या आपको यकीन है? मैंने इसे सामान्य रूप से खोला, पुनः प्रयास करें jkshay.com/…
Serlok

2

आप इस तरह ViewModel में नया ईवेंट हैंडलर बना सकते हैं।

public event EventHandler RequestClose;

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

फिर एक्सिटकमांड के लिए रिलेकेमैंड को परिभाषित करें।

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

फिर XAML फ़ाइल सेट में

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

DataContext को xaml.cs फ़ाइल में सेट करें और हमारे द्वारा बनाई गई घटना की सदस्यता लें।

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

मैंने इवेंट के बजाय MVVM लाइट मैसेंजर का इस्तेमाल किया।
हमीश गुन

1

मेरा प्रोफाइम्ड तरीका ViewModel में डिक्लेयर इवेंट है और नीचे के रूप में ब्लेंड इनवोकमेथोडेशन का उपयोग करें।

नमूना 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 क्लोज करने योग्य इंटरफ़ेस नीचे है, लेकिन इस क्रिया को करने की आवश्यकता नहीं है। ICloseable सामान्य दृश्य सेवा बनाने में मदद करेगा, इसलिए यदि आप निर्भरता इंजेक्शन द्वारा दृश्य और ViewModel का निर्माण करते हैं तो आप क्या कर सकते हैं

internal interface ICloseable
{
    event EventHandler CloseRequested;
}

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

और नीचे Xaml है, आप इस xaml का उपयोग कर सकते हैं, भले ही आप इंटरफ़ेस को लागू न करें, यह केवल आपके दृश्य मॉडल को 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>


1

आप MessengerMVVMLight टूलकिट से उपयोग कर सकते हैं । अपने ViewModelसंदेश को इस तरह भेजें:
Messenger.Default.Send(new NotificationMessage("Close"));
उसके बाद अपने विंडोज़ कोड में, उसके बाद InitializeComponent, उस संदेश के लिए इस तरह रजिस्टर करें:

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

आप MVVMLight टूलकिट के बारे में और अधिक जानकारी यहाँ पा सकते हैं: कोडप्लेक्स पर MVVMLight टूलकिट

ध्यान दें कि MVVM में "कोई भी नियम नहीं है" और आप किसी कोड दृश्य में संदेशों के लिए पंजीकरण कर सकते हैं।


0

यह आसान है। आप लॉगिन के लिए अपना खुद का ViewModel क्लास बना सकते हैं - LoginViewModel। आप दृश्य संवाद = नया उपयोगकर्ता दृश्य () बना सकते हैं; अपने LoginViewModel के अंदर। और आप Command LoginCommand को बटन में सेट कर सकते हैं।

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

तथा

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

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

3
हां, यह एक वैध समाधान भी है। लेकिन अगर आप MVVM और VMs और विचारों के डिकॉप्लिंग से चिपके रहना चाहते हैं, तो आप पैटर्न को तोड़ने जा रहे हैं।
डीएचएन

हाय @ मिसाक - अपने समाधान को लागू करने की कोशिश कर रहा है (अन्य उत्तरों की तरह), यह Object reference not set to an instance of an object.क्लोजलाइन व्यू पद्धति के लिए फेंकता है । कोई सुझाव कैसे उस मुद्दे को हल करने के लिए?
WPFNoob

@PFNoob - मैं इस समाधान को फिर से ट्रे करता हूं। उदाहरण सही ढंग से काम करता है। क्या आप ईमेल पर संपूर्ण दृश्य स्टूडियो समाधान भेजना चाहते हैं?
मि

@PFNoob - मुझे समस्या दिखाई देती है। आप var डायलॉग = नया उपयोगकर्ता दृश्य (); साफ कीवर्ड वर (स्थानीय उदाहरण) LoginViewModel में वैश्विक उदाहरण अधिलेखित कर देता है
Misak

0

यह एक तरह से मैंने इसे बहुत सरलता से किया है:

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

मैंने आपके द्वारा चुने गए उत्तर के साथ कुछ भी गलत नहीं देखा है, मुझे लगा कि यह करने का एक अधिक सरल तरीका हो सकता है!


8
इसके लिए आपके ViewModel की आवश्यकता है और अपने View को देखें।
एंड्रयूज

@ और यह क्यों बुरा है?
thestephenstanton

9
MVVM पैटर्न का पालन करने के लिए, ViewModel को व्यू के बारे में पता नहीं होना चाहिए।
19

1
इस पर विस्तार करने के लिए, MVVM का बिंदु आपके अधिकांश GUI कोड इकाई को परीक्षण योग्य बनाना है। दृश्य में निर्भरता का एक टन होता है जो उन्हें इकाई परीक्षण के लिए असंभव बनाता है। ViewModels इकाई परीक्षण योग्य होना चाहिए, लेकिन यदि आप उन्हें दृश्य पर प्रत्यक्ष निर्भरता देते हैं, तो वे नहीं होंगे।
ILMTitan

और आगे इस पर विस्तार करने के लिए, ठीक से लिखा गया एमवीवीएम आपको एक अलग मंच पर आसानी से समाधान स्थानांतरित करने की अनुमति देता है। विशेष रूप से, आपको बिना किसी बदलाव के अपने व्यूअमॉडेल्स का पुन: उपयोग करने में सक्षम होना चाहिए। इस स्थिति में यदि आप अपने समाधान को Android पर ले जाते हैं, तो यह काम नहीं करेगा, क्योंकि Android के पास विंडो की कोई अवधारणा नहीं है। एमवीवीएम-ब्रेकिंग समाधान के लिए -1।
भूत

0

आप विंडो को एक सेवा के रूप में मान सकते हैं (उदाहरण के लिए यूआई सेवा) और इंटरफ़ेस के माध्यम से viewmodel को पास करते हैं , जैसे:

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

इस समाधान में MVVM को तोड़ने के बिना दृश्यमॉडल को देखने के लिए स्वयं को पास करने का सबसे अधिक निर्णय है, क्योंकि यद्यपि भौतिक रूप से दृश्यमॉडल को पारित किया जाता है, बाद वाले को अभी भी पूर्व के बारे में नहीं पता है, यह केवल कुछ देखता है IMainWindowAccess। उदाहरण के लिए, यदि हम इस समाधान को अन्य प्लेटफ़ॉर्म पर स्थानांतरित करना चाहते हैं, तो यह केवल एक के लिए IMainWindowAccessठीक से लागू करने की बात होगीActivity

मैं घटनाओं की तुलना में एक अलग दृष्टिकोण का प्रस्ताव करने के लिए यहां समाधान पोस्ट कर रहा हूं (हालांकि यह वास्तव में बहुत समान है), क्योंकि यह घटनाओं को लागू करने (संलग्न करने / हटाने आदि) की तुलना में थोड़ा सरल लगता है, लेकिन फिर भी MVVM पैटर्न के साथ अच्छी तरह से संरेखित करता है।


-1

आप निम्न कोड का उपयोग करके वर्तमान विंडो को बंद कर सकते हैं:

Application.Current.Windows[0].Close();

6
यदि आपके पास एक से अधिक विंडो हैं, तो यह गलत विंडो को बंद कर सकता है।
साशा

17
हे भगवान! आपने
एमवीवीएम

-7

System.Environment.Exit (0); देखने में मॉडल काम करेगा।


6
नहीं यह अभ्यस्त। यह एप्लिकेशन से बाहर निकल जाएगा, और वर्तमान विंडो को बंद नहीं करेगा।
तिलक

इसने मेरी समस्या हल कर दी, क्योंकि मेनविंडो को बंद करना (मुझे) == एप्लिकेशन से बाहर निकलना। इसके अलावा सभी प्रस्तावित तरीके अलग-अलग धागों से बुलाए जाने पर मुश्किल बिंदु थे; लेकिन इस दृष्टिकोण को वास्तव में परवाह नहीं है कि कॉलर थ्रेड कौन है :) जो मुझे चाहिए था!
हामेद

बुआहाहाहाहाहाहा सॉरी विरोध नहीं कर सकता था
एल। क्रैबचिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.