DevExpress WPF 中文教程:如何将 WPF 数据网格绑定虚拟数据源?
DevExpress WPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件的衍伸产品,还是以数据为中心的商业智能产品,都能通过DevExpress WPF控件来实现。
本文演示如何使用InfiniteAsyncSource或PagedAsyncSource虚拟源将GridControl绑定到数据源。,欢迎下载最新版组件体验!
获取DevExpress WPF v25.1正式版下载
示例数据
在本教程中,将IssuesContext DbContext后代用作示例数据源。
TIP:DevExpress虚拟源支持Entity Framework、Entity Framework Core和XPO源。
安装EntityFramework NuGet包。
将以下代码添加到应用程序中。
示例数据
Issue.cs
using System.Collections.Generic;public class Issue {
public int Id { get; set; }
public string Subject { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public DateTime Created { get; set; }
public int Votes { get; set; }
public Priority Priority { get; set; }
public Issue() {
Created = DateTime.Now;
}
}
public enum Priority { Low, BelowNormal, Normal, AboveNormal, High }public class User {
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Issue> Issues { get; set; }
}
Issue.vb
Imports System.Collections.GenericPublic Class Issue
Public Property Id As Integer
Public Property Subject As String
Public Property UserId As Integer
Public Overridable Property User As User
Public Property Created As Date
Public Property Votes As Integer
Public Property Priority As PriorityPublic Sub New()
Created = Date.Now
End Sub
End ClassPublic Enum Priority
Low
BelowNormal
Normal
AboveNormal
High
End EnumPublic Class User
Public Property Id As Integer
Public Property FirstName As String
Public Property LastName As String
Public Overridable Property Issues As ICollection(Of Issue)
End Class
IssuesContextInitializer.cs
using System;
using System.Data.Entity;
using System.Linq;public class IssuesContextInitializer
: DropCreateDatabaseIfModelChanges<IssuesContext> {public static void ResetData() {
using(var context = new IssuesContext()) {
context.Users.Load();
context.Users.RemoveRange(context.Users);
context.SaveChanges();
CreateData(context);
}
}protected override void Seed(IssuesContext context) {
base.Seed(context);
CreateData(context);
}static void CreateData(IssuesContext context) {
var users = OutlookDataGenerator.Users
.Select(x =>
{
var split = x.Split(' ');
return new User() {
FirstName = split[0],
LastName = split[1]
};
})
.ToArray();
context.Users.AddRange(users);
context.SaveChanges();var rnd = new Random(0);
var issues = Enumerable.Range(0, 1000)
.Select(i => new Issue()
{
Subject = OutlookDataGenerator.GetSubject(),
UserId = users[rnd.Next(users.Length)].Id,
Created = DateTime.Today.AddDays(-rnd.Next(30)),
Priority = OutlookDataGenerator.GetPriority(),
Votes = rnd.Next(100),
})
.ToArray();
context.Issues.AddRange(issues);context.SaveChanges();
}
}
IssuesContextInitializer.vb
Imports System
Imports System.Data.Entity
Imports System.LinqPublic Class IssuesContextInitializer
Inherits DropCreateDatabaseIfModelChanges(Of IssuesContext)': DropCreateDatabaseAlways<IssuesContext> {Public Shared Sub ResetData()
Using context = New IssuesContext()
context.Users.Load()
context.Users.RemoveRange(context.Users)
context.SaveChanges()
CreateData(context)
End Using
End SubProtected Overrides Sub Seed(ByVal context As IssuesContext)
MyBase.Seed(context)
CreateData(context)
End SubPrivate Shared Sub CreateData(ByVal context As IssuesContext)
Dim users = OutlookDataGenerator.Users.[Select](Function(x)
Dim split = x.Split(" "c)
Return New User() With {
.FirstName = split(0),
.LastName = split(1)
}
End Function).ToArray()
context.Users.AddRange(users)
context.SaveChanges()
Dim rnd = New Random(0)
Dim issues = Enumerable.Range(0, 1000).[Select](Function(i) New Issue() With {
.Subject = OutlookDataGenerator.GetSubject(),
.UserId = users(rnd.Next(users.Length)).Id,
.Created = Date.Today.AddDays(-rnd.Next(30)),
.Priority = OutlookDataGenerator.GetPriority(),
.Votes = rnd.Next(100)
}).ToArray()
context.Issues.AddRange(issues)
context.SaveChanges()
End Sub
End Class
IssuesContext.cs
using System.Data.Entity;public class IssuesContext : DbContext {
static IssuesContext() {
Database.SetInitializer(new IssuesContextInitializer());
}
public IssuesContext() { }protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);modelBuilder.Entity<Issue>()
.HasIndex(x => x.Created);modelBuilder.Entity<Issue>()
.HasIndex(x => x.Votes);
}public DbSet<Issue> Issues { get; set; }
public DbSet<User> Users { get; set; }
}
IssuesContext.vb
Imports System.Data.EntityPublic Class IssuesContext
Inherits DbContextShared Sub New()
Database.SetInitializer(New IssuesContextInitializer())
End SubPublic Sub New()
End SubProtected Overrides Sub OnModelCreating(ByVal modelBuilder As DbModelBuilder)
MyBase.OnModelCreating(modelBuilder)
modelBuilder.Entity(Of Issue)().HasIndex(Function(x) x.Created)
modelBuilder.Entity(Of Issue)().HasIndex(Function(x) x.Votes)
End SubPublic Property Issues As DbSet(Of Issue)
Public Property Users As DbSet(Of User)
End Class
OutlookDataGenerator.cs
public static class OutlookDataGenerator {
static Random rnd = new Random(0);
static string[] Subjects = new string[] { "Developer Express MasterView. Integrating the control into an Accounting System.",
"Web Edition: Data Entry Page. There is an issue with date validation.",
"Payables Due Calculator is ready for testing.",
"Web Edition: Search Page is ready for testing.",
"Main Menu: Duplicate Items. Someone must review all menu items in the system.",
"Receivables Calculator. Where can I find the complete specs?",
"Ledger: Inconsistency. Please fix it.",
"Receivables Printing module is ready for testing.",
"Screen Redraw. Someone must look at it.",
"Email System. What library are we going to use?",
"Cannot add new vendor. This module doesn't work!",
"History. Will we track sales history in our system?",
"Main Menu: Add a File menu. File menu item is missing.",
"Currency Mask. The current currency mask is completely unusable.",
"Drag & Drop operations are not available in the scheduler module.",
"Data Import. What database types will we support?",
"Reports. The list of incomplete reports.",
"Data Archiving. We still don't have these features in our application.",
"Email Attachments. Is it possible to add multiple attachments? I haven't found a way to do this.",
"Check Register. We are using different paths for different modules.",
"Data Export. Our customers asked us for export to Microsoft Excel."};public static readonly string[] Users = new string[] {
"Peter Dolan",
"Ryan Fischer",
"Richard Fisher",
"Tom Hamlett",
"Mark Hamilton",
"Steve Lee",
"Jimmy Lewis",
"Jeffrey McClain",
"Andrew Miller",
"Dave Murrel",
"Bert Parkins",
"Mike Roller",
"Ray Shipman",
"Paul Bailey",
"Brad Barnes",
"Carl Lucas",
"Jerry Campbell",
};public static string GetSubject() {
return Subjects[rnd.Next(Subjects.Length - 1)];
}public static string GetFrom() {
return Users[rnd.Next(Users.Length)];
}
public static Priority GetPriority() {
return (Priority)rnd.Next(5);
}
}
OutlookDataGenerator.vb
Public Module OutlookDataGenerator
Private rnd As Random = New Random(0)
Private Subjects As String() = New String() {"Developer Express MasterView. Integrating the control into an Accounting System.", "Web Edition: Data Entry Page. There is an issue with date validation.", "Payables Due Calculator is ready for testing.", "Web Edition: Search Page is ready for testing.", "Main Menu: Duplicate Items. Someone must review all menu items in the system.", "Receivables Calculator. Where can I find the complete specs?", "Ledger: Inconsistency. Please fix it.", "Receivables Printing module is ready for testing.", "Screen Redraw. Someone must look at it.", "Email System. What library are we going to use?", "Cannot add new vendor. This module doesn't work!", "History. Will we track sales history in our system?", "Main Menu: Add a File menu. File menu item is missing.", "Currency Mask. The current currency mask is completely unusable.", "Drag & Drop operations are not available in the scheduler module.", "Data Import. What database types will we support?", "Reports. The list of incomplete reports.", "Data Archiving. We still don't have these features in our application.", "Email Attachments. Is it possible to add multiple attachments? I haven't found a way to do this.", "Check Register. We are using different paths for different modules.", "Data Export. Our customers asked us for export to Microsoft Excel."}
Public ReadOnly Users As String() = New String() {"Peter Dolan", "Ryan Fischer", "Richard Fisher", "Tom Hamlett", "Mark Hamilton", "Steve Lee", "Jimmy Lewis", "Jeffrey McClain", "Andrew Miller", "Dave Murrel", "Bert Parkins", "Mike Roller", "Ray Shipman", "Paul Bailey", "Brad Barnes", "Carl Lucas", "Jerry Campbell"}Public Function GetSubject() As String
Return Subjects(rnd.Next(Subjects.Length - 1))
End FunctionPublic Function GetFrom() As String
Return Users(rnd.Next(Users.Length))
End FunctionPublic Function GetPriority() As Priority
Return CType(rnd.Next(5), Priority)
End Function
End Module
将GridControl绑定到数据
1. 切换到涉及视图,打开GridControl的Quick Actions并调用Items Source Wizard。
2. 选择IssuesContext数据源。
3. 选择Issue表。
4. 选择InfiniteAsyncSource绑定模型,来无限滚动显示数据。或者,您可以选择PagedAsyncSource模型在页面中显示数据。
5. 确保Key Property设置为Id。
6. 在MainWindow.xaml.cs/MainWindow.xaml.vb中选择Code-Behind来生成绑定代码。
运行应用程序。
MVVM技术
要在视图模型中生成绑定代码,请遵循以下步骤。
创建一个ViewModel模板,实现ViewModelBase并重建解决方案:
ViewModel.cs
using DevExpress.Mvvm;namespace WPFBlankAppWithDatabase {
public class ViewModel : ViewModelBase {}
}ViewModel.vbImports DevExpress.MvvmNamespace WPFBlankAppWithDatabase
Public Class ViewModel
Inherits ViewModelBaseEnd Sub
End Class
End Namespace
请执行步骤1-5。
选择View Model将代码添加到视图模型中。
单击Select a Data Context,选择ViewModel类并点击OK。
启用Set selected class as the data context选项然后点击Finish。
Items Source Wizard在ViewModel中生成数据绑定代码,并在XAML中指定数据上下文和GridControl选项,运行应用程序来查看结果。