WPF:在.NET 3.5

时间:2017-07-13 07:51:36

标签: c# wpf listview asynchronous delegates

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

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

在从数据库请求数据并将其加载到窗口" LvWindow"中的列表视图的过程中,我展示了一个泼溅的说法"正在加载"在它上面(实际上这是一个矩形,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
}

所以问题是对数据库的调用正在返回&#34; RequestDataToDatabase&#34;返回UserData(ObservableCollection)的集合,以便抛出异常。

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

最终解决方案

正如XAMIMAX在评论中所说:

  • 将签名从static void UIThread(Action a)更改为static void UIThread(Action a)
  • 修改UIThread((p)=&gt; LstUsers = result);通过UIThread(()=&gt; LstUsers =结果);

3 个答案:

答案 0 :(得分: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构造函数) )。

答案 1 :(得分:0)

答案 2 :(得分:0)

以下是您可能采用的解决方案之一 在您的视图模型中,您将拥有以下内容:

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);
        }
    }
}