WPF में सुरक्षित रूप से UI (मुख्य) थ्रेड एक्सेस करना


99

मेरे पास एक एप्लिकेशन है जो मेरे डेटाग्रिड को हर बार अपडेट करता है एक लॉग फ़ाइल जिसे मैं देख रहा हूं वह अद्यतन हो जाती है (निम्नलिखित में नए पाठ के साथ जोड़ा गया):

private void DGAddRow(string name, FunctionType ft)
    {
                ASCIIEncoding ascii = new ASCIIEncoding();

    CommDGDataSource ds = new CommDGDataSource();

    int position = 0;
    string[] data_split = ft.Data.Split(' ');
    foreach (AttributeType at in ft.Types)
    {
        if (at.IsAddress)
        {

            ds.Source = HexString2Ascii(data_split[position]);
            ds.Destination = HexString2Ascii(data_split[position+1]);
            break;
        }
        else
        {
            position += at.Size;
        }
    }
    ds.Protocol = name;
    ds.Number = rowCount;
    ds.Data = ft.Data;
    ds.Time = ft.Time;

    dataGridRows.Add(ds); 

    rowCount++;
    }
    ...
    private void FileSystemWatcher()
    {
        FileSystemWatcher watcher = new FileSystemWatcher(Environment.CurrentDirectory);
        watcher.Filter = syslogPath;
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
            | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        watcher.Changed += new FileSystemEventHandler(watcher_Changed);
        watcher.EnableRaisingEvents = true;
    }

    private void watcher_Changed(object sender, FileSystemEventArgs e)
    {
        if (File.Exists(syslogPath))
        {
            string line = GetLine(syslogPath,currentLine);
            foreach (CommRuleParser crp in crpList)
            {
                FunctionType ft = new FunctionType();
                if (crp.ParseLine(line, out ft))
                {
                    DGAddRow(crp.Protocol, ft);
                }
            }
            currentLine++;
        }
        else
            MessageBox.Show(UIConstant.COMM_SYSLOG_NON_EXIST_WARNING);
    }

जब फ़ाइलवॉचर के लिए ईवेंट उठाया जाता है, क्योंकि यह एक अलग थ्रेड बनाता है, जब मैं dataGridRows चलाने की कोशिश करता हूं। जोड़ें (डीएस); नई पंक्ति जोड़ने के लिए, प्रोग्राम केवल डिबग मोड के दौरान दी गई किसी चेतावनी के बिना क्रैश हो जाता है।

Winforms में, यह आसानी से Invoke फ़ंक्शन का उपयोग करके हल किया गया था, लेकिन मुझे यकीन नहीं है कि WPF में इस बारे में कैसे जाना है।

जवाबों:


203

आप उपयोग कर सकते हैं

Dispatcher.Invoke(Delegate, object[])

के Application(या किसी पर)UIElement है) डिस्पैचर।

आप इसे इस तरह उदाहरण के लिए उपयोग कर सकते हैं:

Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));

या

someControl.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));

उपरोक्त दृष्टिकोण एक त्रुटि दे रहा था क्योंकि Application.Current लाइन चलाने के समय शून्य है। ऐसा क्यों होगा?
l46kok

आप इसके लिए किसी भी UIElement का उपयोग कर सकते हैं, क्योंकि प्रत्येक UIElement में "डिस्पैचर" संपत्ति है।
वोल्फगैंग ज़िगलर

1
@ l46kok इसके अलग-अलग कारण हो सकते हैं (कंसोल ऐप, विनफॉर्म से होस्टिंग आदि)। जैसा कि @WolfgangZiegler ने कहा, आप इसके लिए किसी भी UIElement का उपयोग कर सकते हैं। मैं आमतौर पर Application.Currentइसके लिए उपयोग करता हूं क्योंकि यह मुझे साफ दिखता है।
बॉटज़ ३०००

@ बोटज़ ३००० मुझे लगता है कि मुझे भी कुछ रेस कंडीशन की समस्या है। ऊपर दिए गए कोड को लागू करने के बाद, कोड पूरी तरह से काम करता है जब मैं डिबग मोड में जाता हूं और मैन्युअल रूप से स्टेपओवर करता हूं, लेकिन जब मैं एप्लिकेशन को डिबग के बिना चलाता हूं तो कोड क्रैश हो जाता है। मुझे यकीन नहीं है कि यहां ताला क्या है जो समस्या पैदा कर रहा है।
l46kok

1
@ l46kok अगर आपको लगता है कि यह गतिरोध है, तो आप कॉल भी कर सकते हैं Dispatcher.BeginInvoke। यह विधि निष्पादन के लिए प्रतिनिधि को केवल कतार में खड़ा करती है।
बॉटज़ ३०००

51

इसके बारे में जाने का सबसे अच्छा तरीका SynchronizationContextयूआई थ्रेड से प्राप्त करना और इसका उपयोग करना होगा। यह वर्ग अन्य थ्रेड को कॉल करने के लिए सार करता है, और परीक्षण को आसान बनाता है (WPF के Dispatcherसीधे उपयोग करने के विपरीत )। उदाहरण के लिए:

class MyViewModel
{
    private readonly SynchronizationContext _syncContext;

    public MyViewModel()
    {
        // we assume this ctor is called from the UI thread!
        _syncContext = SynchronizationContext.Current;
    }

    // ...

    private void watcher_Changed(object sender, FileSystemEventArgs e)
    {
         _syncContext.Post(o => DGAddRow(crp.Protocol, ft), null);
    }
}

आपका बहुत बहुत धन्यवाद! स्वीकार किए जाने वाले समाधान को हर बार बुलाया जाने लगा, लेकिन यह काम करता है।
डोव

यह तब भी काम करता है जब एक असेंबली से बुलाया जाता है जिसमें व्यू मॉडल होता है लेकिन कोई "वास्तविक" डब्ल्यूपीएफ, यानी एक क्लास लाइब्रेरी नहीं है।
ओनूर

यह एक बहुत ही उपयोगी टिप है, विशेष रूप से जब आपके पास एक थ्रेड के साथ एक गैर-डब्ल्यूपीएफ घटक होता है जिसे आप मार्शल क्रियाएं करना चाहते हैं। बेशक एक और तरीका यह है कि टीपीएल निरंतरता का उपयोग करना होगा
MaYaN

मैं इसे पहली बार में समझ नहीं पाया, लेकिन यह मेरे लिए काम करता है .. एक अच्छा। (DGAddRow को एक निजी तरीका बताना चाहिए)
टिम डेविस

5

UI को किसी अन्य थ्रेड या बैकग्राउंड से बदलने के लिए [Dispatcher.Invoke (DispatcherPriority, Delegate)] का उपयोग करें ।

चरण 1 । निम्नलिखित नामस्थानों का उपयोग करें

using System.Windows;
using System.Threading;
using System.Windows.Threading;

चरण 2 । यूआई को अपडेट करने के लिए निम्नलिखित लाइन लगाएं

Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate
{
    //Update UI here
}));

वाक्य - विन्यास

[BrowsableAttribute(false)]
public object Invoke(
  DispatcherPriority priority,
  Delegate method
)

पैरामीटर

priority

प्रकार: System.Windows.Threading.DispatcherPriority

डिस्पैचर ईवेंट कतार में अन्य लंबित परिचालनों के सापेक्ष प्राथमिकता, निर्दिष्ट विधि लागू होती है।

method

प्रकार: System.Delegate

ऐसी विधि के लिए एक प्रतिनिधि जो कोई तर्क नहीं लेता है, जिसे डिस्पैचर ईवेंट कतार में धकेल दिया जाता है।

प्रतिलाभ की मात्रा

प्रकार: System.Object

यदि प्रतिनिधि के पास कोई वापसी मूल्य नहीं है, तो प्रतिनिधि से आह्वान या अशक्त होना।

संस्करण जानकारी

.NET फ्रेमवर्क 3.0 के बाद से उपलब्ध है

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