Dela via


WPF de Dispatcher kullanımı

Merhabalar,

Bildiğiniz üzere 'Windows Presentation Framework' ile oldukça göz alıcı uygulamalar yapmak mümkün. Tabii bunu gerçekleştirirken uygulamanın gelen isteklere uygun sürelerde yanıt vermesi de önemli. ‘WPF threading’ modelinde Kullanıcı arayüzünde tanımlanan nesneler, ‘UI thread’ e bağlılar, buna ‘thread affinity’ de deniliyor. UI Thread’i haricinde, son olarak arka planda işlev gören bir de ’‘rendering’ thread bulunuyor. ‘Rendering thread’ diğerine göre daha yüksek öncelikli olarak kullanıcı arayüzünü güncelliyor. Dolayısı ile daha hızlı tepki veren bir uygulama için ‘UI Thread’ de çok zaman alacak bir işi ‘non-UI’ bir ‘thread’ e taşıyabiliyoruz. Fakat bunun yapmanın bir doğru bir de yanlış methodu var. Öncelikle yanlış method dan bahsedelim:

'Windows Presentation Framework’ de 'Control', 'Window', 'Panel' gibi nesneler 'DependencyObject' ve bu nesnenin türediği 'DispatcherObject’ denilen başka bir nesneden türetiliyorlar. ‘DispatcherObject’ nesnesinin iki temel fonksiyonu: ‘CheckAccess’ ve ‘VerifyAccess’ ki bu metodlar üzerinde çalışılan thread’in ‘DispatcherObject’ nesnesine erişimi var mı bunu inceliyor. 'WPF' üzerinde bunun etkisi ise; herhangi bir 'non-UI' thread içerisinde bir UI elemanına erişirseniz ( dolayısı ile 'DispatcherObject' nesnesine) bu durumda aşağıdaki gibi bir ‘exception’ fırlatılıyor:

 System.ArgumentException: Must create DependencySource on same Thread as the DependencyObject.

at System.Windows.DependencyObject.ValidateSources(DependencyObject d, DependencySource[] newSources, Expression expr)
at System.Windows.Expression.ChangeSources(DependencyObject d, DependencyProperty dp, DependencySource[] newSources)
at System.Windows.Data.BindingExpressionBase.ChangeSources(DependencyObject target, DependencyProperty dp, WeakDependencySource[] newSources)
at System.Windows.Data.BindingExpressionBase.ChangeSources(WeakDependencySource[] newSources)
at System.Windows.Data.BindingExpression.ChangeWorkerSources(WeakDependencySource[] newWorkerSources, Int32 n)
at MS.Internal.Data.ClrBindingWorker.ReplaceDependencySources()
at MS.Internal.Data.ClrBindingWorker.NewValueAvailable(Boolean dependencySourcesChanged, Boolean initialValue, Boolean isASubPropertyChange)
at MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k, ICollectionView collectionView, Object newValue, Boolean isASubPropertyChange)
at MS.Internal.Data.ClrBindingWorker.ScheduleTransferOperation(Object arg)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)

Böyle bir exception örneğin aşağıdaki implementasyonda gerçekleşebilir:

 // The Work to perform on another thread

ThreadStart start = delegate()

{    // ... 

    // This will throw an exception

    // (it's on the wrong thread)

    statusText.Text = "From Other Thread";

};

 // Create the thread and kick it started!

new Thread(start).Start();

Bu kod yukarıdaki gibi 'exception' fırlatıyor çünkü ‘statusText.Text’ özellği 'UI thread’inden değil başka bir 'thread' içerisinden çağırılıyor. Dolayısı ile bu ‘exception’ı almamak için 'Dispatcher.BeginInvoke' (Asenkron çağrı) yada 'Dispatcher.Invoke' (senkron) fonksiyonunlarını kullanmalıyız. Aşağıda bu kullanımlar için örnek kod parçalarını bulabilirsiniz.

'Dispatcher.BeginInvoke()' kullanım örneği:

     // ...

     // This will work as its using the dispatcher

    DispatcherOperation op = Dispatcher.BeginInvoke(

        DispatcherPriority.Normal,

        new Action<string>(SetStatus),

        "From Other Thread (Async)");

   

    DispatcherOperationStatus status = op.Status;

    while (status != DispatcherOperationStatus.Completed)

    {

        status = op.Wait(TimeSpan.FromMilliseconds(1000));

        if (status == DispatcherOperationStatus.Aborted)

        {

            // Alert Someone

        }

    }

 'Dispatcher.Invoke()' kullanım örneği:

  // ...

   // Sets the Text on a TextBlock Control.

  // This will work as its using the dispatcher

  Dispatcher.Invoke(DispatcherPriority.Normal,

                    new Action<string>(SetStatus),

                    "From Other Thread");

};

// Create the thread and kick it started!

 

İyi çallışmalar dilerim,

Mert Öztürk