2009-11-18 25 views
9

में से मैं अपनी पहली WPF अनुप्रयोग में MVVM पद्धति का उपयोग कर रहा हूँ और कुछ काफी बुनियादी मुझे लगता है के साथ एक समस्या है।अद्यतन यूआई ViewModel वर्ग (MVVM पैटर्न) WPF

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

समस्या यह है कि "सेव()" में कोड निष्पादित करने में कुछ समय लगता है, इसलिए मैं कोड के बड़े हिस्से को निष्पादित करने से पहले UI दृश्य में "सहेजें" बटन को छिपाना चाहता हूं।

समस्या यह है कि दृश्य को अपडेट नहीं है तक सभी कोड viewmodel में क्रियान्वित किया जाता है। सहेजें() कोड निष्पादित करने से पहले प्रॉपर्टी चेंज किए गए ईवेंट को पुन: निकालने और संसाधित करने के लिए मैं दृश्य को कैसे लागू कर सकता हूं?

साथ ही, मैं एक reuseable रास्ता चाहते हैं, ताकि मैं आसानी से और साथ ही अन्य पन्नों में एक ही बात कर सकते हैं .. किसी को भी किसी और की तरह यह पहले से ही कुछ किया? एक "लोड हो रहा है ..." संदेश?

उत्तर

11

यदि यह लंबा समय लगता है, तो एक अलग थ्रेड का उपयोग करने पर विचार करें, उदाहरण के लिए BackgroundWorker का उपयोग करके, ताकि यूआई थ्रेड ऑपरेशन के दौरान उत्तरदायी रह सके (यानी यूआई अपडेट करें)।

अपने Save विधि में, क्या तुम करोगी

  • परिवर्तन यूआई (यानी कुछ INotifyPropertyChanged या DependencyProperty IsBusySaving बूलियन जो अपने यूआई के लिए बाध्य है, संशोधित करने, सहेजें बटन छुपाता है और हो सकता है IsIndeterminate = True के साथ कुछ प्रगति बार दिखाता है) और
  • BackgroundWorker शुरू करें।

DoWork आपके पृष्ठभूमिवर्कर के ईवेंट हैंडलर में, आप लंबे बचत ऑपरेशन करते हैं।

RunWorkerCompleted यूआई थ्रेड में निष्पादित ईवेंट हैंडलर में, आप IsBusySaving को झूठी में सेट करते हैं और यूआई में अन्य सामान बदल सकते हैं ताकि आप यह दिखा सकें कि आप समाप्त हो गए हैं।

कोड उदाहरण (untested):

BackgroundWorker bwSave; 
DependencyProperty IsBusySavingProperty = ...; 

private MyViewModel() { 
    bwSave = new BackgroundWorker(); 

    bwSave.DoWork += (sender, args) => { 
     // do your lengthy save stuff here -- this happens in a separate thread 
    } 

    bwSave.RunWorkerCompleted += (sender, args) => { 
     IsBusySaving = false; 
     if (args.Error != null) // if an exception occurred during DoWork, 
      MessageBox.Show(args.Error.ToString()); // do your error handling here 
    } 
} 

private void Save() { 
    if (IsBusySaving) { 
     throw new Exception("Save in progress -- this should be prevented by the UI"); 
    } 
    IsBusySaving = true; 
    bwSave.RunWorkerAsync(); 
} 
+0

धन्यवाद, मैं इसे आज़मा दूंगा। –

+0

क्षमा करें जब यह थ्रेडिंग की बात आती है तो मैं कुल डम्बो हूं। सहेजें कोड I के अंदर (कभी-कभी) किसी अन्य पृष्ठ पर नेविगेट करने का प्रयास करें। लेकिन क्योंकि मैं एक और धागे में हूं, यह रनटाइम त्रुटि देता है। मुझे लगता है कि मुझे मूल धागे पर कॉलबैक करना है और वहां से दूसरे पृष्ठ पर नेविगेट करना है। लेकिन मैं इसे स्वयं कोशिश करूंगा, मुझे यकीन है कि मूल धागे से संवाद करना मुश्किल नहीं है। –

+0

"कॉलिंग थ्रेड इस ऑब्जेक्ट तक नहीं पहुंच सकता है क्योंकि एक अलग धागा इसका मालिक है।" वह संदेश है जो मुझे मिलता है। यदि आपको दिल से पता है कि मुझे क्या चाहिए, तो मुझे बताएं :-) –

0

तुम हमेशा कुछ इस तरह कर सकता है:

public class SaveDemo : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 
    private bool _canSave; 

    public bool CanSave 
    { 
    get { return _canSave; } 
    set 
    { 
     if (_canSave != value) 
     { 
     _canSave = value; 
     OnChange("CanSave"); 
     } 
    } 
    } 

    public void Save() 
    { 
    _canSave = false; 

    // Do the lengthy operation 
    _canSave = true; 
    } 

    private void OnChange(string p) 
    { 
    PropertyChangedEventHandler handler = PropertyChanged; 
    if (handler != null) 
    { 
     handler(this, new PropertyChangedEventArgs(p)); 
    } 
    } 
} 

तो फिर तुम CanSave संपत्ति के लिए बटन के IsEnabled संपत्ति के लिए बाध्य कर सकता है, और यह होगा स्वचालित रूप से सक्षम/अक्षम हो। एक वैकल्पिक विधि, और एक के साथ मैं इसे हल करने के लिए कमांड CanExecute का उपयोग करना होगा, लेकिन विचार आपके लिए काम करने के लिए पर्याप्त है।

+1

(1) यदि आप CanSave के बजाय _canSave सेट करते हैं, तो OnChange नहीं उठाया जाएगा।(2) मुझे नहीं लगता कि यह काम करेगा, चूंकि सहेजें यूआई थ्रेड में चलता है, इसलिए डब्ल्यूपीएफ यूआई अपडेट नहीं किया जाएगा जब तक कि सेव समाप्त नहीं हो जाता है। – Heinzi

+0

हां वास्तव में, मैं जवाब की सराहना करता हूं लेकिन मुझे नहीं लगता कि यह मेरी समस्या को हल करता है। हेन्ज़ी के सुझाव ने इसे ठीक किया। –

+0

@Hinzi - CanSave पर अच्छी जगह है, और हाँ यह काम करेगा क्योंकि अधिसूचना परिवर्तन सहेजने की शुरुआत की शुरुआत में उठाया जाता है - इसलिए, उस बिंदु पर यूआई अपडेट। –

3

आप एमवीवीएम पैटर्न का उपयोग कर रहे हैं, इसलिए आपका सेव बटन कमांड रूटेड कमांड ऑब्जेक्ट के उदाहरण पर सेट है जो विंडो के कमांडबिंडिंग संग्रह में घोषित या अनिवार्य रूप से जोड़ा गया है।

मान लीजिए कि आप इसे घोषणात्मक रूप से करते हैं। निष्पादित कराई घटना के हैंडलर के लिए की तरह

<Window.CommandBindings> 
    <CommandBinding 
     Command="{x:Static namespace:ClassName.StaticRoutedCommandObj}" 
     CanExecute="Save_CanExecute" 
     Executed="Save" 
    /> 
</Window.CommandBindings> 

कुछ है, अपने को बचाने() विधि, प्रवेश पर, आप एक चर गलत सेट है, वापसी पर आप सच में पुन: निर्धारित किया है। कुछ इस तरह।

void Save(object sender, ExecutedRoutedEventArgs e) 
{ 
    _canExecute = false; 
    // do work 
    _canExecute = true; 
} 

CanExecute का हैंडलर के लिए घटना, Save_CanExecute() विधि से कराई, आप शर्त के रूप में चर का उपयोग करें।

void ShowSelectedXray_CanExecute(object sender, CanExecuteRoutedEventArgs e) 
{ 
    e.CanExecute = _canExecute && _others; 
} 

मुझे उम्मीद है कि मैं स्पष्ट हूं। :)

0

आप नीचे दिए गए कोड ऐसा कर सकते हैं ..

Thread workerThread = null; 
void Save(object sender, ExecutedRoutedEventArgs e) 
{ 
workerThread = new Thread(new ThreadStart(doWork)); 
SaveButton.isEnable = false; 
workerThread.start(); 
} 

dowork में अपने सभी लंबी प्रक्रिया कर() विधि

किसी अन्य विधि में ..

workerThread.join(); 
SaveButtton.isEnable = true; 

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

0

देर से जवाब, लेकिन मुझे लगा कि यह थोड़ा इनपुट करने के लिए भी अच्छा होगा।

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

तरीका है कि ऐसा करने के लिए है:

ThreadPool.QueueUserWorkItem(Save); 

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

+0

आपके उत्तर के लिए धन्यवाद। मैंने अपने आवेदन में स्वीकृत उत्तर का उपयोग किया और यह ठीक काम करता है। मुझे नहीं पता कि संसाधन समाधान आपके समाधान की तुलना में कैसे है लेकिन पृष्ठभूमि कार्यकर्ता के साथ काम करना बहुत सुविधाजनक है। –

+0

कीथ: नेट 3.5 और ऊपर, यदि आपको इसकी आवश्यकता नहीं है तो राज्य वस्तु को ट्रिम करने के लिए लैम्ब्डा अभिव्यक्तियों पर विचार करें। ThreadPool.QueueUserWorkItem (राज्य => सहेजें()); शायद यहां थोड़ा अधिक ओवरहेड है लेकिन कोड को अक्सर कम प्रतिनिधि आकर्षक तरीकों के साथ फ़्लोटिंग के साथ सरल बना दिया जाता है। आप राज्य ऑब्जेक्ट को जो कुछ भी चाहते हैं उसे लाने और विशिष्ट गुणों को खींचने के लिए लैम्ब्डा का भी उपयोग कर सकते हैं। ThreadPool.QueueUserWorkItem (राज्य => सहेजें (SaveArgs के रूप में स्थिति)। तरंग, SaveArgs के रूप में स्थिति)। टाइमस्टैम्प); – Gusdor

संबंधित मुद्दे