WPF'de UI (Ana) İş Parçacığına güvenle erişme


99

İzlediğim bir günlük dosyası her gün güncellendiğinde (yeni metinle eklenir) aşağıdaki şekilde datagridimi güncelleyen bir uygulamam var:

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.Add (ds) 'yi çalıştırmayı denediğimde, ayrı bir iş parçacığı oluşturduğundan, FileWatcher için olay başlatıldığında; yeni satırı eklemek için, program hata ayıklama modu sırasında herhangi bir uyarı verilmeden çöker.

Winforms'ta bu, Invoke işlevini kullanarak kolayca çözüldü, ancak WPF'de bunu nasıl yapacağımdan emin değilim.

Yanıtlar:


205

Kullanabilirsiniz

Dispatcher.Invoke(Delegate, object[])

ilgili Application'in (ya da herhangi bir UIElement' ler) memuru.

Örneğin şu şekilde kullanabilirsiniz:

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

veya

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

Yukarıdaki yaklaşım bir hata veriyordu çünkü Application.Current, hattı çalıştırırken null. Durum neden böyle olsun?
l46kok

Bunun için herhangi bir UIElement'i kullanabilirsiniz, çünkü her UIElement "Dağıtıcı" özelliğine sahiptir.
Wolfgang Ziegler

1
@ l46kok Bunun farklı nedenleri olabilir (konsol uygulaması, winformlardan barındırma vb.). @WolfgangZiegler'in dediği gibi, bunun için herhangi bir UIElement kullanabilirsiniz. Application.CurrentBana daha temiz göründüğü için genellikle kullanırım .
Botz3000

@ Botz3000 Sanırım burada bazı yarış durumu sorunlarım var. Yukarıda verilen kodu ekledikten sonra, hata ayıklama moduna girdiğimde ve manuel olarak geçiş yaptığımda kod mükemmel çalışıyor, ancak uygulamayı hata ayıklama olmadan çalıştırdığımda kod çöküyor. Soruna neden olan burada neyi kilitleyeceğimi bilmiyorum.
l46kok

1
@ l46kok Eğer bir kilitlenme olduğunu düşünüyorsanız, ayrıca arayabilirsiniz Dispatcher.BeginInvoke. Bu yöntem, temsilciyi yürütme için sıraya alır.
Botz3000

52

Bunu yapmanın en iyi yolu SynchronizationContext, UI iş parçacığından bir almak ve kullanmaktır. Bu sınıf, diğer iş parçacıklarına sıralama çağrılarını özetler ve testi kolaylaştırır (WPF'leri Dispatcherdoğrudan kullanmanın aksine ). Örneğin:

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);
    }
}

Çok teşekkürler! Kabul edilen çözüm her çağrıldığında asılmaya başladı ama bu işe yarıyor.
Dov

Görünüm modelini içeren ancak "gerçek" WPF içermeyen bir derlemeden çağrıldığında da çalışır, yani bir sınıf kitaplığıdır.
Onur

Bu, özellikle eylemleri sıralamak istediğiniz bir iş parçacığına sahip wpf olmayan bir bileşeniniz olduğunda çok yararlı bir ipucudur. elbette bunu yapmanın başka bir yolu da TPL devam
ettirmelerini

İlk başta anlamadım, ama benim için çalıştı .. güzeldi. (DGAddRow'un özel bir yöntem olduğunu belirtmelidir)
Tim Davis

5

Kullanım [Dispatcher.Invoke (DispatcherPriority, temsilci)] bir parçacığı ya da arka plan UI değiştirmek için kullanılır.

1. Adım . Aşağıdaki ad alanlarını kullanın

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

2. Adım . Kullanıcı arayüzünü güncellemeniz gereken yere aşağıdaki satırı koyun

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

Sözdizimi

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

Parametreler

priority

Tür: System.Windows.Threading.DispatcherPriority

Dispatcher olay kuyruğundaki diğer bekleyen işlemlere göre öncelik, belirtilen yöntem çağrılır.

method

Tür: System.Delegate

Dispatcher olay kuyruğuna gönderilen bağımsız değişken almayan bir yönteme temsilci.

Geri dönüş değeri

Tür: System.Object

Temsilcinin dönüş değeri yoksa, çağrılan temsilcinin dönüş değeri veya null.

Versiyon bilgisi

.NET Framework 3.0'dan beri mevcuttur

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.