使用MongoDB的WPF C#异步MVVM

本文关键字:异步 MVVM WPF MongoDB 使用 | 更新日期: 2023-09-27 18:20:22

所以我的情况是:我希望能够使用MongoDB将MVVM与我的WPF应用程序一起使用。我对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的代码索引也是如此。

当我运行程序时,什么都不显示(即:窗口启动,但ListBox是空的,即使有数据)。我将很快添加一些按钮功能,这些功能将使用MongoDB执行原子操作。

近两周来,我一直在尝试制作自己的MVVM程序,但没有成功。如有任何协助,我们将不胜感激。

使用MongoDB的WPF C#异步MVVM

您没有将getUserList()返回值放入变量中

我想你的意思是做以下

Task.Run(async ()=>this.userList = await getUserList());

这将起作用——你应该考虑是否要等待任务完成,然后在它之后放置.Wait()

您的另一个问题可能是在应该使用StaticResource而不是绑定的上下文中绑定ViewModel的方式

像这样:

<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。但我更喜欢在Codebehin或app.xaml.cs.中为主窗口设置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>

我想赞扬gilMishalblindmes为我指明了正确的方向。你的两个答案都有帮助。这是我更新的(和功能!)代码:

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>