[英]Asynchronous MVVM for WPF C# using MongoDB
所以我的情況是這樣的:我希望能夠使用MongoDB在我的WPF應用程序中使用MVVM。 我對MVVM非常陌生(我了解的很少),但是我對使用.NET和WPF有一定的經驗。
我有一個用於調用MongoDB集合的名稱空間,其中的Model組件作為一個名為“ User”的類存儲在該名稱空間中
模型(在單獨的命名空間中):
public class User
{
[BsonElement("_id")]
public ObjectId Id { get; set; }
public string name { get; set; }
// other methods listed here
public async static Task<List<User>> getUserList()
{
// allows me to get a list of users
var col = MongoDBServer<User>.openMongoDB("Users");
var filter = Builders<User>.Filter.Exists("name");
List<User> userList = await col.Find(filter).ToListAsync();
return userList;
}
}
我創建了一個非常基本的ViewModelBase(抽象ViewModelBase):
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if(handler == null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
還有一個用於處理用戶列表的派生類(ViewModel):
public class UserListViewModel : ViewModelBase
{
private User _user;
private ObservableCollection<User> _userList;
public User user
{
get { return _user; }
set
{
_user = value;
OnPropertyChanged("user");
}
}
public ObservableCollection<User> userList
{
get { return _userList; }
set
{
_userList = value;
OnPropertyChanged("userList");
}
}
public UserListViewModel()
{
user = new User();
this.userList = new ObservableCollection<User>();
// since MongoDB operations are asyncrhonous, the async method "getUserList()" is used to fill the observable collection
getUserList().Wait();
}
public async Task getUserList()
{
var UserListRaw = await User.getUserList();
this.userList = new ObservableCollection<User>(UserListRaw);
}
}
視圖組件是帶有列表框的簡單窗口,如下所示(視圖):
<Window x:Class="UserManagementMVVM.UsersWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UserManagementMVVM"
mc:Ignorable="d"
Title="UsersWindow" Height="300" Width="300">
<Window.Resources>
<local:UserListViewModel x:Key="ViewModel"/>
<!-- Receiving error for this XAML block saying "Object reference not set to instance of an object -->
</Window.Resources>
<Grid DataContext="{Binding ViewModel}">
<ListBox Margin="5" ItemsSource="{Binding userList}"/>
</Grid>
</Window>
App.Xaml及其背后的代碼以及View的背后代碼均保持不變。
當我運行該程序時,什么都沒有顯示(即:Window啟動,但是即使有數據,ListBox也為空)。 我將很快添加一些按鈕功能,這些功能將對MongoDB執行原子操作。
我已經嘗試了將近2個星期來為此制作自己的MVVM程序,但沒有成功。 任何幫助將不勝感激。
您沒有將getUserList()返回值放入變量中
我認為您的意思是執行以下操作
Task.Run(async ()=>this.userList = await getUserList());
這應該可以工作,您應該考慮是否要等待任務完成,而不是在任務之后放置.Wait()
。
您的另一個問題可能是您在上下文中綁定到ViewModel的方式,它應該使用StaticResource而不是綁定
像這樣:
<Grid DataContext="{StaticResource ViewModel}">
<Window x:Class="UserManagementMVVM.UsersWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UserManagementMVVM"
mc:Ignorable="d"
Title="UsersWindow" Height="300" Width="300">
<Window.DataContext>
<!--You have to set the DataContext -->
<local:UserListViewModel x:Key="ViewModel"/>
</Window.DataContext>
<Grid>
<ListBox Margin="5" ItemsSource="{Binding userList}"/>
</Grid>
</Window>
您必須設置DataContext權限。 我改變了你的XAML。 但是我更喜歡在Codebehind或app.xaml.cs中為Mainwindow設置DataContext。
例如:app.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
var data = new MainWindowViewmodel();
this.MainWindow = new MainWindow(data);
this.MainWindow.Show();
}
我的視圖的所有其他DataContext是通過ResourceDictionary中的DataTemplates完成的
<DataTemplate DataType="{x:Type local:MyOtherViewmodel}">
<local::MyOtherViewmodelView />
</DataTemplate>
我要贊揚gilMishal和blindmeis為我指明了正確的方向。 您的回答都對您有所幫助。 這是我更新的(和功能!)代碼:
App.xaml.cs進行了如下修改(貸記為blindmeis ):
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
UsersWindow window = new UsersWindow();
var ViewModel = new UserListViewModel();
window.DataContext = ViewModel;
window.Show();
}
}
ViewModel已更新:
public class UserListViewModel : ViewModelBase
{
private User _user;
private ObservableCollection<string> _userList; // changed from "User" class to string
public User user
{
get { return _user; }
set
{
_user = value;
OnPropertyChanged("user");
}
}
public ObservableCollection<string> userList
{
get { return _userList; }
set
{
_userList = value;
OnPropertyChanged("userList");
}
}
public UserListViewModel()
{
userList = new ObservableCollection<string>();
Task.Run(async () => this.userList = await getUserList()); // Credit to gilMishal
}
public async Task<ObservableCollection<string>> getUserList()
{
var UserListRaw = await User.getUserList();
var userListOC = new ObservableCollection<string>();
foreach (var doc in UserListRaw) // extracting the "name" property from each "User" object
{
userListOC.Add(doc.name);
}
return userListOC;
}
}
和視圖:
<Window x:Class="UserManagementMVVM.UsersWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UserManagementMVVM"
mc:Ignorable="d"
Title="UsersWindow" Height="300" Width="300">
<Window.Resources>
<local:UserListViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid> <!-- data context removed from here, credit blindmeis -->
<ListBox Margin="5" ItemsSource="{Binding userList}"/>
</Grid>
</Window>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.