【问题标题】:Asynchronous MVVM for WPF C# using MongoDB使用 MongoDB 的 WPF C# 的异步 MVVM
【发布时间】:2015-11-03 19:00:58
【问题描述】:

所以我的情况是这样的:我希望能够将 MVVM 与使用 MongoDB 的 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 及其代码隐藏保持不变,视图的代码隐藏也是如此。

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

我已经尝试了将近 2 周来为此制作自己的 MVVM 程序,但没有成功。任何帮助将不胜感激。

【问题讨论】:

  • 你的 userList 是否包含数据?
  • 是的。这是我从 MongoDB 调用的具有数据的“用户”列表。它由 ViewModel 中的“getUserList()”函数填充。
  • 你试过...调试吗? “什么都没有显示”并不是足够的信息。
  • 是的,我的意思是你看到它在调试器中填充了吗?
  • Will -- 我应该从哪里开始调试? “什么都没有出现”是指程序启动时 ListBox 是空的。抱歉没有详细说明。

标签: c# wpf mongodb xaml mvvm


【解决方案1】:

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

我假设您的意思是执行以下操作

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

这应该可行,您应该考虑是否要等待任务完成,然后在其后放置.Wait()

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

像这样:

<Grid DataContext="{StaticResource ViewModel}">

【讨论】:

  • 这需要我创建 getUserList() 和 awaitable 函数,并且我不能将它放入构造函数中(它们不是异步的或可等待的)。如果您检查 getUserList() 函数,您会发现它被设计为在运行构造函数时执行异步任务,并且它从本质上更新了 userList 属性。
  • 你可以将它放在一个任务中并从构造函数中运行它,但应该有更好的方法。无论如何,您遇到的问题很可能是您没有填充 userList
  • 谢谢...代码有效,但 ListBox 仍未填充。我尝试在 getUserList 和构造函数中进行调试,但似乎都没有达到。
  • 调试器到达了构造函数,但我在“this.UserLIst = new ObservableCollection();”处得到了目标调用异常...知道为什么会发生这种情况吗?
  • 你应该查看堆栈跟踪和/或内部异常以找出你得到它的原因,因为我现在看不出任何原因
【解决方案2】:
<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>

【讨论】:

    【解决方案3】:

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

    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>
    

    【讨论】:

    • 不要在构造函数中调用长时间运行的异步任务。考虑使用 NavigationService 和受 prism 启发的 INavigationAware 接口。见stackoverflow.com/questions/28457037/…
    • 查看其他答案的链接。它直接链接到 Prism 中的相关类(Prism 是微软开发的开源 MVVM 框架)。我建议在 Prism 工作。一旦你掌握了它,它就会非常强大,而且你对特定的其他 MVVM 框架没有任何限制
    猜你喜欢
    • 2019-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-12
    • 2017-05-21
    • 1970-01-01
    • 2016-01-20
    • 2010-12-05
    相关资源
    最近更新 更多