Windows应用商店应用程序UI更新

本文关键字:UI 更新 应用程序 应用 Windows | 更新日期: 2023-09-27 18:29:22

我正在为Windows 8编写一个Windows应用商店应用程序玩具应用程序。它只有一个带有TextBlock的xaml页面。页面的MyTimer类为DataContext:

this.DataContext = new MyTimer();

MyTimer实现INotifyPropertyChanged,属性Time的更新是用定时器进行的

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

带有

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

TextBlock在时间上具有数据绑定

<TextBlock Text="{Binding Time}" />

当我运行应用程序时,我有以下异常:

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

带有消息

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

真正的问题是我正在更新类MyTimer的属性,而不是GUI本身,我想不通,但我认为解决方案应该使用这样的东西。

Windows应用商店应用程序UI更新

是的,您从线程池线程而不是UI线程通知属性更改。您需要在计时器回调中将通知封送回UI线程。现在,您的视图模型与视图是分离的(这是一件好事),因此它没有到Dispatcher基础设施的直接链接。所以你想做的是交给它一个合适的SynchronizationContext来进行交流。要做到这一点,您需要在构建过程中捕获当前的SynchronizationContext,或者允许它显式传递给构造函数,这对测试很有好处,或者如果您从UI线程开始初始化对象。

整个爆炸看起来像这样:

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

一种方法是在循环中等待Task.Delay(),而不是使用计时器:

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

如果在UI线程上调用构造函数,它也会在那里调用PropertyChanged。好的是,完全相同的代码也可以在WPF中工作(在.Net 4.5和C#5下)。

这个博客的代码怎么样:

http://metrowindows8.blogspot.in/2011/10/metro-tiles.html

这对我很有效。我必须将ThreadPoolTimer对象传递给我的委托函数