2012-03-29 24 views
7

Windows 8 için bir Windows Store App oyuncak uygulaması yazıyorum. TextBlock ile yalnızca bir xaml sayfası var.Windows Mağazası App UI güncelleştirmesi

this.DataContext = new MyTimer(); 

MyTimerINotifyPropertyChanged uygular ve mal Time güncellenmesi bir zamanlayıcı ile yapılır:

public MyTimer(){ 
    TimerElapsedHandler f = new TimerElapsedHandler(NotifyTimeChanged); 
    TimeSpan period = new TimeSpan(0, 0, 1); 
    ThreadPoolTimer.CreatePeriodicTimer(f, period); 
} 

TextBlock sahiptir

private void NotifyTimeChanged(){ 
    if (this.PropertyChanged != null){ 
     this.PropertyChanged(this, new PropertyChangedEventArgs("Time")); 
    } 
} 

ile sayfa DataContext olarak sınıf MyTimer vardır Zaman

adresindeki bir veritabanında
<TextBlock Text="{Binding Time}" /> 

Ben şu istisna var uygulamayı çalıştırdığınızda: mesajın asıl sorun Sınıf MyTimer özelliği güncelleştiriyorum olmasıdır

The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

ile

System.Runtime.InteropServices.COMException was unhandled by user code 

, GUI'nin kendisi değil, Bunu çözemiyorum ama çözümün this one gibi bir şey kullanması gerektiğini düşünüyorum.

cevap

7

Evet, özellik değişikliklerini UI iş parçacığı yerine bir iş parçacığı havuzu iş parçacığından bildiriyorsunuz. Zamanlayıcı geriçağırımındaki UI iş parçacığına bildirimi yeniden başlatmanız gerekir. Şimdi, görünüm modeliniz görünümünüzden ayrılır (iyi bir şey), bu nedenle Dispatcher altyapısına doğrudan bir bağlantısı yoktur. Yani yapmak istediğin, iletişim kurması gereken uygun SynchronizationContext olduğunu. Bunu yapmak için, yapım sırasında geçerli SynchronizationContext'u yakalamanız veya testler için iyi olan veya başlangıç ​​için UI iş parçacığından nesneyi başlatıyorsanız, bir kurucuya açıkça iletilmesine izin vermeniz gerekir.

tümü şöyle görünecektir: Üzerinde kurucu çağırırsanız

class MyTimer : INotifyPropertyChanged 
{ 
    public MyTimer() 
    { 
     Start(); 
    } 

    private async void Start() 
    { 
     while (true) 
     { 
      await Task.Delay(TimeSpan.FromSeconds(1)); 
      PropertyChanged(this, new PropertyChangedEventArgs("Time")); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged = delegate { }; 

    public DateTime Time { get { return DateTime.Now; } } 
} 

: bir döngü içinde Task.Delay() bekleyen yerine bir zamanlayıcı kullanıyor bunu

public class MyTimer 
{ 
    private SynchronizationContext synchronizationContext; 

    public MyTimer() : this(SynchronizationContext.Current) 
    { 
    } 

    public MyTimer(SynchronizationContext synchronizationContext) 
    { 
     if(this.synchronizationContext == null) 
     { 
      throw new ArgumentNullException("No synchronization context was specified and no default synchronization context was found.") 
     } 

     TimerElapsedHandler f = new TimerElapsedHandler(NotifyTimeChanged); 
     TimeSpan period = new TimeSpan(0, 0, 1); 
     ThreadPoolTimer.CreatePeriodicTimer(f, period); 
    } 

    private void NotifyTimeChanged() 
    { 
     if(this.PropertyChanged != null) 
     { 
      this.synchronizationContext.Post(() => 
       { 
        this.PropertyChanged(this, new PropertyChangedEventArgs("Time")); 
       }); 
     } 
    } 
} 
+0

Çok teşekkürler. Bu, eşzamansız bir çağrı kullandığımda SynchronizationContext'i kullanmam gerektiğini önerir. Async anahtar kelimesiyle böyle bir şey yapmayı düşüneceğim. – Gabber

+0

Evet, C# 4.5'ün anahtar kelimesini bekliyorsanız, bunu varsayılan olarak alacaksınız. –

+0

Cevabınız için teşekkürler!Ben sadece bir sidenote eklemek istiyorum: Eğer UI bağlamında geri aramak istiyorsanız, UI inşa edilmiş olandan daha kısa bir sürede "SynchronizationContext" 'i yakaladığınızdan emin olun (bunu yapmak için 'OnLaunched' olay işleyicisini kullanıyorum), aksi halde Kaptığın bağlam hizmet etmeyecek. Daha fazla bilgi için: http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I – Lvsti

5

Tek yön UI iş parçacığı, o da orada PropertyChanged çağırır. Ve güzel olan şey aynı kodun tam olarak WPF'de de çalışacağıdır (.Net 4.5 ve C# 5 altında).

+0

Bu kodun çalışmayacağını söylemiyor ancak IME Task.Delay'ın çok yüksek çözünürlüğü yok. WPF, Win8 ve WP8 testlerinde, 8 dakikaya kadar bir CancellationTokenSource.CancelAfter ayarladım ve UI sadece 7:56 dakikaya ulaştı. – Stonetip

+0

@Stonetip Kesinlikle 1 saniyeden daha iyi bir çözünürlüğe sahip olmalı, tahminimce kodunuzda başka bir şey oluyor. Ayrıca, Task.Delay() '' CacelAfter() 'değil, aynı çözünürlüğe sahip olmalarını beklerim. – svick

+0

Task.Delay, 1 saniyeden daha iyi bir çözünürlüğe sahip ancak milisaniye cinsinden noktaya gelmesini beklersiniz, daha sonra WPF, Win8 ve Win Phone 8'deki testlerim dakikada 5 saniyeye kadar tutarsız bir davranış gösterir. . Bununla birlikte, ThreadPoolTimer sınıfının harika çalıştığı son iki ortam için buldum. – Stonetip

İlgili konular