从TimerCallback函数Windows Phone 8更新UI线程 [英] Updating UI Thread from TimerCallback Function Windows Phone 8

查看:73
本文介绍了从TimerCallback函数Windows Phone 8更新UI线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这里有些难住.

我有一个TimerCallback,它每10秒触发一次,其中包含要放置在地图上的Geopoint.我尝试将它们从计时器回调函数中添加到地图中,但是由于它位于不同的线程中,因此无法执行此操作.我收到以下错误:

I have a TimerCallback that fires every 10 seconds which contains Geopoints to put on a map. I try to add these to the map from within the timer callback function, however since it is in a different thread, I am unable to do this. I receive the following errors:

类型为"System.IO.FileNotFoundException"的异常发生在 mscorlib.ni.dll,并且在受管理/本地边界之前未得到处理.

An exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.ni.dll and wasn't handled before a managed/native boundary.

类型为'System.UnauthorizedAccessException'的第一次机会异常 发生在System.Windows.ni.dll

A first chance exception of type 'System.UnauthorizedAccessException' occurred in System.Windows.ni.dll

我该如何规避?我以为也许添加一个NotifyCollectionChanged侦听器可以工作,但是我仍然遇到相同的问题.代码如下所示.

How would can I circumvent this? I thought that maybe added a NotifyCollectionChanged listener would work, however I am still having the same problem. Code is shown below.

    private ObservableCollection<Bus> _busList;
    private Timer _timer = null;
    public ItemViewModel route;

    public ObservableCollection<Bus> BusList
    {
        get { return _busList; }
        set { _busList = value; }
    }
    //public LocationManager locMan = LocationManager.Instance;
    // Constructor
    public DetailsPage()
    {
        InitializeComponent();

        // Sample code to localize the ApplicationBar
        //BuildLocalizedApplicationBar();
    }

    // When page is navigated to set data context to selected item in list
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        route = null;
        if (DataContext == null)
        {
            string selectedIndex = "";
            if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
            {
                int index = int.Parse(selectedIndex);
                DataContext = App.ViewModel.Items[index];
                route = App.ViewModel.Items[index];
                if (_timer == null)
                {
                    TimerCallback tcb = obtainJSON;
                    _timer = new Timer(tcb, route.RouteID, 0, 10000);
                }
                else
                {
                    _timer.Change(0, 10000);
                }
                if (BusList == null)
                {
                    BusList = new ObservableCollection<Bus>();
                }
                else
                {
                    BusList.Clear();
                }
                BusList.CollectionChanged += HandleBusAdded;
            }
        }
    }

    private void HandleBusAdded(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Reset)
        {
            Debug.WriteLine("Everything was cleared");
        }
        else
        {
            foreach (Bus item in e.NewItems)
            {
                Debug.WriteLine(item.vehicleID);
                Polygon polygon = new Polygon();
                polygon.Points.Add(new Point(0, 0));
                polygon.Points.Add(new Point(0, 75));
                polygon.Points.Add(new Point(25, 0));
                polygon.Fill = new SolidColorBrush(Colors.Blue);

                // Create a MapOverlay and add marker
                MapOverlay overlay = new MapOverlay();
                overlay.Content = polygon;
                overlay.GeoCoordinate = new GeoCoordinate(item.lat, item.lng);
                overlay.PositionOrigin = new Point(0.0, 1.0);
                MapLayer mapLayer = new MapLayer();
                mapLayer.Add(overlay);
                //mapView.Layers.Add(mapLayer);

                this.Dispatcher.BeginInvoke(() =>
                {
                    mapView.Layers.Add(mapLayer);
                });
            }
        }

    }

    public void obtainJSON(Object stateInfo)
    {

        string url = "http://xxx.xxx.xxx" + stateInfo.ToString();
        var client = new WebClient();
        client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(Decrypt);
        client.DownloadStringAsync(new Uri(url));
    }

    public void Decrypt(Object sender, DownloadStringCompletedEventArgs e)
    {
        if (BusList.Count > 0)
        {
            BusList.Clear();
        }
        if (!e.Cancelled && e.Error == null)
        {
            var temp = new List<string>();
            string[] buses = e.Result.Split('\n');
            foreach (string bus in buses)
            {
                if (!string.IsNullOrWhiteSpace(bus) && !string.IsNullOrEmpty(bus))
                {
                    temp.Add(bus);
                }
            }
            foreach (string item in temp)
            {
                string[] busInfo = item.Split(',');
                Bus newBus = new Bus(busInfo[0], busInfo[1], busInfo[2]);
                BusList.Add(newBus);

            }

            // This is where I initially tried
            /*Polygon polygon = new Polygon();
            polygon.Points.Add(new Point(0, 0));
            polygon.Points.Add(new Point(0, 75));
            polygon.Points.Add(new Point(25, 0));
            polygon.Fill = new SolidColorBrush(Colors.Blue);

            // Enable marker to be tapped for location information
            // polygon.Tag = new GeoCoordinate(BusList[0].lat, BusList[0].lng);

            // Create a MapOverlay and add marker
            MapOverlay overlay = new MapOverlay();
            overlay.Content = polygon;
            overlay.GeoCoordinate = new GeoCoordinate(BusList[0].lat, BusList[0].lng);
            overlay.PositionOrigin = new Point(0.0, 1.0);
            MapLayer mapLayer = new MapLayer();
            mapLayer.Add(overlay);

            this.Dispatcher.BeginInvoke(() =>
            {
                mapView.Layers.Add(mapLayer);
            });*/
            Debug.WriteLine("Present buses " + BusList[0].vehicleID + ", " + BusList[1].vehicleID);
        }
    }
}

推荐答案

最简单的方法是将obtainJSON方法中发生的事情包装在一个匿名函数中,该匿名函数被分派回UI线程:

The simplest thing would be to wrap what happens in your obtainJSON method in an anonymous function that is dispatched back to the UI thread:

public void obtainJSON(Object stateInfo)
{    
    Dispatcher.BeginInvoke(() => { 
        string url = "http://xxx.xxx.xxx" + stateInfo.ToString();
        var client = new WebClient();
        client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(Decrypt);
        client.DownloadStringAsync(new Uri(url));
    });    
}

这意味着所有事情(同时发出Web请求和处理响应,现在都在WebClient上执行它在被调用的线程上的回调)都发生在UI线程上,因此您无需更新绑定到的对象来自其他线程的UI线程.

This will mean that everything (both making the web request and processing the response as WebClient now executes it's callback on the thread it was called from) happens on the UI thread so you're not updating objects that are bound to the UI thread from a different thread.

您也可以尝试使用DispatcherTimer而不是线程化的线程.

You could also try using a DispatcherTimer rather than the threading one.

您还应注意处理恶劣的网络条件,在这种情况下,一个请求将在10秒钟后超时,并且最终可能会导致多个请求正在运行-这可能对您来说可能不是问题.

You should also take care to handle poor network conditions where a request will timeout after more than 10 seconds and you could end up with multiple requests running - which may or may not be an issue for you.

这篇关于从TimerCallback函数Windows Phone 8更新UI线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆