65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Entity Framework 的基本 WPF – MVVM 项目教程

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (6投票s)

2015 年 2 月 18 日

CPOL

3分钟阅读

viewsIcon

33426

downloadIcon

885

这是“使用 Entity Framework 的基本 WPF – MVVM 项目教程”的替代方案。

引言

Richard Protzel 创建的教程是使用 WPF 中的 Entity Framework 的一个很好的例子,但是像大多数类似例子一样 - 它不是为扩展到大型数据集而设计的。

本文的想法是展示如何采用一个简单的例子,并通过放入 VirtualObservableCollection,最终得到一个可以扩展到几乎任何数据集大小的系统。

背景

这是在以下教程的扩展:https://codeproject.org.cn/Articles/873592/Tutorial-for-a-Basic-WPF-MVVM-Project-Using-Entity

使用 VirtualizingObservableCollection,请参见:https://codeproject.org.cn/Articles/874363/Virtualizing-Data-with-XAML

使用代码

放入 VirtualizingObservableCollection 非常简单,只需为数据定义一个提供程序,而不是“填充”列表,而是使用提供程序返回 VirtualizingObservableCollection,而不是返回填充的列表。

在本例中,我们有两个列表 - 一个是作者列表,另一个是该作者的书籍列表。

在我们的例子中,我们将使用分页系统创建一个作者提供程序。 这样,我们一次只加载小块,而不是每次都等待整个列表完全加载。

如果您已从第一篇文章 (Richards) 下载了该项目,我们将定义一个返回作者集合的提供程序 - 在 MainWindowViewModel.cs 中,我们需要添加

        public class AuthorProvider : IPagedSourceProvider<Author>
        {
            private AuthorBookEntities _ctx = null;

            public AuthorProvider(AuthorBookEntities ctx)
            {
                _ctx = ctx;
            }

            public int Count
            {
                get { return (from a in _ctx.Authors select a).Count(); }
            }

            public PagedSourceItemsPacket<Author> GetItemsAt(int pageoffset, int count, bool usePlaceholder)
            {
                return new PagedSourceItemsPacket<Author>() { LoadedAt = DateTime.Now, Items = (from a in _ctx.Authors orderby a.AuthorName select a).Skip(pageoffset).Take(count) };
            }

            public int IndexOf(Author item)
            {
                return (from a in _ctx.Authors orderby a.AuthorName where a.AuthorName.CompareTo(item.AuthorName)<0 select a).Count();
            }

            public void OnReset(int count)
            {
            }
        }

在我们的例子中,我们将实体上下文传递到构造函数中,因为我们需要它,然后我们实现 IPagedSourceProvider 约定,它是三个方法和一个属性:-

Count - 用于告知 ItemsSource 定义集合中有多少项。请注意,我们实际上并没有获取项目,而是只返回计数。

GetItemsAt - 在这种情况下,我们只需使用 Skip 和 Take 返回项目 - 基本上是取回一页数据 - 我们正在使用默认值,因此它一次取回大约 100 个项目。

IndexOf - 当项目未连接(即不在我们“看到”的页面中)时使用此方法,该方法由 Combo 选择系统使用 - 但很少使用...

OnReset - 我们不需要在这里做任何聪明的事情。一个空方法就足够了。

接下来,我们将原始 Authors List 替换为 VirtualizingObservableCollection,而不是填充该列表,而是返回该集合。

所以:

        private void FillAuthors()
        {
            var q = (from a in ctx.Authors
                     select a).ToList();
            this.Authors = q;
        }
 
        private List<Author> _authors;
        public List<Author> Authors
        {
            get
            {
                return _authors;
            }
            set
            {
                _authors = value;
                NotifyPropertyChanged();
            }
        }

被替换为

        private void FillAuthors()
        {
            Authors = new VirtualizingObservableCollection<Author>(new PaginationManager<Author>(new AuthorProvider(this.ctx)));
        }

        private VirtualizingObservableCollection<Author> _authors;
        public VirtualizingObservableCollection<Author> Authors
        {
            get
            {
                return _authors;
            }
            set
            {
                _authors = value;
                NotifyPropertyChanged();
            }
        }

接下来,我们需要编写一个提供程序来访问作者的书籍 - 在这种情况下,我们需要传入两件事 - 实体上下文,以及我们要用来过滤书籍的作者。

在我们的例子中,我们通过构造函数传入这些,所以提供程序看起来像这样

        public class BookProvider : IPagedSourceProvider<Book>
        {
            private AuthorBookEntities _ctx = null;

            private Author _Author = null;
           
            public BookProvider(AuthorBookEntities ctx, Author author)
            {
                _ctx = ctx;
                _Author = author;
            }

            public int Count
            {
                get { return (from b in _ctx.Books where b.AuthorId == _Author.AuthorId orderby b.Title select b).Count(); }
            }

            public PagedSourceItemsPacket<Book> GetItemsAt(int pageoffset, int count, bool usePlaceholder)
            {
                return new PagedSourceItemsPacket<Book>() { LoadedAt = DateTime.Now, Items = (from b in _ctx.Books where b.AuthorId == _Author.AuthorId orderby b.Title select b).Skip(pageoffset).Take(count) };
            }

            public int IndexOf(Book item)
            {
                return (from b in _ctx.Books where b.AuthorId == _Author.AuthorId && b.Title.CompareTo(item.Title)<0 orderby b.Title select b).Count() ;
            }

            public void OnReset(int count)
            {
            }
        }

如您所见,它与 Authors 提供程序非常相似(除了它返回书籍!),唯一的区别是构造函数的额外参数 Author,以及在 Where 子句中使用该 Author 对象将其仅过滤为该作者。

接下来,我们需要替换“fillbooks”以使用提供程序,并将 List<Books> 替换为虚拟化集合,因此原始代码是

        private void FillBook()
        {
            Author author = this.SelectedAuthor;
 
            var q = (from book in ctx.Books
                     orderby book.Title
                     where book.AuthorId == author.AuthorId
                     select book).ToList();
 
            this.Books = q;
        }
 
        private List<Book> _books;
        public List<Book> Books
        {
            get
            {
                return _books;
            }
            set
            {
                _books = value;
                NotifyPropertyChanged();
            }
        }

变为

        private void FillBook()
        {
            Author author = this.SelectedAuthor;

            this.Books = new VirtualizingObservableCollection<Book>(new PaginationManager<Book>(new BookProvider(this.ctx, author)));
        }

        private VirtualizingObservableCollection<Book> _books = null;
        public VirtualizingObservableCollection<Book> Books
        {
            get
            {
                return _books;
            }
            set
            {
                _books = value;
                NotifyPropertyChanged();
            }
        }

所以,我们的新 ViewModel 看起来像这样

using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using AlphaChiTech.Virtualization;

namespace Wpf_EF_Mvvm_sample
{
    /// <summary>
    ///
    /// Before running this proejct please create the required tables from the CreateAuthorBook.sql file
    /// and then update the connection string in app.config
    /// note:  if the following error message comes up after compiling the project
    ///
    /// >>Error 1 No connection string named 'AuthorBookEntities' could be found in the application config file.<<
    ///
    /// this error message can be ignored.  The project should run fine.
    /// </summary>
    class MainWindowViewModel : INotifyPropertyChanged
    {
        public class AuthorProvider : IPagedSourceProvider<Author>
        {
            private AuthorBookEntities _ctx = null;

            public AuthorProvider(AuthorBookEntities ctx)
            {
                _ctx = ctx;
            }

            public int Count
            {
                get { return (from a in _ctx.Authors select a).Count(); }
            }

            public PagedSourceItemsPacket<Author> GetItemsAt(int pageoffset, int count, bool usePlaceholder)
            {
                return new PagedSourceItemsPacket<Author>() { LoadedAt = DateTime.Now, Items = (from a in _ctx.Authors orderby a.AuthorName select a).Skip(pageoffset).Take(count) };
            }

            public int IndexOf(Author item)
            {
                return (from a in _ctx.Authors orderby a.AuthorName where a.AuthorName.CompareTo(item.AuthorName)<0 select a).Count();
            }

            public void OnReset(int count)
            {
            }
        }

        public class BookProvider : IPagedSourceProvider<Book>
        {
            private AuthorBookEntities _ctx = null;

            private Author _Author = null;
           
            public BookProvider(AuthorBookEntities ctx, Author author)
            {
                _ctx = ctx;
                _Author = author;
            }

            public int Count
            {
                get { return (from b in _ctx.Books where b.AuthorId == _Author.AuthorId orderby b.Title select b).Count(); }
            }

            public PagedSourceItemsPacket<Book> GetItemsAt(int pageoffset, int count, bool usePlaceholder)
            {
                return new PagedSourceItemsPacket<Book>() { LoadedAt = DateTime.Now, Items = (from b in _ctx.Books where b.AuthorId == _Author.AuthorId orderby b.Title select b).Skip(pageoffset).Take(count) };
            }

            public int IndexOf(Book item)
            {
                return (from b in _ctx.Books where b.AuthorId == _Author.AuthorId && b.Title.CompareTo(item.Title)<0 orderby b.Title select b).Count() ;
            }

            public void OnReset(int count)
            {
            }
        }

        AuthorBookEntities ctx = new AuthorBookEntities();

        public MainWindowViewModel()
        {
            FillAuthors();
        }

        private void FillAuthors()
        {
            Authors = new VirtualizingObservableCollection<Author>(new PaginationManager<Author>(new AuthorProvider(this.ctx)));
        }

        private VirtualizingObservableCollection<Author> _authors;
        public VirtualizingObservableCollection<Author> Authors
        {
            get
            {
                return _authors;
            }
            set
            {
                _authors = value;
                NotifyPropertyChanged();
            }
        }

        private Author _selectedAuthor;
        public Author SelectedAuthor
        {
            get
            {
                return _selectedAuthor;
            }
            set
            {
                _selectedAuthor = value;
                NotifyPropertyChanged();
                FillBook();
            }
        }

        private void FillBook()
        {
            Author author = this.SelectedAuthor;

            this.Books = new VirtualizingObservableCollection<Book>(new PaginationManager<Book>(new BookProvider(this.ctx, author)));
        }

        private VirtualizingObservableCollection<Book> _books = null;
        public VirtualizingObservableCollection<Book> Books
        {
            get
            {
                return _books;
            }
            set
            {
                _books = value;
                NotifyPropertyChanged();
            }
        }

        private Book _selectedBook;
        public Book SelectedBook
        {
            get
            {
                return _selectedBook;
            }
            set
            {
                _selectedBook = value;
                NotifyPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

现在,我们需要做的就是引导管理器,所以在 MainWindow.xaml.cs 中,我们扩展构造函数如下

        public MainWindow()
        {
            if (!VirtualizationManager.IsInitialized)
            {
                VirtualizationManager.Instance.UIThreadExcecuteAction = (a) => Dispatcher.Invoke(a);
                new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.Background, delegate(object s, EventArgs a) { VirtualizationManager.Instance.ProcessActions(); }, this.Dispatcher).Start();
            }

            InitializeComponent();
        }

 

就是这样 - 我们完成了! 现在我们可以放心,如果我们有三个或三十万作者/书籍,系统将以相似的性能响应。

关注点

VirtualizingObservableCollection 可以在 nuget 上找到 - 请参阅 https://nuget.net.cn/packages/VirtualizingObservableCollection/

欢迎访问 https://alphachitech.wordpress.com/ 获取更多信息和更新。

历史

初始版本。 感谢 Richard 提供如此干净的示例!

© . All rights reserved.