2012-10-23 20 views
16

मेरे पास एक प्रोजेक्ट आधारित WPF और MVVM है। मेरी परियोजना एक सामग्री नियंत्रण युक्त विज़ार्ड पर आधारित है जो मेरे विचार (उपयोगकर्ता नियंत्रण) दिखाती है कि दृश्य पूरी तरह से लोड होने के बाद मैं एक आदेश निष्पादित करना चाहता हूं, मैं चाहता हूं कि उपयोगकर्ता तुरंत आदेश देखने के बाद यूआई देखें मार डाला।दृश्य लोड होने के बाद कमांड निष्पादित करें WPF MVVM

मैं उपयोग करने की कोशिश:

<i:Interaction.Triggers> 
    <i:EventTrigger EventName="Loaded"> 
     <i:InvokeCommandAction Command="{Binding StartProgressCommand}"/> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

लेकिन इससे पहले कि मैं दृश्य UI देखने के आदेश निष्पादित किया जाता है और यह मैं क्या देख रहा हूँ नहीं है।

क्या किसी को पता है कि मुझे इसे कैसे कार्यान्वित करने की आवश्यकता है?

उत्तर

16

ऐसा इसलिए है क्योंकि तकनीकी रूप से दृश्य लोड किया गया है (यानी: सभी घटक स्मृति में तैयार हैं), आपका ऐप अभी तक निष्क्रिय नहीं है, और इस प्रकार यूआई को अभी तक ताज़ा नहीं किया गया है।

Loaded ईवेंट पर इंटरैक्शन ट्रिगर्स का उपयोग करके कमांड सेट करना पहले से ही अच्छा है, क्योंकि इसमें कोई बेहतर ईवेंट नहीं है।
अब वास्तव में प्रतीक्षा करने के लिए यूआई दिखाया गया है जब तक, में यह कर अपने StartProgress() (मैं यहाँ है कि इस विधि का नाम है कि StartProgressCommand बात करने के लिए है यह सोचते हैं रहा हूँ):

public void StartProgress() 
{ 
    new DispatcherTimer(//It will not wait after the application is idle. 
         TimeSpan.Zero, 
         //It will wait until the application is idle 
         DispatcherPriority.ApplicationIdle, 
         //It will call this when the app is idle 
         dispatcherTimer_Tick, 
         //On the UI thread 
         Application.Current.Dispatcher); 
} 

private static void dispatcherTimer_Tick(object sender, EventArgs e) 
{ 
    //Now the UI is really shown, do your computations 
} 
+1

धागे को कैसे रोकें? इसकी कॉलिंग अंतहीन है। – digz6666

+3

पुराना मुझे पता है, लेकिन जल्दबाजी में उन लोगों के लिए है कि धागे को कैसे समाप्त किया जाए: आपके ईवेंट हैंडलर var timer = प्रेषक डिस्पैचर टाइमर के रूप में प्रेषक; टाइमर। स्टॉप(); – Shawn

+0

मुझे वास्तव में पता नहीं है, अगर यह एक बहुत अच्छा दृष्टिकोण है, लेकिन यह काम करता है! – peter70

0

आप दृश्य के लिए IsLoaded संपत्ति की जांच कर सकते हैं, जब लोड लोड फॉर्म में होता है तो यह झूठा होता है, जब दृश्य पूरी तरह से लोड हो जाता है, तो यह संपत्ति सच हो जाती है।

धन्यवाद, रजनीकांत

+1

यह काम नहीं कर रहा है। दृश्य को – Ofir

0

आप ContentRendered घटना के लिए बाध्य करने की कोशिश की है? यह लोड होने वाली घटना के बाद होगा, फिर भी मुझे यकीन नहीं है कि यह एक गारंटी है कि यूआई थ्रेड ने खिड़की को पेंट करना समाप्त कर दिया है।

+1

प्रस्तुत करने से पहले IsLoaded को वास्तविक मान मिलता है, मैं उपयोगकर्ता नियंत्रण का उपयोग करता हूं, न कि विंडो के लिए, इसलिए ईवेंट सामग्री प्रस्तुत नहीं है। – Ofir

+0

क्या आपका नियंत्रण विंडो में गतिशील रूप से जोड़ा गया है? यदि नहीं, तो पैरेंट विंडो – Dominik

0

आप "CommandExecute" विधि की पहली पंक्ति में "थ्रेड स्लीप (10000)" लिख सकते हैं। एक ही लोड ट्रिगर का प्रयोग करें।

यदि आप थ्रेड का उपयोग नहीं करना चाहते हैं तो सो जाओ तो आप "डिस्पैचर टिमर" के लिए जा सकते हैं। अपने कमांड निष्पादन विधि में एक टाइमर शुरू करें और अपने सभी कोड टाइमर टिक घटना में स्थानांतरित करें। अपने टाइमर अंतराल को 2 सेकंड में सेट करें, ताकि उपयोगकर्ता यूआई को देख सके।

+0

की घटना से जुड़ें मैंने इसके बारे में सोचा लेकिन यदि संभव हो तो मैं समय धारणाओं पर कमांड बेस निष्पादित करने से बचना पसंद करता हूं – Ofir

+0

यहां तक ​​कि मुझे अपने आवेदन में टाइमर का उपयोग करना पसंद नहीं है, लेकिन मुझे नहीं लगता कि हम WPF में कोई भी ईवेंट है जिसे लोड होने के कुछ सेकंड बाद निकाल दिया जाएगा। यदि आप कमांड निष्पादित करने से पहले थोड़ी देर के लिए यूआई दिखाना चाहते हैं तो आपको टाइमर का उपयोग करने के लिए मजबूर होना पड़ता है। – Bathineni

+0

ऑफिस के साथ सहमत हैं। एक सामान्य नियम के रूप में, कभी भी दौड़ की स्थिति को हल करने के लिए समय धारणाएं नहीं बनाते हैं। आवेदन पर ट्रिगरिंग। लुई कौफमैन के रूप में दिखाया गया है सबसे अच्छा है। – blearyeye

1

हम टाइमर समाधान का उपयोग करते हैं - मैं भी इसके बारे में बहुत संदिग्ध था लेकिन ऐसा लगता है कि यह ठीक काम करता है।

public static class DispatcherExtensions 
{ 
    private static Dictionary<string, DispatcherTimer> timers = 
     new Dictionary<string, DispatcherTimer>(); 
    private static readonly object syncRoot = new object(); 

    public static void DelayInvoke(this Dispatcher dispatcher, string namedInvocation, 
     Action action, TimeSpan delay, 
     DispatcherPriority priority = DispatcherPriority.Normal) 
    { 
     lock (syncRoot) 
     { 
      RemoveTimer(namedInvocation); 
      var timer = new DispatcherTimer(delay, priority, (s, e) => 
       { 
        RemoveTimer(namedInvocation); 
        action(); 
       }, dispatcher); 
      timer.Start(); 
      timers.Add(namedInvocation, timer); 
     } 
    } 


    public static void CancelNamedInvocation(this Dispatcher dispatcher, string namedInvocation) 
    { 
     lock (syncRoot) 
     { 
      RemoveTimer(namedInvocation); 
     } 
    } 

    private static void RemoveTimer(string namedInvocation) 
    { 
     if (!timers.ContainsKey(namedInvocation)) return; 
     timers[namedInvocation].Stop(); 
     timers.Remove(namedInvocation); 
    } 


} 

फिर हम

Dispatcher.CurrentDispatcher.DelayInvoke("InitSomething",()=> { 
    DoSomething(); 
},TimeSpan.FromSeconds(1)); 
12

का उपयोग कर आह्वान आप इस के लिए डिस्पैचर का उपयोग करें और ApplicationIdle को प्राथमिकता सेट कर सकते हैं ताकि उस पर अमल होगा जब सब कुछ

  Application.Current.Dispatcher.Invoke(
      DispatcherPriority.ApplicationIdle, 
      new Action(() => 
      { 
       StartProgressCommand.Invoke(args); 

      })); 

अधिक जानकारी समाप्त हो गया है प्रेषक http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx

चीयर्स पर। स्टी।

+0

यह मेरे लिए काम करता है :) – digz6666

+0

यह स्वीकृत उत्तर की तुलना में एक बहुत साफ समाधान की तरह लगता है। एक टाइमर क्यों शुरू करें जब आप केवल एक चक्र चलाने जा रहे हैं? –

+0

'Invoke' मेरे लिए चाल नहीं प्रतीत होता था, इसलिए मैंने 'Invoke' के बजाय' BeginInvoke' का उपयोग किया है। वह पूरी तरह से काम किया। –

0

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

असल में, यह

  • एक DispatcherTimer का उपयोग करता है एक विधि शुरू करने के लिए यूआई this
  • एक async विधि जो Action एक पैरामीटर

कॉल के रूप में पारित निष्पादित करेंगे के अनुसार प्रस्तुत करना के बाद:

this.LaunchThisWhenUiLoaded(() => { /*Stuff to do after Ui loaded here*/ }); 

विधि:

private DispatcherTimer dispatchTimer; 
private Action ActionToExecuteWhenUiLoaded; 

/// <summary> 
/// Handy method to launch an Action after full UI rendering 
/// </summary> 
/// <param name="toExec"></param> 
protected void LaunchThisWhenUiLoaded(Action toExec) 
{    
    ActionToExecuteWhenUiLoaded = toExec; 
    // Call UiLoaded method when UI is loaded and rendered 
    dispatchTimer = new DispatcherTimer(TimeSpan.Zero, DispatcherPriority.ContextIdle, UiLoaded, Application.Current.Dispatcher); 
} 

/// <summary> 
/// Method called after UI rendered 
/// </summary> 
/// <param name="sender"></param> 
/// <param name="e"></param> 
protected async void UiLoaded(object sender, EventArgs e) 
{ 
    this.IsBusy = true;  

    if (ActionToExecuteWhenUiLoaded != null) 
     await Task.Run(ActionToExecuteWhenUiLoaded); 
    dispatchTimer.Stop(); 

    this.IsBusy = false; 
} 

हो सकता है कि साफ नहीं है, लेकिन यह अपेक्षा के अनुरूप काम करता है।

1

एक और तरीका यह करने के लिए:

इस xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" और xmlns:mi="http://schemas.microsoft.com/expression/2010/interactions" अपने usercontrol XAML पर परिभाषित करने और अपनी परियोजना पर Microsoft.Expression.Interactions विधानसभा जोड़ें। बस के रूप में bellow अपने ट्रिगर पर CallMethodAction उपयोग करते हैं,: ग्रिड:

<i:Interaction.Triggers> 
    <i:EventTrigger EventName="Loaded"> 
     <mi:CallMethodAction TargetObject="{Binding}" MethodName="StartProgressCommand"/> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

अपने usercontrol के मूल तत्व, उदा अंदर triger रखो। और उदाहरण, सादे पुराने नियमित विधि के आदेश से अपने StartProgressCommand बदलते हैं तो आपकी ViewModel कक्षा में,,:

public void StartProgressCommand() 
{ 
    /* put your program logic here*/ 
} 

यह विधि वास्तव में एक बार प्रत्येक बार आपके उपयोगकर्ता नियंत्रण प्रदान की गई चलाएँगे।

+0

आसान और सटीक ... बस 'xmlns के साथ संपादित: i' जो – andrepaulo

+0

गुम था, यह मेरे लिए काम नहीं करता है यह मुझे बता रहा है कि विधि या संचालन लागू नहीं किया गया है –