WPF में Application.DoEvents () कहां है?


88

मेरे पास निम्न नमूना कोड है जो हर बार बटन दबाए जाने पर ज़ूम करता है:

XAML:

<Window x:Class="WpfApplication12.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <Canvas x:Name="myCanvas">

        <Canvas.LayoutTransform>
            <ScaleTransform x:Name="myScaleTransform" />
        </Canvas.LayoutTransform> 

        <Button Content="Button" 
                Name="myButton" 
                Canvas.Left="50" 
                Canvas.Top="50" 
                Click="myButton_Click" />
    </Canvas>
</Window>

* .cs

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

    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        Console.WriteLine("scale {0}, location: {1}", 
            myScaleTransform.ScaleX,
            myCanvas.PointToScreen(GetMyByttonLocation()));

        myScaleTransform.ScaleX =
            myScaleTransform.ScaleY =
            myScaleTransform.ScaleX + 1;

        Console.WriteLine("scale {0}, location: {1}",
            myScaleTransform.ScaleX,
            myCanvas.PointToScreen(GetMyByttonLocation()));
    }

    private Point GetMyByttonLocation()
    {
        return new Point(
            Canvas.GetLeft(myButton),
            Canvas.GetTop(myButton));
    }
}

आउटपुट है:

scale 1, location: 296;315
scale 2, location: 296;315

scale 2, location: 346;365
scale 3, location: 346;365

scale 3, location: 396;415
scale 4, location: 396;415

जैसा कि आप देख सकते हैं, एक समस्या है, जिसे मैंने सोचा था कि उपयोग करके हल किया जाएगा Application.DoEvents();... लेकिन यह .NET 4 में एक प्राथमिकता नहीं है ।

क्या करें?


8
थ्रेडिंग? Application.DoEvents () किसी भी घटना में ठीक से बहु-थ्रेडेड एप्लिकेशन और अत्यंत खराब अभ्यास लिखने के लिए गरीब आदमी का विकल्प था।
कॉलिन मैके

2
मुझे पता है कि गरीब और बुरा है, लेकिन मैं कुछ ऐसा पसंद करता हूं जो बिल्कुल भी नहीं है।
सेरहियो

जवाबों:


25

पुरानी Application.DoEvents () पद्धति को डिस्पैचर या बैकग्राउंड वर्कर थ्रेड का उपयोग करने के पक्ष में WPF में पदावनत किया गया है जैसा कि आपने वर्णित किया है। दोनों वस्तुओं का उपयोग कैसे करें पर लेख के एक जोड़े के लिए लिंक देखें।

यदि आप पूरी तरह से Application.DoEvents () का उपयोग करते हैं, तो आप बस अपने आवेदन में system.windows.forms.dll आयात कर सकते हैं और विधि को कॉल कर सकते हैं। हालाँकि, यह वास्तव में अनुशंसित नहीं है, क्योंकि आप WPF द्वारा प्रदान किए गए सभी फायदे खो रहे हैं।


मुझे पता है कि गरीब और बुरा है, लेकिन मैं कुछ ऐसा पसंद करता हूं जो कुछ भी नहीं है ... मैं अपनी स्थिति में डिस्पैचर का उपयोग कैसे कर सकता हूं?
सेहियो

3
मैं अपकी स्थिति को समझता हूँ। मैं इसमें था जब मैंने अपना पहला WPF ऐप लिखा था, लेकिन मैंने आगे बढ़कर नई लाइब्रेरी सीखने के लिए समय लिया और लंबे समय में इसके लिए बहुत बेहतर था। मैं अत्यधिक समय लेने की सलाह देता हूं। आपके विशेष मामले के लिए, यह मुझे ऐसा लगता है कि आप चाहते हैं कि डिस्पैचर हर बार आपके क्लिक ईवेंट के लिए निर्देशांक का प्रदर्शन संभाल सके। आपको सटीक कार्यान्वयन के लिए डिस्पैचर पर अधिक पढ़ना होगा।
डिलि-ओ

नहीं, मैं Application.DoEventsmyScaleTransform.ScaleX को बढ़ाने के बाद कॉल करूंगा। डिस्पैचर के साथ संभव है या नहीं, पता नहीं।
सेहियो

12
Application.DoEvents () को हटाना लगभग उतना ही कष्टप्रद है जितना कि MS विंडोज पर "स्टार्ट" बटन को
हटाना

2
नहीं, यह एक अच्छी बात थी, इस पद्धति से अच्छे से अधिक नुकसान हुआ।
जेसी

136

कुछ इस तरह की कोशिश करो

public static void DoEvents()
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
                                          new Action(delegate { }));
}

1
मैंने आवेदन के लिए एक विस्तार विधि भी लिखी :)public static void DoEvents(this Application a)
serhio

@ सेशियो: नीट विस्तार विधि :)
फ्रेड्रिक हेडब्लैड

2
मुझे हालांकि टिप्पणी करनी चाहिए कि वास्तविक एप्लिकेशन में Application.Currentकभी-कभी अशक्त होता है ... इसलिए शायद यह बिल्कुल समकक्ष नहीं है।
सर्हियो

उत्तम। इसका उत्तर होना चाहिए। Naysayers को क्रेडिट नहीं मिलना चाहिए।
जेसन मार्तजया

6
यह हमेशा काम नहीं करेगा क्योंकि यह फ्रेम को धक्का नहीं देता है, अगर एक बाधा डालने वाला निर्देश बनाया जा रहा है (जैसे कि एक WCF विधि के लिए एक कॉल जो इस कमांड में समकालिक जारी है) तो संभावना है कि आप 'ताज़ा' नहीं देखेंगे क्योंकि यह अवरुद्ध हो जाएगा .. यही कारण है कि MSDN संसाधन से प्रदान किया गया उत्तर flq इस एक से अधिक सही है।
GY

57

ठीक है, मैंने सिर्फ एक मामले को मारा जहां मैं एक विधि पर काम शुरू करता हूं जो डिस्पैचर धागे पर चलता है, और इसे यूआई थ्रेड को अवरुद्ध किए बिना ब्लॉक करने की आवश्यकता है। बताते हैं कि एमएसडीएन बताता है कि डिस्पैचर के आधार पर DoEvents () कैसे लागू किया जाए:

public void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(ExitFrame), frame);
    Dispatcher.PushFrame(frame);
}

public object ExitFrame(object f)
{
    ((DispatcherFrame)f).Continue = false;

    return null;
}

( डिस्पैचर से लिया गया ।PushFrame विधि )

कुछ इसे एक ही तरीके से पसंद कर सकते हैं जो एक ही तर्क को लागू करेगा:

public static void DoEvents()
{
    var frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(
            delegate (object f)
            {
                ((DispatcherFrame)f).Continue = false;
                return null;
            }),frame);
    Dispatcher.PushFrame(frame);
}

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

2
@HugoRune यह ब्लॉग पोस्ट बताता है कि यह दृष्टिकोण अनावश्यक है, और मेल्क के समान कार्यान्वयन का उपयोग करने के लिए।
लुकाज़ोइड

1
@ Lukazoid जहां तक ​​मैं बता सकता हूं कि सरल कार्यान्वयन हार्ड-टू-ट्रेस लॉक-अप का कारण हो सकता है। (मैं कारण के बारे में निश्चित नहीं हूं, संभवतः समस्या डिस्पैचर कतार में कोड है जिसे दोबारा DoEvents कहा जाता है, या डिस्पैचर कतार में कोड आगे डिस्पैचर फ्रेम उत्पन्न करता है।) किसी भी मामले में, बाहर निकलने के समाधान के साथ ऐसी कोई समस्या नहीं है। सलाह है कि एक। (या, ज़ाहिर है, dovvents का उपयोग नहीं करते हुए)
ह्यूगो रून

1
जब ऐप कॉलबैक बंद कर रहा हो तो वीएम को शामिल करने के लिए कैलिबर के तरीके के संयोजन के बजाय एक संवाद के बजाय अपनी खिड़की पर एक ओवरले दिखाना। मुझे खुशी होगी यदि आप मुझे DoEvents हैक के बिना एक समाधान प्रस्तुत करते हैं।
12

1
पुराने ब्लॉग पोस्ट का नया लिंक: kent-boogaart.com/blog/dispatcher-frames
CAD bloke

12

यदि आपको बस विंडो ग्राफिक अपडेट करने की आवश्यकता है, तो इस तरह से बेहतर उपयोग करें

public static void DoEvents()
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Render,
                                          new Action(delegate { }));
}

1
DispatcherPyerity का उपयोग करना। रेंडर अधिक तेजी से काम करें तो DispatcherPyerity.Background।
स्टॉपवॉकर के

मैंने सिर्फ इस ट्रिक का इस्तेमाल किया है, लेकिन बेहतरीन परिणामों के लिए डिस्पैचरपायरिटी (उच्च प्राथमिकता) का उपयोग करें; अधिक उत्तरदायी यूआई।
बेंट रासमुसेन

6
myCanvas.UpdateLayout();

साथ ही काम करने लगता है।


मैं इसका उपयोग करने जा रहा हूं क्योंकि यह मुझे बहुत सुरक्षित लगता है, लेकिन मैं अन्य मामलों के लिए DoEvents रखने जा रहा हूं।
कार्टर मेडलिन

पता नहीं क्यों लेकिन यह मेरे लिए काम नहीं करता है। DoEvents () ठीक काम करता है।
न्यूमैन

मेरे मामले में मुझे ऐसा करना था और साथ ही DoEvents ()
Jeff

3

दोनों प्रस्तावित दृष्टिकोणों के साथ एक समस्या यह है कि वे निष्क्रिय सीपीयू उपयोग (मेरे अनुभव में 12% तक) में प्रवेश करते हैं। उदाहरण के लिए, इस तकनीक का उपयोग करते समय मोडल UI व्यवहार को लागू करने के लिए कुछ मामलों में यह सबॉप्टीमल है।

निम्नलिखित भिन्नता टाइमर का उपयोग करके फ़्रेम के बीच एक न्यूनतम देरी का परिचय देती है (ध्यान दें कि यह Rx के साथ यहां लिखा गया है लेकिन किसी भी टाइमर के साथ प्राप्त किया जा सकता है):

 var minFrameDelay = Observable.Interval(TimeSpan.FromMilliseconds(50)).Take(1).Replay();
 minFrameDelay.Connect();
 // synchronously add a low-priority no-op to the Dispatcher's queue
 Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => minFrameDelay.Wait()));

1

की शुरूआत के बाद asyncऔर awaitइसके अब के माध्यम से एक (पूर्व) यूआई धागा बीच-बीच में छोड़ने के लिए संभव का उपयोग कर कोड की * तुल्यकालिक ब्लॉक Task.Delay, जैसे

private async void myButton_Click(object sender, RoutedEventArgs e)
{
    Console.WriteLine("scale {0}, location: {1}", 
        myScaleTransform.ScaleX,
        myCanvas.PointToScreen(GetMyByttonLocation()));

    myScaleTransform.ScaleX =
        myScaleTransform.ScaleY =
        myScaleTransform.ScaleX + 1;

    await Task.Delay(1); // In my experiments, 0 doesn't work. Also, I have noticed
                         // that I need to add as much as 100ms to allow the visual tree
                         // to complete its arrange cycle and for properties to get their
                         // final values (as opposed to NaN for widths etc.)

    Console.WriteLine("scale {0}, location: {1}",
        myScaleTransform.ScaleX,
        myCanvas.PointToScreen(GetMyByttonLocation()));
}

मैं ईमानदार रहूंगा, मैंने इसे ऊपर दिए गए सटीक कोड के साथ आज़माया नहीं है, लेकिन मैं इसे तंग छोरों में उपयोग करता हूं जब मैं कई आइटम रख रहा हूं ItemsControlजिसमें एक महंगा आइटम टेम्पलेट होता है, कभी-कभी दूसरे को देने के लिए एक छोटी सी देरी को जोड़ना। UI पर सामान अधिक समय।

उदाहरण के लिए:

        var levelOptions = new ObservableCollection<GameLevelChoiceItem>();

        this.ViewModel[LevelOptionsViewModelKey] = levelOptions;

        var syllabus = await this.LevelRepository.GetSyllabusAsync();
        foreach (var level in syllabus.Levels)
        {
            foreach (var subLevel in level.SubLevels)
            {
                var abilities = new List<GamePlayingAbility>(100);

                foreach (var g in subLevel.Games)
                {
                    var gwa = await this.MetricsRepository.GetGamePlayingAbilityAsync(g.Value);
                    abilities.Add(gwa);
                }

                double PlayingScore = AssessmentMetricsProcessor.ComputePlayingLevelAbility(abilities);

                levelOptions.Add(new GameLevelChoiceItem()
                    {
                        LevelAbilityMetric = PlayingScore,
                        AbilityCaption = PlayingScore.ToString(),
                        LevelCaption = subLevel.Name,
                        LevelDescriptor = level.Ordinal + "." + subLevel.Ordinal,
                        LevelLevels = subLevel.Games.Select(g => g.Value),
                    });

                await Task.Delay(100);
            }
        }

विंडोज स्टोर पर, जब संग्रह पर एक अच्छा विषय संक्रमण होता है, तो प्रभाव काफी वांछनीय होता है।

ल्यूक

  • टिप्पणी देखो। जब मैं जल्दी से अपना उत्तर लिख रहा था, तो मैं कोड के एक सिंक्रोनस ब्लॉक लेने के कार्य के बारे में सोच रहा था और फिर इसके कॉलर को थ्रेड वापस भेज रहा था, जिसका प्रभाव कोड के ब्लॉक को अतुल्यकालिक बनाता है। मैं अपने जवाब को पूरी तरह से खारिज नहीं करना चाहता, क्योंकि तब पाठक यह नहीं देख सकते थे कि मैं किस सेवा के बारे में सोच रहा था।

"यह अब एक तुल्यकालिक ब्लॉक के माध्यम से यूआई थ्रेड पार्टवे को त्यागना संभव है" नहीं, यह नहीं है। आपने यूआई थ्रेड से संदेशों को सिंक्रोनाइज़ करने के बजाय कोड को अतुल्यकालिक बना दिया है । अब, एक सही ढंग से डिज़ाइन किया गया डब्ल्यूपीएफ एप्लिकेशन एक ऐसा होगा जो यूआई थ्रेड को पहले स्थान पर यूआई थ्रेड में लंबे समय तक चलने वाले संचालन को सिंक्रोनाइज़ करने से कभी भी ब्लॉक नहीं करता है, मौजूदा संदेश पंप को संदेशों को उचित रूप से पंप करने की अनुमति देने के लिए अतुल्यकालिक का उपयोग करता है।
सर्वि

@Servy कवर्स के तहत, awaitकंपाइलर को असाइक विधि के बाकी हिस्सों को प्रतीक्षित कार्य पर जारी रखने के लिए साइन अप करना होगा। यह निरंतरता UI थ्रेड (समान सिंक संदर्भ) पर होगी। नियंत्रण फिर एस्किंट विधि के कॉलर पर वापस आ जाता है, यानी WPF ईवेंट्स सबसिस्टम, जहां इवेंट तब तक चलेंगे जब तक कि निर्धारित अवधि समाप्त होने के कुछ समय बाद तक शेड्यूल जारी रहता है।
ल्यूक पुप्लेट

हां, मैं इससे अच्छी तरह वाकिफ हूं। यही कारण है कि विधि अतुल्यकालिक (कॉल करने के लिए उपज नियंत्रण और केवल एक निरंतरता शेड्यूल करता है) बनाता है। आपका उत्तर बताता है कि जब यूआई को अपडेट करने के लिए यह अतुल्यकालिक का उपयोग कर रहा है तो विधि तुल्यकालिक है ।
18

पहली विधि (ओपी कोड) तुल्यकालिक, सर्व है। दूसरा उदाहरण यूआई को लूप में रखने या लंबी सूची में आइटम डालने के लिए सिर्फ एक टिप है।
ल्यूक पुप्लेट

और आपने जो किया है वह सिंक्रोनस कोड को एसिंक्रोनस बना दिया है। आपने अपने विवरण के अनुसार, या उत्तर के लिए पूछता है कि आपने एक तुल्यकालिक विधि के भीतर यूआई को उत्तरदायी नहीं रखा है।
सर्विस

0

WPF में अपना DoEvent () बनाएं:

Thread t = new Thread(() => {
            // do some thing in thread
            
            for (var i = 0; i < 500; i++)
            {
                Thread.Sleep(10); // in thread

                // call owner thread
                this.Dispatcher.Invoke(() => {
                    MediaItem uc = new MediaItem();
                    wpnList.Children.Add(uc);
                });
            }
            

        });
        t.TrySetApartmentState(ApartmentState.STA); //for using Clipboard in Threading
        t.Start();

मेरे लिए अच्छा काम करो!


-2

मूल प्रश्न का उत्तर देना: DoEvents कहां है?

मुझे लगता है कि DoEvents VBA है। और VBA में स्लीप फंक्शन नहीं लगता है। लेकिन वीबीए में नींद या देरी के समान प्रभाव प्राप्त करने का एक तरीका है। मुझे लगता है कि DoEvents स्लीप (0) के बराबर है।

VB और C # में, आप .NET में काम कर रहे हैं। और मूल प्रश्न C # प्रश्न है। C # में, आप Thread.Sleep (0) का उपयोग करेंगे, जहाँ 0 0 मिलीसेकंड है।

आप की जरूरत है

using System.Threading.Task;

उपयोग करने के लिए फ़ाइल के शीर्ष पर

Sleep(100);

आपके कोड में

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