如何在后台获取数据并在准备好时异步更新UI数据?

时间:2017-09-26 08:29:16

标签: c# wpf mvvm async-await

我有选择对象DataGrid,其行选择已更改在方法中处理(使用交互事件触发器InvokeCommandAction)。一行代表一个真实的生活对象,并且有很多属性。

我首先设置一个属性,该行对应并在地图上突出显示对象,执行其他操作,最后调用异步方法获取对象属性并使用这些属性刷新属性DataGrid。

SelectedObjectsViewModel.cs(包含SelectedObjects DataGrid的UserControl的Viewmodel)

public void SelectedObjectsGridSelectionChangedCommand(object parameter)
{
    IList selectedRows = parameter as IList;
    if (selectedRows.Count == 1)
        ObjectProperties.Instance.SetAttributeObjectNoRefresh(((SelectedNetObjectBindingSource)selectedRows[0]).Data);
    else                         
        ObjectProperties.Instance.SetAttributeObjectNoRefresh(null);

     SetAttributeObjectExtraHightlight<SelectedNetObjectBindingSource>(selectedRows);

     DoSomeOtherStuff(selectedRows)

     App.Current.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
     {
         ((ObjectViewModel)MyApp.ViewModels["Shared.Panels.Object"]).SelectedObjectsChanged();
     }));
}

ObjectViewModel.cs(包含Properties DataGrid的UserControl的Viewmodel)

    // this is ItemsSource binding for properties datagrid 
    public ICollectionView ObjectInfoItems { get; private set; }

    public ObjectViewModel()
    {
         ObjectInfoItems = CollectionViewSource.GetDefaultView(ObjectProperties.Instance.GetAttributeObjectAttributes());
    }

    public void SelectedObjectsChanged()
    {
         // refreshed DataGrid
         ObjectInfoItems.Refresh();
    }

ObjectProperties.cs

    public void SetAttributeObjectNoRefresh(NetObject netObject)
    {
        _attributeObject = netObject;
    }

   public IEnumerable<PropertyRow> GetAttributeObjectAttributes()
    {
        if (_attributeObject != null)
        {
            List<string> fieldNames = new List<string>();
            List<string> visibleNames = new List<string>();
            List<string> values = new List<string>();
            /* get properties for _attributeObject from db and 
             * process in native code */
            var properties = GetPropertiesFilterAndSort(fieldNames, visibleNames, values, _attributeObject);

            foreach (var property in properties)
            {
                yield return property;
            }
        }
    }

然而,在完成其他所有内容(如突出显示)之后,包含对象属性的DataGrid最多会延迟1,2秒。我想立即开始获取我知道对象,继续突出显示等,最后异步启动SelectedObjectsChanged以使用fetched属性数据来刷新DataGrid。

提取涉及数据库提取和一些处理。根据具体情况和设置,数据库获取时间最多可达80%,但可能只有50%。

我的问题是:如何做到这一点:

  1. 所选对象DataGrid中的行选择和地图上的突出显示立即进行,而不是等待属性提取或属性数据网格刷新
  2. 选定对象数据网格选择之后的属性DataGrid刷新没有长时间延迟,因为它正在获取属性
  3. 谢谢!

1 个答案:

答案 0 :(得分:3)

您应该在后台线程上执行任何长时间运行的方法。请注意,您无法访问后台线程上的任何UI元素,因此您基本上需要启动一个新任务来获取数据,然后在任务完成后处理任何UI内容。

以下是在后台线程上执行GetAttributeObjectAttributes()方法的基本示例:

private ICollectionView _ojectInfoItems;
public ICollectionView ObjectInfoItems
{
    get { return myVar; }
    set { myVar = value; OnPropertyChanged(); }
}

public ObjectViewModel()
{
    Task.Factory.StartNew(() =>
    {
        return ObjectProperties.Instance.GetAttributeObjectAttributes();
    }).ContinueWith(task =>
    {
        ObjectInfoItems = CollectionViewSource.GetDefaultView(task.Result);
    }, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}

请注意,ObjectViewModel类应实现INotifyPropertyChanged接口,并在设置数据绑定PropertyChanged属性时引发ObjectInfoItems事件。

修改

基于您的评论的另一个例子:

public void SelectedObjectsGridSelectionChangedCommand(object parameter)
{
    object data = null;
    IList selectedRows = parameter as IList;
    if (selectedRows.Count == 1)
        data = (SelectedNetObjectBindingSource)selectedRows[0]).Data;

    Task.Factory.StartNew(() =>
    {
        ObjectProperties.Instance.SetAttributeObjectNoRefresh(null);

        Parallel.Invoke(
            () => SetAttributeObjectExtraHightlight<SelectedNetObjectBindingSource>(selectedRows),
            () => DoSomeOtherStuff(selectedRows));

    }).ContinueWith(task =>
    {
        ((ObjectViewModel)MyApp.ViewModels["Shared.Panels.Object"]).SelectedObjectsChanged();
    }, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}