व्यूमॉडल को फॉर्म कैसे बंद करना चाहिए?


247

मैं WPF और MVVM समस्या सीखने की कोशिश कर रहा हूं, लेकिन एक रोड़ा मारा है। यह प्रश्न समान है लेकिन यह एक जैसा नहीं है (हैंडलिंग-डायलॉग-इन- wpf-with-mvvm ...

मेरे पास MVVM पैटर्न का उपयोग करके एक "लॉगिन" फ़ॉर्म है।

इस फ़ॉर्म में एक ViewModel है, जो उपयोगकर्ता नाम और पासवर्ड रखता है, जो सामान्य डेटा बाइंडिंग का उपयोग करके XAML में दृश्य के लिए बाध्य हैं। इसमें एक "लॉगिन" कमांड भी है जो फॉर्म पर "लॉगिन" बटन से बंधा है, सामान्य डेटाबाइंडिंग का उपयोग करके अगैन।

जब "लॉगिन" कमांड फायर करता है, तो यह ViewModel में एक फ़ंक्शन को आमंत्रित करता है जो बंद हो जाता है और लॉग इन करने के लिए नेटवर्क पर डेटा भेजता है। जब यह फ़ंक्शन पूरा हो जाता है, तो 2 क्रियाएं होती हैं:

  1. लॉगिन अमान्य था - हम सिर्फ एक संदेश बॉक्स दिखाते हैं और सब ठीक है

  2. लॉगिन मान्य था, हमें लॉगिन फ़ॉर्म को बंद करने की आवश्यकता है और क्या यह उसके रूप में सही है DialogResult...

समस्या यह है कि, ViewModel को वास्तविक दृश्य के बारे में कुछ भी नहीं पता है, इसलिए यह दृश्य को कैसे बंद कर सकता है और किसी विशेष डायलॉगResult को वापस करने के लिए कह सकता है ?? मैं कोड कोड में कुछ कोड चिपका सकता था, और / या ViewModel के माध्यम से दृश्य पास कर सकता था, लेकिन ऐसा लगता है कि यह पूरी तरह से MVVM के पूरे बिंदु को हरा देगा ...


अपडेट करें

अंत में मैंने एमवीवीएम पैटर्न की "शुद्धता" का उल्लंघन किया और व्यू को एक Closedघटना प्रकाशित किया, और एक Closeविधि का पर्दाफाश किया । ViewModel तो बस फोन होगा view.Close। दृश्य केवल एक इंटरफेस के माध्यम से जाना जाता है और एक आईओसी कंटेनर के माध्यम से वायर्ड किया जाता है, इसलिए कोई परीक्षण या स्थिरता नहीं खो जाती है।

यह मूर्खतापूर्ण लगता है कि स्वीकृत उत्तर -5 मतों पर है! जब मैं अच्छी भावनाओं के बारे में अच्छी तरह से जानता हूं कि किसी को "शुद्ध" होने के दौरान एक समस्या को हल करने से मिलता है, तो निश्चित रूप से मैं केवल एक ही नहीं हूं जो सोचता है कि घटनाओं, आदेशों और व्यवहारों की 200 लाइनें सिर्फ एक लाइन पद्धति से बचने के लिए "पैटर्न" और "पवित्रता" का नाम थोड़ा हास्यास्पद है ...।


2
मैंने स्वीकार किए गए उत्तर को अस्वीकार नहीं किया, लेकिन मैं अनुमान लगाता हूं कि गिरावट का कारण यह है कि यह सामान्य रूप से सहायक नहीं है, भले ही यह एक मामले में काम करे। आपने इसे स्वयं एक अन्य टिप्पणी में कहा: "जबकि लॉगिन फ़ॉर्म 'दो फ़ील्ड्स' संवाद है, मेरे पास कई अन्य हैं जो बहुत अधिक जटिल हैं (और इसलिए वारंट एमवीवीएम), लेकिन फिर भी इसे बंद करने की आवश्यकता है ..."
जो व्हाइट

1
मैं आपकी बात देख रहा हूं, लेकिन मैं व्यक्तिगत रूप से सोचता हूं कि सामान्य मामले के लिए एक सरल Closeतरीका अभी भी सबसे अच्छा समाधान है। अन्य अधिक जटिल संवादों पर सब कुछ MVVM और डेटाबाउंड है, लेकिन यह सिर्फ एक सरल विधि के बजाय विशाल "समाधान" को लागू करने के लिए मूर्खतापूर्ण लग रहा था ...
ओरियन एडवर्ड्स

2
आप संवाद परिणाम asimsajjad.blogspot.com/2010/10/… के लिए निम्नलिखित लिंक की जांच कर सकते हैं , जो संवाद को फिर से लौटाएगा और दृश्य से दृश्य बंद कर देगा
आसिम सज्जाद

3
कृपया इस प्रश्न के स्वीकृत उत्तर को बदल दें। इस कार्यक्षमता के लिए MVVM के उपयोग पर सवाल उठाने वाले लोगों की तुलना में काफी अच्छे समाधान हैं। वह उत्तर नहीं है, वह परिहार है।
स्कॉटचेयर

2
@ ओरियन मुझे लगता है कि आप यहाँ पैटर्न को तोड़ने के लिए सही थे। एक डिज़ाइन पैटर्न का मुख्य उद्देश्य विकास चक्रों को गति देना, स्थिरता में वृद्धि करना और समान नियमों का पालन करते हुए पूरी टीम बनाकर अपने कोड को सरल बनाना है। यह बाहरी लाइब्रेरी पर निर्भरता को जोड़कर और एक कार्य को पूरा करने के लिए सैकड़ों कोड लाइनों को लागू करने से पूरी तरह से अनदेखा नहीं किया जाता है, यह देखते हुए कि बहुत अधिक सरल समाधान है, सिर्फ इसलिए कि कोई पैटर्न की "शुद्धता" का त्याग करने के लिए जिद्दी है। बस दस्तावेज़ क्या किया your've को सुनिश्चित करने और चुंबन अपने कोड ( कश्मीर eep मैं टी एस hort और रों imple)।
M463

जवाबों:


324

मैं एक सरल संलग्न संपत्ति लिखने के लिए थेजुआन के उत्तर से प्रेरित था । कोई शैली नहीं, कोई ट्रिगर नहीं; इसके बजाय, आप ऐसा कर सकते हैं:

<Window ...
        xmlns:xc="clr-namespace:ExCastle.Wpf"
        xc:DialogCloser.DialogResult="{Binding DialogResult}">

यह लगभग उतना ही साफ है जितना कि WPF की टीम ने इसे सही तरीके से प्राप्त किया था और पहले स्थान पर DialogResult को एक निर्भरता संपत्ति बनाया था। बस bool? DialogResultअपने ViewModel पर एक संपत्ति डालें और INotifyPropertyChanged, और voilà को लागू करें, आपका ViewModel केवल एक संपत्ति सेट करके विंडो को बंद कर सकता है (और इसके DialogResult को सेट कर सकता है)। MVVM जैसा होना चाहिए।

यहाँ DialogCloser के लिए कोड है:

using System.Windows;

namespace ExCastle.Wpf
{
    public static class DialogCloser
    {
        public static readonly DependencyProperty DialogResultProperty =
            DependencyProperty.RegisterAttached(
                "DialogResult",
                typeof(bool?),
                typeof(DialogCloser),
                new PropertyMetadata(DialogResultChanged));

        private static void DialogResultChanged(
            DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            var window = d as Window;
            if (window != null)
                window.DialogResult = e.NewValue as bool?;
        }
        public static void SetDialogResult(Window target, bool? value)
        {
            target.SetValue(DialogResultProperty, value);
        }
    }
}

मैंने इसे अपने ब्लॉग पर भी पोस्ट किया है ।


3
यह एक जवाब है जो मुझे सबसे अधिक पसंद है! अच्छी नौकरी लेखन जो संलग्न संपत्ति।
जॉर्ज वर्गास

2
अच्छा विकल्प है, लेकिन इस समाधान में एक सूक्ष्म बग है। यदि संवाद के लिए दृश्य मॉडल एक सिंगलटन है, तो संवाद के अगले उपयोग के लिए DialogResult मान को आगे बढ़ाया जाता है। इसका मतलब है कि यह तुरंत ही रद्द हो जाएगा या खुद को दिखाने से पहले स्वीकार कर लेगा ताकि संवाद दूसरी बार दिखाई न दे।
कोडिंग

13
@ हाईटेक मैजिक, लगता है कि बग पहले स्थान पर एक सिंगलटन व्यूमॉडल का उपयोग कर रहा है। (मुस्कराहट) गंभीरता से, आप पृथ्वी पर एक एकल दृश्यमॉडल क्यों चाहते हैं? वैश्विक चरों में परस्पर स्थिति बनाए रखना एक बुरा विचार है। एक दुःस्वप्न का परीक्षण करने में मदद करता है, और उन कारणों में से एक है जो आप पहली बार में MVVM का उपयोग करेंगे।
जो व्हाइट

3
क्या MVVM की बात किसी विशिष्ट यूआई के लिए आपके तर्क को कसकर जोड़े नहीं है? इस मामले में, बूल? WinForm जैसे किसी अन्य UI द्वारा निश्चित रूप से प्रयोग करने योग्य नहीं है, और WPF के लिए DialogCloser विशिष्ट है। तो यह एक समाधान के रूप में कैसे फिट बैठता है? इसके अलावा, सिर्फ बाइंडिंग के माध्यम से विंडो बंद करने के लिए 2x-10x कोड क्यों लिखें?
डेविड एंडरसन

2
@DavidAnderson, मैं किसी भी मामले में WinForms के साथ MVVM की कोशिश नहीं करूंगा; इसका डेटाबाइंडिंग समर्थन बहुत कमजोर है, और MVVM एक सुविचारित बाध्यकारी प्रणाली पर निर्भर करता है। और यह 2x-10x कोड के पास कहीं नहीं है। आप उस कोड को एक बार लिखें , हर विंडो के लिए एक बार नहीं। उसके बाद यह एक-पंक्ति बाइंडिंग प्लस एक सूचित संपत्ति है, उसी तंत्र का उपयोग करके जो आप पहले से ही अपने दृश्य में सब कुछ के लिए उपयोग कर रहे हैं (इसलिए, उदाहरण के लिए, आपको बस बंद करने के लिए अतिरिक्त दृश्य इंटरफ़ेस इंजेक्ट करने की आवश्यकता नहीं है खिड़की)। अन्य ट्रेडऑफ़ बनाने के लिए आपका स्वागत है, लेकिन यह मेरे लिए आम तौर पर अच्छा सौदा है।
जो व्हाइट

64

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

प्रत्येक ViewModel वर्ग को उस प्रकार WorkspaceViewModelकी RequestCloseघटना और CloseCommandसंपत्ति से वारिस होना चाहिए ICommandCloseCommandसंपत्ति का डिफ़ॉल्ट कार्यान्वयन RequestCloseघटना को बढ़ाएगा ।

खिड़की बंद करने के लिए, OnLoadedआपकी खिड़की की विधि को ओवरराइड किया जाना चाहिए:

void CustomerWindow_Loaded(object sender, RoutedEventArgs e)
{
    CustomerViewModel customer = CustomerViewModel.GetYourCustomer();
    DataContext = customer;
    customer.RequestClose += () => { Close(); };
}

या OnStartupआप अनुप्रयोग की विधि:

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        MainWindow window = new MainWindow();
        var viewModel = new MainWindowViewModel();
        viewModel.RequestClose += window.Close;
        window.DataContext = viewModel;

        window.Show();
    }

मुझे लगता है कि RequestCloseघटना और CloseCommandसंपत्ति कार्यान्वयन WorkspaceViewModelबहुत स्पष्ट हैं, लेकिन मैं उन्हें लगातार दिखाऊंगा:

public abstract class WorkspaceViewModel : ViewModelBase
// There's nothing interesting in ViewModelBase as it only implements the INotifyPropertyChanged interface
{
    RelayCommand _closeCommand;
    public ICommand CloseCommand
    {
        get
        {
            if (_closeCommand == null)
            {
                _closeCommand = new RelayCommand(
                   param => Close(),
                   param => CanClose()
                   );
            }
            return _closeCommand;
        }
    }

    public event Action RequestClose;

    public virtual void Close()
    {
        if ( RequestClose != null )
        {
            RequestClose();
        }
    }

    public virtual bool CanClose()
    {
        return true;
    }
}

और के स्रोत कोड RelayCommand:

public class RelayCommand : ICommand
{
    #region Constructors

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    #endregion // ICommand Members

    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields
}

PS मुझे उन स्रोतों के लिए बुरी तरह से व्यवहार न करें! अगर मैं उनके पास कल होता तो मुझे कुछ घंटे बच जाते ...

PPS किसी भी टिप्पणी या सुझाव का स्वागत है।


2
उम्म, जिस तथ्य को आपने customer.RequestCloseअपने XAML फ़ाइल के पीछे कोड हैंड ईवेंट हैंडलर पर झुकाया है, क्या वह MVVI पैटर्न का उल्लंघन नहीं करता है? आप पहले से ही Clickअपने बंद बटन पर इवेंट हैंडलर को बाँध सकते हैं , यह देखते हुए कि आपने कोड को किसी भी तरह से स्पर्श किया है और किया है this.Close()! सही?
गोनैले

1
मुझे ईवेंट दृष्टिकोण के साथ बहुत अधिक समस्याएं नहीं हैं, लेकिन मुझे RequestClose शब्द पसंद नहीं है, क्योंकि मेरे लिए यह अभी भी व्यू के कार्यान्वयन के बारे में बहुत अधिक ज्ञान से संबंधित है। मैं IsCancelled जैसे गुणों को उजागर करना पसंद करता हूं जो संदर्भ को देखते हुए और अधिक सार्थक होते हैं और प्रतिक्रिया में देखने के लिए जो माना जाता है वह कम होता है।
जिपरसन

18

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

http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/


यह अब तक का एकमात्र उत्तर है, जिसके लिए विंडो में किसी भी कोडबेहैंड की आवश्यकता नहीं है (और वास्तव में एक अन्य दृष्टिकोण का सुझाव देने के बजाय एक मोडल विंडो को बंद कर देता है)। अफ़सोस कि इसके लिए स्टाइल और ट्रिगर के साथ इतनी जटिलता की आवश्यकता होती है, और यह सब टकराता है - ऐसा लगता है कि यह वास्तव में एक-लाइन से जुड़े व्यवहार के साथ करने योग्य होना चाहिए।
जो व्हाइट

4
अब यह एक-लाइन संलग्न व्यवहार के साथ उल्लेखनीय है। मेरा जवाब देखें: stackoverflow.com/questions/501886/…
जो व्हाइट

15

यहाँ MVVM के पेशेवरों और विपक्षों पर बहस करते हुए बहुत सारी टिप्पणियां हैं। मेरे लिए, मैं नीर से सहमत हूं; यह उचित रूप से पैटर्न का उपयोग करने की बात है और एमवीवीएम हमेशा फिट नहीं होता है। लोगों को लगता है कि एमवीवीएम फिट करने के लिए इसे प्राप्त करने के लिए सॉफ्टवेयर डिजाइन के सबसे महत्वपूर्ण सिद्धांतों का बलिदान करने के लिए तैयार हो गए हैं।

उस ने कहा, .. मुझे लगता है कि आपका मामला थोड़ा सा रिफैक्टिंग के साथ एक अच्छा फिट हो सकता है।

ज्यादातर मामलों में मैं भर में आया हूँ, WPF आपको कई Windowएस के बिना प्राप्त करने में सक्षम बनाता है । हो सकता है कि आप विंडोज के बजाय एस के साथ एस Frameऔर Pageएस का उपयोग करने की कोशिश कर सकते हैं DialogResult

आपके मामले में मेरे सुझाव को LoginFormViewModelसंभालना होगा LoginCommandऔर यदि लॉगिन अमान्य है, तो एक संपत्ति को LoginFormViewModelउचित मूल्य ( falseया कुछ एनम मान UserAuthenticationStates.FailedAuthentication) पर सेट करें। आप एक सफल लॉगिन ( trueया कुछ अन्य एनम वैल्यू) के लिए भी ऐसा ही करेंगे । फिर आप DataTriggerविभिन्न उपयोगकर्ता प्रमाणीकरण राज्यों के लिए एक उत्तर का उपयोग करेंगे और Setterकी Sourceसंपत्ति को बदलने के लिए एक सरल का उपयोग कर सकते हैं Frame

आपकी लॉगिन विंडो वापस होने से DialogResultमुझे लगता है कि आप भ्रमित हो रहे हैं; कि DialogResultवास्तव में अपने ViewModel की संपत्ति है। WPF के साथ मेरे, सीमित रूप से सीमित अनुभव, जब कुछ सही नहीं लगता है क्योंकि आमतौर पर मैं सोच रहा होता हूं कि मैंने WinForms में एक ही काम कैसे किया होगा।

उम्मीद है की वो मदद करदे।


10

मान लें कि आपका लॉगिन डायलॉग पहली विंडो है जो बनाई गई है, इसे अपने LoginViewModel वर्ग के अंदर आज़माएँ:

    void OnLoginResponse(bool loginSucceded)
    {
        if (loginSucceded)
        {
            Window1 window = new Window1() { DataContext = new MainWindowViewModel() };
            window.Show();

            App.Current.MainWindow.Close();
            App.Current.MainWindow = window;
        }
        else
        {
            LoginError = true;
        }
    }

पुरुष यह सरल है और महान काम करता है। वर्तमान में मैं इस दृष्टिकोण का उपयोग कर रहा हूं।
इररे एफे

यह केवल MAIN विंडो के लिए काम करता है। इसलिए किसी अन्य विंडो के लिए इसका उपयोग न करें।
ओलेक्सी

7

यह एक सरल और साफ समाधान है - आप एक घटना को ViewModel में जोड़ते हैं और उस घटना को निकाल दिए जाने पर विंडो को स्वयं बंद करने का निर्देश देते हैं।

अधिक जानकारी के लिए मेरी ब्लॉग पोस्ट देखें, ViewModel से विंडो बंद करें

XAML:

<Window
  x:Name="this"
  xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"  
  xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions">
  <i:Interaction.Triggers>
    <i:EventTrigger SourceObject="{Binding}" EventName="Closed">
      <ei:CallMethodAction
        TargetObject="{Binding ElementName=this}"
        MethodName="Close"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
<Window>

ViewModel:

private ICommand _SaveAndCloseCommand;
public ICommand SaveAndCloseCommand
{
  get
  {
    return _SaveAndCloseCommand ??
      (_SaveAndCloseCommand = new DelegateCommand(SaveAndClose));
  }
}
private void SaveAndClose()
{
  Save();
  Close();
}

public event EventHandler Closed;
private void Close()
{
  if (Closed != null) Closed(this, EventArgs.Empty);
}

नोट: उदाहरण प्रिज्म DelegateCommand( प्रिज्म: कमांडिंग देखें ) का उपयोग करता है , लेकिन किसी भी ICommandकार्यान्वयन का उपयोग उस मामले के लिए किया जा सकता है।

आप इस आधिकारिक पैकेज से व्यवहार का उपयोग कर सकते हैं ।


2
+1 लेकिन आपको स्वयं ही उत्तर में अधिक विवरण प्रदान करना चाहिए, उदाहरण के लिए कि इस समाधान के लिए अभिव्यक्ति ब्लेंड इंटरएक्टिविटी असेंबली के संदर्भ की आवश्यकता है।
सर्फ करें

6

जिस तरह से मैं इसे संभालूंगा वह मेरे ViewModel में एक इवेंट हैंडलर जोड़ना है। जब उपयोगकर्ता सफलतापूर्वक लॉग इन हो जाता है तो मैं इस घटना को आग लगा दूंगा। मेरे विचार में मैं इस घटना से जुड़ूंगा और जब यह निकाल दिया जाएगा तो मैं खिड़की बंद कर दूंगा।


2
यही मैं आमतौर पर भी करता हूं। हालांकि यह थोड़ा गंदा लगता है कि सभी newfangled wpf- कमांडिंग सामान।
बॉटज़ ३०००

4

यहां मैंने वही किया है, जो काम करता है, हालांकि यह लंबे समय से घुमावदार और बदसूरत लगता है (वैश्विक स्थैतिक कुछ भी अच्छा नहीं है)

1: App.xaml.cs

public partial class App : Application
{
    // create a new global custom WPF Command
    public static readonly RoutedUICommand LoggedIn = new RoutedUICommand();
}

2: LoginForm.xaml

// bind the global command to a local eventhandler
<CommandBinding Command="client:App.LoggedIn" Executed="OnLoggedIn" />

3: LoginForm.xaml.cs

// implement the local eventhandler in codebehind
private void OnLoggedIn( object sender, ExecutedRoutedEventArgs e )
{
    DialogResult = true;
    Close();
}

4: LoginFormViewModel.cs

// fire the global command from the viewmodel
private void OnRemoteServerReturnedSuccess()
{
    App.LoggedIn.Execute(this, null);
}

मैंने बाद में इस सारे कोड को हटा दिया, और इसके पास केवल LoginFormViewModelकॉल विधि थी, जिस पर यह दृश्य था। यह समाप्त हो गया बहुत अच्छा है और पालन करने में आसान है। IMHO पैटर्न का मतलब लोगों को यह समझने का एक आसान तरीका देना है कि आपका ऐप क्या कर रहा है, और इस मामले में, MVVM इसे समझने की तुलना में बहुत कठिन बना रहा था अगर मैंने इसका उपयोग नहीं किया था, और अब एक विरोधी-विरोधी था।


3

FYI करें, मैं इसी समस्या में भाग गया और मुझे लगता है कि मुझे एक काम के बारे में पता चला है जिसमें ग्लोबल्स या स्टैटिक्स की आवश्यकता नहीं है, हालांकि यह सबसे अच्छा जवाब नहीं हो सकता है। मैं आप लोगों को यह तय करने देता हूं कि अपने लिए।

मेरे मामले में, ViewModel जो विंडो को प्रदर्शित करने के लिए तुरंत करता है (इसे ViewModelMain कहता है) LoginFormViewModel (उदाहरण के रूप में ऊपर की स्थिति का उपयोग करके) के बारे में भी जानता है।

तो मैंने जो किया वह लॉगिनफ्रेम व्यू मोड पर एक संपत्ति बनाने के लिए किया गया था जो कि आईसीओमैंड का था (आइए इसे क्लोजविंडकोमांड कहते हैं)। फिर, मैं कॉल करने से पहले .ShowDialog () विंडो पर, मैं CloseWindowCommand प्रॉपर्टी को विंडो पर LoginFormViewModel पर सेट करता हूं। विंडो के विंडो को बंद करें () विधि को मैंने तुरंत किया। फिर LoginFormViewModel के अंदर मुझे बस इतना करना है कि विंडो को बंद करने के लिए CloseWindowCommand.Execute () कॉल करें।

यह थोड़ा सा वर्कअराउंड / हैक मैं मान लेता हूं, लेकिन यह वास्तव में MVVM पैटर्न को तोड़ने के बिना अच्छी तरह से काम करता है।

बेझिझक इस प्रक्रिया को जितना आप पसंद करते हैं, मैं इसे ले सकता हूं! :)


मुझे यकीन नहीं है कि मैं इसे पूरी तरह से कर दूंगा, लेकिन इसका मतलब यह नहीं है कि आपके MainWindow को आपके LoginWindow से पहले तुरंत चालू करना होगा? यदि संभव हो तो कुछ ऐसा है जिससे मैं बचना चाहूंगा
ओरियन एडवर्ड्स

3

यह शायद बहुत देर हो चुकी है, लेकिन मैं एक ही समस्या में आया था और मुझे एक समाधान मिला जो मेरे लिए काम करता है।

मैं यह पता नहीं लगा सकता कि संवाद के बिना ऐप कैसे बनाया जाए (हो सकता है कि यह सिर्फ माइंड ब्लॉक हो)। इसलिए मैं MVVM के साथ एक गतिरोध पर था और एक संवाद दिखा रहा था। इसलिए मैं इस CodeProject लेख में आया:

http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

जो एक UserControl है जो मूल रूप से एक विंडो को दूसरी विंडो के विज़ुअल ट्री (xaml में अनुमति नहीं है) के भीतर होने की अनुमति देता है। यह एक बुलियन डिपेंडेंसीप्रोस्पेर्टी को भी उजागर करता है जिसे इस्शोइंग कहा जाता है।

आप एक शैली सेट कर सकते हैं, जैसे कि आमतौर पर एक resourcedfox में, जो मूल रूप से संवाद प्रदर्शित करता है जब भी नियंत्रण की सामग्री संपत्ति! ट्रिगर के माध्यम से शून्य:

<Style TargetType="{x:Type d:Dialog}">
    <Style.Triggers>
        <Trigger Property="HasContent"  Value="True">
            <Setter Property="Showing" Value="True" />
        </Trigger>
    </Style.Triggers>
</Style>

दृश्य में, जहाँ आप संवाद दिखाना चाहते हैं, बस यह है:

<d:Dialog Content="{Binding Path=DialogViewModel}"/>

और अपने ViewModel में आपको बस इतना करना है कि संपत्ति को एक मूल्य पर सेट करें (ध्यान दें: ViewModel वर्ग को कुछ होने के लिए देखने के लिए INotifyPropertyChanged का समर्थन करना चाहिए)।

इस तरह:

DialogViewModel = new DisplayViewModel();

ViewModel को व्यू के साथ मिलाने के लिए आपके पास resourcedEDIA में ऐसा कुछ होना चाहिए:

<DataTemplate DataType="{x:Type vm:DisplayViewModel}">
    <vw:DisplayView/>
</DataTemplate>

इन सबके साथ आपको डायलॉग दिखाने के लिए वन-लाइनर कोड मिलता है। आपको जो समस्या है वह यह है कि आप वास्तव में सिर्फ उपरोक्त कोड के साथ संवाद को बंद नहीं कर सकते। तो इसीलिए आपको एक ViewModel बेस क्लास में एक इवेंट में रखना होगा, जो DisplayViewModel को ऊपर दिए गए कोड के बजाय विरासत में मिला है, इसे लिखें

        var vm = new DisplayViewModel();
        vm.RequestClose += new RequestCloseHandler(DisplayViewModel_RequestClose);
        DialogViewModel = vm;

तब आप कॉलबैक के माध्यम से संवाद का परिणाम संभाल सकते हैं।

यह थोड़ा जटिल लग सकता है, लेकिन एक बार जब इसकी नींव रखी जाती है, तो यह बहुत सीधा होता है। फिर से यह मेरा कार्यान्वयन है, मुझे यकीन है कि अन्य लोग भी हैं :)

आशा है कि यह मदद करता है, यह मुझे बचाया।


3

ठीक है, इसलिए यह प्रश्न लगभग 6 साल पुराना है और मैं अभी भी यहां नहीं पा रहा हूं कि मुझे क्या लगता है कि यह उचित उत्तर है, इसलिए मुझे अपने "2 सेंट" साझा करने की अनुमति दें ...

मेरे पास वास्तव में इसे करने के 2 तरीके हैं, पहला एक सरल है ... दूसरा दूसरा दाईं ओर है, इसलिए यदि आप दाईं ओर देख रहे हैं, तो # 1 छोड़ें और # 2 पर जाएं :

1. त्वरित और आसान (लेकिन पूरा नहीं)

अगर मेरे पास सिर्फ एक छोटा प्रोजेक्ट है तो मैं कभी-कभी ViewModel में एक CloseWindowAction बनाता हूं :

        public Action CloseWindow { get; set; } // In MyViewModel.cs

और जो कोई भी दृश्य को क्रेट करता है, या उसके पीछे दृश्य कोड में मैं सिर्फ वह विधि सेट करता हूं जो क्रिया कॉल करेगी:

(याद रखें एमवीवीएम व्यू और व्यूमॉडल के पृथक्करण के बारे में है ... व्यू कोड कोड अभी भी व्यू है और जब तक उचित पृथक्करण नहीं होता है तब तक आप पैटर्न का उल्लंघन नहीं कर रहे हैं)

यदि कुछ ViewModel एक नई विंडो बनाता है:

private void CreateNewView()
{
    MyView window = new MyView();
    window.DataContext = new MyViewModel
                             {
                                 CloseWindow = window.Close,
                             }; 
    window.ShowDialog();
}

या यदि आप इसे अपने मुख्य विंडो में चाहते हैं, तो बस इसे अपने व्यू के निर्माता के नीचे रखें:

public MyView()
{
    InitializeComponent();           
    this.DataContext = new MainViewModel
                           {
                                CloseWindow = this.Close
                           };
}

जब आप विंडो बंद करना चाहते हैं, तो अपने ViewModel पर कार्रवाई को कॉल करें।


2. सही तरीका

अब इसे करने का उचित तरीका प्रिज्म (IMHO) का उपयोग कर रहा है , और इसके बारे में सभी यहां पाया जा सकता है

आप एक इंटरैक्शन अनुरोध कर सकते हैं , इसे अपनी नई विंडो में आपको जो भी डेटा की आवश्यकता होगी, उसे दोपहर का भोजन, इसे बंद करें और यहां तक ​​कि डेटा वापस प्राप्त करें । इस सभी को ध्वस्त कर दिया गया और एमवीवीएम ने मंजूरी दे दी। यहां तक ​​कि आपको यह भी पता चलता है कि विंडो कैसे बंद की गई थी , जैसे कि यूजर Canceledया Accepted(ओके बटन) विंडो और डेटा को जरूरत पड़ने पर वापस । यह थोड़ा और अधिक जटिल है और उत्तर # 1 है, लेकिन यह बहुत अधिक पूर्ण है, और Microsoft द्वारा अनुशंसित पैटर्न है।

मेरे द्वारा दिए गए लिंक में सभी कोड स्निपेट और उदाहरण हैं, इसलिए मैं यहां किसी भी कोड को रखने की जहमत नहीं उठाऊंगा, बस प्रिज्म क्विक स्टार्ट डाउनलोड करने और इसे चलाने के लेख को पढ़ें, यह वास्तव में सरल है बस थोड़ा और अधिक समझने के लिए यह काम करते हैं, लेकिन सिर्फ एक खिड़की बंद करने से लाभ बड़े हैं।


अच्छा तरीका है, लेकिन ViewModels का रिज़ॉल्यूशन और असाइनमेंट हमेशा सीधे आगे नहीं हो सकता है। क्या होगा अगर एक ही viewmodel कई विंडोज का DataContext है?
किलो रेन

तब मुझे लगता है कि आपको एक साथ सभी विंडो बंद करनी +=होंगी , याद रखें एक एक्शन एक बार में कई प्रतिनिधियों को ट्रिगर कर सकता है, बस एक प्रतिनिधि जोड़ने के लिए उपयोग करें, और एक्शन को कॉल करें, यह उन सभी को आग लगा देगा .... या आप करेंगे अपने वीएम पर एक विशेष तर्क बनाना होगा ताकि यह पता चले कि किस विंडो को बंद करना है (हो सकता है कि क्लोज एक्ट्स का संग्रह हो) .... लेकिन मुझे लगता है कि एक वीएम से जुड़े कई विचार होना सर्वोत्तम अभ्यास नहीं है, यह एक दृश्य और एक वीएम उदाहरण को प्रबंधित करने के लिए बेहतर है, एक दूसरे के लिए बाध्य और शायद एक माता-पिता वीएम जो सभी बच्चों को वीएम का प्रबंधन करता है जो सभी दृश्यों के लिए बाध्य हैं।
mFeinstein

3
public partial class MyWindow: Window
{
    public ApplicationSelection()
    {
      InitializeComponent();

      MyViewModel viewModel = new MyViewModel();

      DataContext = viewModel;

      viewModel.RequestClose += () => { Close(); };

    }
}

public class MyViewModel
{

  //...Your code...

  public event Action RequestClose;

  public virtual void Close()
  {
    if (RequestClose != null)
    {
      RequestClose();
    }
  }

  public void SomeFunction()
  {
     //...Do something...
     Close();
  }
}

2

आपके पास ViewModel उस घटना को उजागर कर सकता है, जिसे View रजिस्टर करता है। फिर, जब ViewModel दृश्य को बंद करने के लिए अपना समय तय करता है, तो यह उस घटना को आग लगा देता है जिससे दृश्य बंद हो जाता है। यदि आप एक विशिष्ट परिणाम मान वापस चाहते हैं, तो उसके लिए आपके पास ViewModel में एक संपत्ति होगी।


मैं इससे सहमत हूं - सादगी मूल्यवान है। मुझे इस बारे में सोचना होगा कि जब अगले जूनियर डेवलपर को इस प्रोजेक्ट को संभालने के लिए काम पर रखा जाता है तो क्या होगा। मेरा अनुमान है कि आपके पास इस अधिकार को प्राप्त करने के लिए एक बेहतर अवसर होगा, जैसा कि आप वर्णन करते हैं। जब तक आपको नहीं लगता कि आप इस कोड को हमेशा के लिए बनाए रखने जा रहे हैं? +1
डीन

2

बस भारी संख्या में उत्तरों को जोड़ने के लिए, मैं निम्नलिखित जोड़ना चाहता हूं। यह मानते हुए कि आपके पास अपने ViewModel पर एक ICommand है, और आप चाहते हैं कि कमांड इसकी खिड़की (या उस मामले के लिए कोई अन्य कार्रवाई) को बंद कर दे, आप निम्न की तरह कुछ का उपयोग कर सकते हैं।

var windows = Application.Current.Windows;
for (var i=0;i< windows.Count;i++ )
    if (windows[i].DataContext == this)
        windows[i].Close();

यह सही नहीं है, और परीक्षण करना मुश्किल हो सकता है (क्योंकि स्थैतिक को स्थिर करना / रोकना मुश्किल है) लेकिन यह अन्य समाधानों की तुलना में क्लीनर (IMHO) है।

एरिक


आपका सरल उत्तर देखकर मैं बहुत खुश हुआ! लेकिन यह भी काम नहीं करता है! मुझे विजुअल बेसिक के साथ खोलने और बंद करने की आवश्यकता है। क्या आपको VB में (विंडोज़ [i] .DataContext == यह) की समानता पता है?
एहसान

मैं अंत में मिल गया! :) धन्यवाद। अगर खिड़कियां (i) .DataContext मुझे है
एहसान

क्या आप भी खिड़की खोलने का वही सरल तरीका जानते हैं? मुझे चाइल्ड व्यूमॉडल और इसके विपरीत कुछ डेटा भेजने और प्राप्त करने की आवश्यकता है।
एहसान

1

मैंने जो व्हाइट के समाधान को लागू किया, लेकिन कभी-कभार समस्याओं के साथ भाग गया " डायलॉगResult को विंडो बनाए जाने और डायलॉग " त्रुटियों के रूप में दिखाए जाने के बाद ही सेट किया जा सकता है

मैं व्यू बंद होने के बाद ViewModel को चारों ओर रख रहा था और कभी-कभी मैंने बाद में उसी VM का उपयोग करके एक नया व्यू खोला। ऐसा प्रतीत होता है कि पुराने दृश्य को एकत्र करने से पहले नए दृश्य को बंद करने से कचरा एकत्रित हो गया था, जिसके परिणामस्वरूप डायलॉग ResultChanged बंद विंडो पर DialogResult गुण सेट करने का प्रयास कर रहा था , इस प्रकार त्रुटि को भड़का रहा था।

मेरा समाधान DialogResultChanged को बदलना था कि खिड़की की संपत्ति को जांचने के लिए जाए :

private static void DialogResultChanged(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e)
{
    var window = d as Window;
    if (window != null && window.IsLoaded)
        window.DialogResult = e.NewValue as bool?;
}

इस परिवर्तन को करने के बाद बंद संवादों के लिए किसी भी अनुलग्नक को अनदेखा कर दिया जाता है।


धन्यवाद महोदय। मेरे पास एक ही समस्या थी
डीजे बर्ब

1

मैंने जो व्हाइट के उत्तर और एडम मिल्स के उत्तर के कुछ कोड को सम्मिश्रित किया , क्योंकि मुझे प्रोग्रामेटिक रूप से बनाई गई विंडो में उपयोगकर्ता नियंत्रण दिखाने की आवश्यकता थी। इसलिए DialogCloser को विंडो पर नहीं होना चाहिए, यह उपयोगकर्ता के नियंत्रण पर ही हो सकता है

<UserControl ...
    xmlns:xw="clr-namespace:Wpf"
    xw:DialogCloser.DialogResult="{Binding DialogResult}">

और DialogCloser उपयोगकर्ता नियंत्रण की खिड़की को ढूंढ लेगा यदि यह खिड़की से जुड़ा नहीं था।

namespace Wpf
{
  public static class DialogCloser
  {
    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached(
            "DialogResult",
            typeof(bool?),
            typeof(DialogCloser),
            new PropertyMetadata(DialogResultChanged));

    private static void DialogResultChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
      var window = d.GetWindow();
      if (window != null)
        window.DialogResult = e.NewValue as bool?;
    }

    public static void SetDialogResult(DependencyObject target, bool? value)
    {
      target.SetValue(DialogResultProperty, value);
    }
  }

  public static class Extensions
  {
    public static Window GetWindow(this DependencyObject sender_)
    {
      Window window = sender_ as Window;        
      return window ?? Window.GetWindow( sender_ );
    }
  }
}

1

व्यवहार यहाँ सबसे सुविधाजनक तरीका है।

  • एक हाथ से, इसे दिए गए व्यूमॉडल से बाँधा जा सकता है (जो कि "फॉर्म को बंद कर सकता है!"

  • दूसरे हाथ से, यह स्वयं फॉर्म तक पहुंच रखता है ताकि आवश्यक फॉर्म-विशिष्ट घटनाओं की सदस्यता ले सके, या पुष्टि संवाद, या कुछ और दिखा सके।

आवश्यक व्यवहार लिखना पहली बार उबाऊ देखा जा सकता है। हालाँकि, अब से, आप इसे एक-लाइनर XAML स्निपेट द्वारा आवश्यक हर एक रूप पर पुनः उपयोग कर सकते हैं। और यदि आवश्यक हो, तो आप इसे एक अलग विधानसभा के रूप में निकाल सकते हैं ताकि इसे किसी भी अगले प्रोजेक्ट में शामिल किया जा सके जो आप चाहते हैं।


0

सिर्फ कमांड पैरामीटर के रूप में विंडो पास क्यों नहीं?

सी#:

 private void Cancel( Window window )
  {
     window.Close();
  }

  private ICommand _cancelCommand;
  public ICommand CancelCommand
  {
     get
     {
        return _cancelCommand ?? ( _cancelCommand = new Command.RelayCommand<Window>(
                                                      ( window ) => Cancel( window ),
                                                      ( window ) => ( true ) ) );
     }
  }

XAML:

<Window x:Class="WPFRunApp.MainWindow"
        x:Name="_runWindow"
...
   <Button Content="Cancel"
           Command="{Binding Path=CancelCommand}"
           CommandParameter="{Binding ElementName=_runWindow}" />

मुझे नहीं लगता कि VM को विंडो प्रकार पर प्रतिबंधित करना एक अच्छा विचार है।
बजे शमी वेइटहैंडलर

2
मुझे नहीं लगता कि वीएम को एक Windowप्रकार से प्रतिबंधित करना एक अच्छा विचार है जो कुछ हद तक "शुद्ध" MVVM नहीं है। यह उत्तर देखें , जहां VM किसी Windowवस्तु तक सीमित नहीं है ।
शिम्मी वेइटहैंडलर

इस तरह से निर्भरता एक बटन पर रखी जा रही है जो निश्चित रूप से हमेशा की स्थिति नहीं हो सकती है। इसके अलावा UIM को ViewModel से गुजारना एक बुरा अभ्यास है।
Kylo Ren

0

एक अन्य उपाय यह है कि इन्टॉलिज़प्रोपरेटी चेंज के साथ प्रॉपर्टी बनाएं, जिसमें डायलॉग की तरह मॉडल देखें, और फिर इसके पीछे कोड में:

public class SomeWindow: ChildWindow
{
    private SomeViewModel _someViewModel;

    public SomeWindow()
    {
        InitializeComponent();

        this.Loaded += SomeWindow_Loaded;
        this.Closed += SomeWindow_Closed;
    }

    void SomeWindow_Loaded(object sender, RoutedEventArgs e)
    {
        _someViewModel = this.DataContext as SomeViewModel;
        _someViewModel.PropertyChanged += _someViewModel_PropertyChanged;
    }

    void SomeWindow_Closed(object sender, System.EventArgs e)
    {
        _someViewModel.PropertyChanged -= _someViewModel_PropertyChanged;
        this.Loaded -= SomeWindow_Loaded;
        this.Closed -= SomeWindow_Closed;
    }

    void _someViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == SomeViewModel.DialogResultPropertyName)
        {
            this.DialogResult = _someViewModel.DialogResult;
        }
    }
}

सबसे महत्वपूर्ण टुकड़ा है _someViewModel_PropertyChangedDialogResultPropertyNameमें कुछ सार्वजनिक बाधा हो सकती हैSomeViewModel

जब मैं ViewModel में यह करना कठिन होता है, तो मैं व्यू कंट्रोल में कुछ बदलाव करने के लिए इस तरह की ट्रिक का उपयोग करता हूं। ViewModel में OnPropertyChanged आप व्यू में कुछ भी कर सकते हैं। ViewModel अभी भी 'यूनिट टेस्टेबल' है और कोड में कोड की कुछ छोटी लाइनों से कोई फर्क नहीं पड़ता है।


0

मैं इस तरह से जाना होगा:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;    
using GalaSoft.MvvmLight.Messaging; 

// View

public partial class TestCloseWindow : Window
{
    public TestCloseWindow() {
        InitializeComponent();
        Messenger.Default.Register<CloseWindowMsg>(this, (msg) => Close());
    }
}

// View Model

public class MainViewModel: ViewModelBase
{
    ICommand _closeChildWindowCommand;

    public ICommand CloseChildWindowCommand {
        get {
            return _closeChildWindowCommand?? (_closeChildWindowCommand = new RelayCommand(() => {
                Messenger.Default.Send(new CloseWindowMsg());
        }));
        }
    }
}

public class CloseWindowMsg
{
}

0

मैंने सभी उत्तर पढ़े हैं, लेकिन मुझे कहना होगा कि उनमें से अधिकांश अभी भी पर्याप्त नहीं हैं या इससे भी बदतर हैं।

आप डायलॉग सेवा सेवा के साथ इस सुंदर तरीके से संभाल सकते हैं कि डायलॉग विंडो और रिटर्न डायलॉग परिणाम दिखाने की जिम्मेदारी किसकी है । मैंने नमूना परियोजना बनाई है जो यह दिखा रही है कि यह कार्यान्वयन और उपयोग है।

यहाँ सबसे महत्वपूर्ण भाग हैं:

//we will call this interface in our viewmodels
public interface IDialogService
{
    bool? ShowDialog(object dialogViewModel, string caption);
}

//we need to display logindialog from mainwindow
public class MainWindowViewModel : ViewModelBase
{
    public string Message {get; set;}
    public void ShowLoginCommandExecute()
    {
        var loginViewModel = new LoginViewModel();
        var dialogResult = this.DialogService.ShowDialog(loginViewModel, "Please, log in");

        //after dialog is closed, do someting
        if (dialogResult == true && loginViewModel.IsLoginSuccessful)
        {
            this.Message = string.Format("Hello, {0}!", loginViewModel.Username);
        }
    }
}


public class DialogService : IDialogService
{
    public bool? ShowDialog(object dialogViewModel, string caption)
    {
        var contentView = ViewLocator.GetView(dialogViewModel);
        var dlg = new DialogWindow
        {
            Title = caption
        };
        dlg.PART_ContentControl.Content = contentView;

        return dlg.ShowDialog();
    }
}

क्या यह सिर्फ सरल नहीं है? अधिक सरल, अधिक पठनीय और अंतिम लेकिन कम से कम ईवेंटएग्रेगेटर या अन्य समान समाधानों की तुलना में डिबग करना आसान नहीं है?

जैसा कि आप देख सकते हैं, मेरे विचार मॉडल में मैंने अपने पोस्ट में वर्णित ViewModel के पहले दृष्टिकोण का उपयोग किया है: WPF में ViewModel से दृश्य कॉल करने के लिए सबसे अच्छा अभ्यास

बेशक, वास्तविक दुनिया में, DialogService.ShowDialogसंवाद को कॉन्फ़िगर करने के लिए और अधिक विकल्प होना चाहिए, उदाहरण के लिए बटन और कमांड जिन्हें उन्हें निष्पादित करना चाहिए। ऐसा करने के अलग-अलग तरीके हैं, लेकिन इसके दायरे से बाहर हैं :)


0

हालांकि यह इस सवाल का जवाब नहीं देता है कि यह व्यूमोडेल के माध्यम से कैसे किया जाता है, यह दिखाता है कि यह केवल XAML + ब्लेंड एसडीके का उपयोग करके कैसे करें।

मैंने ब्लेंड एसडीके से दो फ़ाइलों को डाउनलोड करने और उपयोग करने के लिए चुना, दोनों ही आप Microsoft से NuGet के माध्यम से पैकेज के रूप में ले सकते हैं। फाइलें हैं:

System.Windows.Interactivity.dll और Microsoft.Expression.Interactions.dll

Microsoft.Expression.Interactions.dll आपको अच्छी क्षमताएं प्रदान करता है जैसे कि संपत्ति सेट करने की क्षमता या आपके व्यूमोडल या अन्य लक्ष्य पर एक विधि लागू करने और अन्य विजेट भी अंदर हैं।

कुछ XAML:

<Window x:Class="Blah.Blah.MyWindow"
    ...
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
  ...>
 <StackPanel>
    <Button x:Name="OKButton" Content="OK">
       <i:Interaction.Triggers>
          <i:EventTrigger EventName="Click">
             <ei:ChangePropertyAction
                      TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
                      PropertyName="DialogResult"
                      Value="True"
                      IsEnabled="{Binding SomeBoolOnTheVM}" />                                
          </i:EventTrigger>
    </Button>
    <Button x:Name="CancelButton" Content="Cancel">
       <i:Interaction.Triggers>
          <i:EventTrigger EventName="Click">
             <ei:ChangePropertyAction
                      TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
                      PropertyName="DialogResult"
                      Value="False" />                                
          </i:EventTrigger>
    </Button>

    <Button x:Name="CloseButton" Content="Close">
       <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <!-- method being invoked should be void w/ no args -->
                    <ei:CallMethodAction
                        TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
                        MethodName="Close" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
    </Button>
 <StackPanel>
</Window>

ध्यान दें कि यदि आप केवल साधारण ओके / रद्द व्यवहार के लिए जा रहे हैं, तो आप w / विंडो के रूप में लंबे समय तक दिखाया जा सकता है, जब तक कि विंडो को w / Window.ShowDialog () दिखाया गया है, तब तक आप w / IsDefault और IsCancel गुणों का उपयोग कर सकते हैं।
मुझे व्यक्तिगत रूप से समस्या थी w / एक बटन जिसके पास IsDefault प्रॉपर्टी सही थी, लेकिन पेज के लोड होने पर इसे छिपा दिया गया था। यह दिखाने के बाद अच्छी तरह से खेलना नहीं चाहता था, इसलिए मैं सिर्फ Window.DialogResult संपत्ति सेट कर रहा हूं जैसा कि ऊपर दिखाया गया है और यह मेरे लिए काम करता है।


0

यहाँ सरल बग मुक्त समाधान (स्रोत कोड के साथ) है, यह मेरे लिए काम कर रहा है।

  1. से अपना ViewModel डिलीवर करें INotifyPropertyChanged

  2. एक अवलोकनीय संपत्ति बनाएँ ViewModel में CloseDialog

    public void Execute()
    {
        // Do your task here
    
        // if task successful, assign true to CloseDialog
        CloseDialog = true;
    }
    
    private bool _closeDialog;
    public bool CloseDialog
    {
        get { return _closeDialog; }
        set { _closeDialog = value; OnPropertyChanged(); }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    private void OnPropertyChanged([CallerMemberName]string property = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    }

  3. इस संपत्ति परिवर्तन के लिए दृश्य में एक हैंडलर संलग्न करें

        _loginDialogViewModel = new LoginDialogViewModel();
        loginPanel.DataContext = _loginDialogViewModel;
        _loginDialogViewModel.PropertyChanged += OnPropertyChanged;
  4. अब आप लगभग हो चुके हैं। घटना में हैंडलर बनाते हैंDialogResult = true

    protected void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        if (args.PropertyName == "CloseDialog")
        {
            DialogResult = true;
        }
    }

0

Dependency Propertyअपने View/ किसी भी UserControl(या Windowआप बंद करना चाहते हैं) में एक बनाएँ । नीचे की तरह:

 public bool CloseTrigger
        {
            get { return (bool)GetValue(CloseTriggerProperty); }
            set { SetValue(CloseTriggerProperty, value); }
        }

        public static readonly DependencyProperty CloseTriggerProperty =
            DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(ControlEventBase), new PropertyMetadata(new PropertyChangedCallback(OnCloseTriggerChanged)));

        private static void OnCloseTriggerChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
        {
            //write Window Exit Code
        }

और इसे अपने ViewModel की संपत्ति से बांधें :

<Window x:Class="WpfStackOverflowTempProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"  Width="525"
        CloseTrigger="{Binding Path=CloseWindow,Mode=TwoWay}"

इसमें संपत्ति VeiwModel:

private bool closeWindow;

    public bool CloseWindow
    {
        get { return closeWindow; }
        set 
        { 
            closeWindow = value;
            RaiseChane("CloseWindow");
        }
    }

अब CloseWindowViewModel में मान बदलकर क्लोज ऑपरेशन को ट्रिगर करें । :)


-2

जहां आपको विंडो बंद करने की आवश्यकता है, बस इसे व्यूमाडेल में रखें:

टा-डा

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

एक ViewModel किसी भी तरह से एक UIElement शामिल नहीं होना चाहिए , क्योंकि यह बग का निर्माण कर सकता है
WiiMaxx

क्या होगा अगर DataContext को विरासत में कई खिड़कियां मिलें?
Kylo Ren

ta-da, यह पूरी तरह से MVVM नहीं है।
अलेक्जेंड्रू डिकू

-10
Application.Current.MainWindow.Close() 

बस काफी है!


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