Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/271.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Windows应用商店应用程序UI更新_C#_Xaml_Windows Runtime_Windows Store Apps - Fatal编程技术网

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

C# Windows应用商店应用程序UI更新,c#,xaml,windows-runtime,windows-store-apps,C#,Xaml,Windows Runtime,Windows Store Apps,我正在为Windows 8编写一个Windows应用商店应用程序玩具应用程序。 它只有一个带有TextBlock的xaml页面。页面的MyTimer类为DataContext: this.DataContext = new MyTimer(); MyTimer实现了INotifyPropertyChanged,并且使用计时器更新属性Time: public MyTimer(){ TimerElapsedHandler f = new TimerElapsedHandler(Notify

我正在为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);
}

TextBlock
具有准时数据绑定功能

<TextBlock Text="{Binding Time}" />
带着信息

应用程序调用了为不同线程封送的接口。(来自HRESULT的异常:0x8001010E(RPC_E_错误_线程))

真正的问题是我正在更新MyTimer类的属性,而不是GUI本身,
我无法理解,但我认为解决方案应该使用类似的方式。

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

整个shebang看起来像这样:

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下)。

这个博客中的代码如何:

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

,非常感谢。这表明我每次使用异步调用时都需要使用SynchronizationContext…我也会考虑使用async关键字来执行类似操作。是的,如果您使用的是C#4.5的Wait关键字,您将默认得到此结果。谢谢您的回答!我只想添加一个旁注:如果您想在UI上下文中回调,请确保在UI构建之前捕获
SynchronizationContext
(我使用
OnLaunched
事件处理程序来完成),否则您获取的上下文将无法提供服务。进一步阅读:不是说这段代码不起作用,而是IME任务。延迟并没有很高的分辨率。在WPF、Win8和WP8测试中,我将CancellationTokenSource.CancelAfter设置为8分钟,而UI仅设置为7:56分钟。@Stonetip它的分辨率肯定比1秒高,我猜您的代码中还存在其他问题。另外,
Task.Delay()
不是
CacelAfter()
,尽管我希望它们具有相同的分辨率。Task.Delay的分辨率确实比1秒高,但如果您希望它以毫秒为单位,那么我在WPF、Win8和Win Phone 8上的测试显示出的不一致行为高达每分钟0.5秒。然而,我发现,对于后两种环境,ThreadPoolTimer类工作得非常好。
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"));
                });
        }
    }
}
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; } }
}