异步任务中的ObservableCollection

时间:2015-05-15 18:45:15

标签: c# mvvm task-parallel-library

我在任务中有一个ObservableCollection的奇怪行为。

这样可行:

    private string status;
    public string Status
    {
        get { return status; }
        set { SetField<string>(ref status, value); }
    }

    public void AddRandomTask()
    {
        Task t = Task.Run(async () =>
        {
            await Randomizer.Instance.LongRandAsync (new MainViewModelObserver(this));
                });

                t.ContinueWith(m => 
                    {
                        string s = "done: " + DateTime.Now.ToString("HH:mm:ss");
                        Status = s;
                    }, TaskScheduler.FromCurrentSynchronizationContext());

                Tasks.Add(t);       

                Msg.Add("Task is added");
            }

这失败了2个例外:

在构造函数

Msg = new ObservableCollection<string>();

并进一步:

public ObservableCollection<string> Msg { get; private set; }

public void AddRandomTask()
        {
            Task t = Task.Run(async () =>
            {
                await Randomizer.Instance.LongRandAsync(new MainViewModelObserver(this));
            });

            t.ContinueWith(m => 
                {
                    string s = "done: " + DateTime.Now.ToString("HH:mm:ss");
                    this.Msg.Add(s);
                }, TaskScheduler.FromCurrentSynchronizationContext());

            Tasks.Add(t);       

            Msg.Add("Task is added");
        }

我缺少什么,为什么字符串属性与ObservableCollection不同?

编辑:例外:

A first chance exception of type 'System.NotSupportedException' occurred in PresentationFramework.dll
A first chance exception of type 'System.NotSupportedException' occurred in mscorlib.dll

2 个答案:

答案 0 :(得分:1)

如果没有最小的,可重复的例子,很难准确地说出发生了什么;但我怀疑问题是你的代码正在改变一个数据绑定到UI元素的可观察集合。

作为一般规则,我总是将我的ViewModel属性(包括可观察集合)视为具有UI亲和力。

  

为什么字符串属性与ObservableCollection不同?

我认为你的意思是你发现你可以从后台线程更改字符串属性并让它工作,而从后台线程更改/// <summary> /// Represents the JSONNode class. /// </summary> public class JSONNode { /// <summary> /// Initializes a new instance of the <see cref="JSONNode"/> class. /// </summary> /// <param name="name">The name of the node.</param> /// <param name="value">The value of the node.</param> public JSONNode(string name, string value) { this.Name = name; this.Value = value; this.Children = new Dictionary<string, JSONNode>(); } /// <summary> /// Initializes a new instance of the <see cref="JSONNode"/> class. /// </summary> /// <param name="name">The name of the node.</param> public JSONNode(string name) : this(name, string.Empty) { } /// <summary> /// Gets the name of the node. /// </summary> public string Name { get; private set; } /// <summary> /// Gets the children of the node. /// </summary> public Dictionary<string, JSONNode> Children { get; private set; } /// <summary> /// Gets the value of the node. /// </summary> public string Value { get; private set; } /// <summary> /// Inserts a new node in the corresponding hierarchy. /// </summary> /// <param name="keyHierarchy">A list with entries who specify the hierarchy.</param> /// <param name="value">The value of the node.</param> /// <exception cref="System.ArgumentNullException">Is thrown if the keyHierarchy is null.</exception> /// <exception cref="System.ArgumentException">Is thrown if the keyHierarchy is empty.</exception> public void InsertInHierarchy(List<string> keyHierarchy, string value) { if (keyHierarchy == null) { throw new ArgumentNullException("keyHierarchy"); } if (keyHierarchy.Count == 0) { throw new ArgumentException("The specified hierarchy list is empty", "keyHierarchy"); } // If we are not in the correct hierarchy (at the last level), pass the problem // to the child. if (keyHierarchy.Count > 1) { // Extract the current hierarchy level as key string key = keyHierarchy[0]; // If the key does not already exists - add it as a child. if (!this.Children.ContainsKey(key)) { this.Children.Add(key, new JSONNode(key)); } // Remove the current hierarchy from the list and ... keyHierarchy.RemoveAt(0); // ... pass it on to the just inserted child. this.Children[key].InsertInHierarchy(keyHierarchy, value); return; } // If we are on the last level, just insert the node with it's value. this.Children.Add(keyHierarchy[0], new JSONNode(keyHierarchy[0], value)); } /// <summary> /// Gets the textual representation of this node as JSON entry. /// </summary> /// <returns>A textual representaiton of this node as JSON entry.</returns> public string ToJSONEntry() { // If there is no child, return the name and the value in JSON format. if (this.Children.Count == 0) { return string.Format("\"{0}\":\"{1}\"", this.Name, this.Value); } // Otherwise there are childs so return all of them formatted as object. StringBuilder builder = new StringBuilder(); builder.AppendFormat("\"{0}\":", this.Name); builder.Append(this.ToJSONObject()); return builder.ToString(); } /// <summary> /// Gets the textual representation of this node as JSON object. /// </summary> /// <returns>A textual representaiton of this node as JSON object.</returns> public string ToJSONObject() { StringBuilder builder = new StringBuilder(); builder.Append("{"); foreach (JSONNode value in this.Children.Values) { builder.Append(value.ToJSONEntry()); builder.Append(","); } builder.Remove(builder.Length - 1, 1); builder.Append("}"); return builder.ToString(); } } 会引发异常。好吧,WPF通过在幕后为简单属性做一些自动线程编组来尝试让事情变得简单。收藏品的最新版本also supports some level of this也是。

但我建议你不要使用它。首先,WPF是唯一支持此功能的XAML平台AFAIK。 Windows应用商店,通用应用,Xamarin Forms,......没有其他人可以,即使是简单的属性。此外,保持您的UI(以及您的UI的逻辑表示 - 您的ViewModel)与后台处理代码分开将使您的代码更易于移植,同时鼓励关注点分离,并且更简洁,更简单。

因此,IMO最好的方法就是不从后台线程更新ViewModels。在你的情况下,看起来ObservableCollection似乎是多余的,所以这是一个相对容易的修复:

Task.Run

另请注意,使用单独的public void AddRandomTask() { Task t = ProcessAsync(); Tasks.Add(t); Msg.Add("Task is added"); } private async Task ProcessAsync() { await Randomizer.Instance.LongRandAsync(new MainViewModelObserver(this)); string s = "done: " + DateTime.Now.ToString("HH:mm:ss"); this.Msg.Add(s); } 方法,无需使用明确的async / ConfigureAwait

答案 1 :(得分:0)

它没有失败,也不是一个错误,它是一个功能。

  

第一次机会异常是否意味着我的代码存在问题?

     

第一次机会异常消息通常并不意味着有一个   代码中的问题。适用于处理的应用程序/组件   优雅的异常,第一次机会异常消息让   开发人员知道遇到异常情况并且是   处理。

http://blogs.msdn.com/b/davidklinems/archive/2005/07/12/438061.aspx