我有一个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在评论中所说:
这是您可能的解决方案之一。 在您的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);
}
}
}
由于您无法在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构造函数)。