【问题标题】:Why in AsyncTaskManager PropertyChanged is always null?为什么在 AsyncTaskManager PropertyChanged 中始终为空?
【发布时间】:2014-04-15 17:18:46
【问题描述】:

我尝试实现类似于本文中介绍的模式: http://msdn.microsoft.com/en-us/magazine/dn605875.aspx

以下是我的问题描述:

在我看来,我已经设置:

<Button Content="LoadData" Command="{Binding LoadDataCommand}" />
<ListBox Grid.Row="1" x:Name="listBox" ItemsSource="{Binding DataSource.Result}"

然后在codeBehind中:

this.DataContext = new ProductViewModel();

然后在 ProductViewModel 中:

public AsyncTaskManager<ObservableCollection<Product>> DataSource { get; private set; }

与:

public ProductViewModel()
    {
        _loadDataCommand = new DelegateCommand(LoadDataAction);

    }
private void LoadDataAction(object p)
    {
        ProductRepository pRepository = new ProductRepository();
        DataSource = new AsyncTaskManager<ObservableCollection<Product>>(pRepository.LoadProducts());      
    }

并且在 AsyncTaskManager PropertyChanged 中始终为 null :( 为什么会这样?绑定有什么问题?

但是当我删除“加载数据”按钮和 _loadDataCommand 并简单地设置时

ProductRepository pRepository = new ProductRepository();
        DataSource = new AsyncTaskManager<ObservableCollection<Product>>(pRepository.LoadProducts());   

ProductViewModel 构造函数中,它的工作方式与示例中一样,但我希望用户能够使用构造函数中未启动的按钮调用加载数据:/

下面是 AsyncTaskManager 代码:

using System;
using System.ComponentModel;
using System.Threading.Tasks;

namespace PhoneClientApp.Models
{
    public sealed class AsyncTaskManager<TResult> : INotifyPropertyChanged
    {
    public AsyncTaskManager(Task<TResult> task)
    {
        Task = task;
        if (!task.IsCompleted)
        {
            var _ = WatchTaskAsync(task);
        }
    }

    private async Task WatchTaskAsync(Task task)
    {
        try
        {
            await task;
        }
        catch
        {
        }
        var propertyChanged = PropertyChanged;
        if (propertyChanged == null)
            return;
        propertyChanged(this, new PropertyChangedEventArgs("Status"));
        propertyChanged(this, new PropertyChangedEventArgs("IsCompleted"));
        propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted"));
        if (task.IsCanceled)
        {
            propertyChanged(this, new PropertyChangedEventArgs("IsCanceled"));
        }
        else if (task.IsFaulted)
        {
            propertyChanged(this, new PropertyChangedEventArgs("IsFaulted"));
            propertyChanged(this, new PropertyChangedEventArgs("Exception"));
            propertyChanged(this,
                new PropertyChangedEventArgs("InnerException"));
            propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage"));
        }
        else
        {
            propertyChanged(this,
                new PropertyChangedEventArgs("IsSuccessfullyCompleted"));
            propertyChanged(this, new PropertyChangedEventArgs("Result"));
        }
    }

    public Task<TResult> Task { get; private set; }

    public TResult Result
    {
        get
        {
            return (Task.Status == TaskStatus.RanToCompletion)
                ? Task.Result
                : default(TResult);
        }
    }

    public TaskStatus Status
    {
        get { return Task.Status; }
    }

    public bool IsCompleted
    {
        get { return Task.IsCompleted; }
    }

    public bool IsNotCompleted
    {
        get { return !Task.IsCompleted; }
    }

    public bool IsSuccessfullyCompleted
    {
        get
        {
            return Task.Status ==
                   TaskStatus.RanToCompletion;
        }
    }

    public bool IsCanceled
    {
        get { return Task.IsCanceled; }
    }

    public bool IsFaulted
    {
        get { return Task.IsFaulted; }
    }

    public AggregateException Exception
    {
        get { return Task.Exception; }
    }

    public Exception InnerException
    {
        get
        {
            return (Exception == null)
                ? null
                : Exception.InnerException;
        }
    }

    public string ErrorMessage
    {
        get
        {
            return (InnerException == null)
                ? null
                : InnerException.Message;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

}

【问题讨论】:

  • 因为它很可能在绑定设置之前完成。

标签: c# .net mvvm async-await


【解决方案1】:

请尝试创建最小的可重现代码集,并观察调试器中的输出窗口是否存在数据绑定错误。

以下代码对我来说很好用(在 LINQPad 中):

void Main()
{
    var context = new ParserContext();
    context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
    var xaml = @"<Grid><ListBox ItemsSource=""{Binding DataSource.Result}"" /></Grid>";
    var element = (FrameworkElement)XamlReader.Parse(xaml, context);
    element.DataContext = new ProductViewModel();

    PanelManager.StackWpfElement(element);
}

class ProductViewModel
{
    public ProductViewModel()
    {
        DataSource = new AsyncTaskManager<ObservableCollection<string>>(LoadProductsAsync());
    }

    private async Task<ObservableCollection<string>> LoadProductsAsync()
    {
        await Task.Delay(10000);
        return new ObservableCollection<string> { "first", "second", "third" };
    }

    public AsyncTaskManager<ObservableCollection<string>> DataSource { get; private set; }
}

列表框首先显示为空,然后在延迟后填充值。

【讨论】:

  • 好的,现在我知道问题出在哪里了。当我在 ProductViewModel 构造函数中设置 DataSource 时,一切正常,但是当我在 LoadDataAction 中设置它时,_loadDataCommand 绑定到按钮“加载数据”,然后 AsyncTaskManager PropertyChanged 为空。我编辑了我的问题,但我不明白为什么会这样:(
  • 您需要为 视图模型中发生更改的每个属性 提高 PropertyChanged。在这种情况下,您在 ProductViewModel.DataSource 属性上缺少它。
  • 好的,现在它可以工作了,当我在构造函数中设置 DataSource 时,我不必提高 PropertyChanged。
  • 当你在构造函数中设置它时,它永远不会改变,所以不需要PropertyChanged。如果你构造了viewmodel然后改变属性值,那么你需要提高PropertyChanged
猜你喜欢
  • 2014-06-15
  • 2011-10-13
  • 2010-12-03
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
  • 1970-01-01
  • 2017-03-12
相关资源
最近更新 更多