WPF:在.NET 3.5中异步执行一些代码

问题描述 投票:1回答:3

我有一个MVVM WPF应用程序。我有一个窗口,让我们说“LvWindow”,其中列表视图是从数据库中传入的数据加载的。从主窗口“MainWindow”,我有一个菜单,有一些选项。当我选择访问“LvWindow”的选项时,它是打开的。然后从ViewModel,在构造函数中,我调用了一个数据库,我从中请求了一些数据,然后我加载到listview中。

我的目标是使进程从数据库请求数据,然后在listview中异步加载它。我想要这个是为了不阻止整个应用程序,我的意思是,在这个窗口加载时,用户可以转到主窗口菜单并选择打开另一种类型的窗口。 Windows在标签页中打开。

在从数据库中请求数据并在窗口“LvWindow”中加载到listview的过程中,我在其上显示了一个“正在加载”的说法(实际上这是一个矩形,zindex设置为更高的数字,以避免用户可以与listview交互直到它完全加载)。当列表视图加载来自数据库的数据时,将关闭此启动。

因此,为了使进程异步,我知道在winforms中可以使用beginInvoke,endInvoke和callbacks方法完成委托,请参阅here

另外,另一种可能性是使用后台工作者,例如发布的here

那么在WPF中这是最好的方法吗?使用代表作为winforms或后台工作者?

ATTEMPT#1:我尝试过XANIMAX解决方案:

public class TestViewModel : BaseViewModel
{
    private static Dispatcher _dispatcher;
    public ObservableCollection<UserData> lstUsers

    public ObservableCollection<UserData> LstUsers
    {
        get
        {
            return this.lstUsers;
        }

        private set
        {
            this.lstUsers= value;
            OnPropertyChanged("LstUsers");
        }
    }

    public TestViewModel()
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback((o) =>
        {
            var result = getDataFromDatabase();
            UIThread((p) => LstUsers = result);
        }));
    }

    ObservableCollection<UserData> getDataFromDatabase()
    {            
        return this.RequestDataToDatabase();
    }

    static void UIThread(Action<object> a)
    {
        if(_dispatcher == null) _dispatcher = Dispatcher.CurrentDispatcher;
        //this is to make sure that the event is raised on the correct Thread
        _dispatcher.Invoke(a); <---- HERE EXCEPTION IS THROWN
    }
}

但在行_dispatcher.Invoke(a)中抛出异常:

TargetParameterCountException: the parameter count mismatch

UserData是我的数据模型,它是一个具有一些公共属性的类。就像是:

public class UserData
{
   public string ID{ get; set; }
   public string Name { get; set; }
   public string Surname { get; set; }

   // Other properties
}

所以问题是对数据库的调用返回“RequestDataToDatabase”正在返回UserData(ObservableCollection)的集合,因此引发了异常。

我不知道如何解决它。请问你能帮帮我吗?

最终盐:

正如XAMIMAX在评论中所说:

  • 将签名从静态void UIThread(Action a)更改为static void UIThread(Action a)
  • 修改UIThread((p)=> LstUsers = result);通过UIThread(()=> LstUsers = result);
c# wpf listview asynchronous delegates
3个回答
0
投票

这是您可能的解决方案之一。 在您的View模型中,您将拥有以下内容:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;

namespace VM
{
    public class TestViewModel : BaseViewModel
    {
        private static Dispatcher _dispatcher;
        List<object> ListToDisplay { get; set; }//INPC omitted for brevity
        public TestViewModel()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback((o) =>
            {
                var result = getDataFromDatabase();
                UIThread(() => ListToDisplay = result);
            }));
        }

        List<object> getDataFromDatabase()
        {
            //your logic here
            return new List<object>();
        }

        static void UIThread(Action a)
        {
            if(_dispatcher == null) _dispatcher = Dispatcher.CurrentDispatcher;
            //this is to make sure that the event is raised on the correct Thread
            _dispatcher.Invoke(a);
        }
    }
}

1
投票

由于您无法在C#7.0中的构造函数中等待异步方法(但是异步Main将在7.1中出现),您可以将异步函数调用提取到ViewModel中的单独函数,并在View的代码隐藏构造函数中同步调用它。您已创建ViewModel并将其分配给View的DataContext:

public MainWindow()
{
    this.vm = new MyViewModel();
    this.DataContext = this.vm;
    this.InitializeComponent();
    this.vm.AsychronousFunctionToCallDatabase();
}

正如XAMIMAX所说,您希望实现ViewModel来处理View和模型之间的业务逻辑。然后,如果您的ViewModel实现了INotifyPropertyChanged,并且您在XAML中将Binding设置为ViewModel中的属性 - 那么在数据库调用之后,显示将刷新而不会阻止UI线程。注意,如果您从数据库调用填充了任何集合,那么它们应该是ObservableCollection类型。

但正如Kundan所说,在你的AsychronousFunctionToCallDatabase()函数中,你应该在调用数据库的行上包含一个await语句或者创建一个Task - 这会将控制权返回给调用函数(在这种情况下,返回到MainWindow构造函数)。


© www.soinside.com 2019 - 2024. All rights reserved.