wpf-6091-controls-and-libraries-data-grid-bind-to-data-server-mode-and-instant-feedback-bind-to-xpo-data-sources.md
XPO is the DevExpress ORM library that helps you access and process data stored in-memory or within traditional database engines. This article describes how to use XPO with the DevExpress WPF Data Grid.
This example includes multiple solutions that demonstrate:
View Example: Bind the WPF Data Grid to Data
The example uses the Issues Service database that stores data for two entities: users and issues assigned to these users.
Each XPO project includes the common Issues folder. Files in this folder define the XPO-related logic:
OutlookDataGenerator.cs Generates data.ConnectionHelper.cs Connects to a database.DemoDataHelper.cs Loads generated data to the data source.Issues.cs and Customer.cs Define entities.
OutlookDataGenerator.cs / OutlookDataGenerator.vb
using System;
namespace XPOIssues.Issues {
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. Somebody has to 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. Somebody has to 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 in 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 this 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);
}
}
}
Imports System
Namespace Issues
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. Somebody has to 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. Somebody has to 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 in 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 this 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 Function
Public Function GetFrom() As String
Return Users(rnd.Next(Users.Length))
End Function
Public Function GetPriority() As Priority
Return CType(rnd.Next(5), Priority)
End Function
End Module
End Namespace
ConnectionHelper.cs / ConnectionHelper.vb
using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using DevExpress.Xpo.Metadata;
using System;
using System.Configuration;
namespace XPOIssues.Issues {
public static class ConnectionHelper {
static readonly Type[] PersistentTypes = new Type[]{
typeof(Issue),
typeof(User)
};
public static void Connect() {
XpoDefault.DataLayer = CreateDataLayer(true);
}
static IDataLayer CreateDataLayer(bool threadSafe) {
string connStr = ConfigurationManager.ConnectionStrings["XpoTutorial"]?.ConnectionString ?? "XpoProvider=InMemoryDataStore";
// Uncomment this line if you use a database server like SQL Server, Oracle, PostgreSql etc.
//connStr = XpoDefault.GetConnectionPoolString(connStr);
ReflectionDictionary dictionary = new ReflectionDictionary();
// Pass all of your persistent object types to this method.
dictionary.GetDataStoreSchema(PersistentTypes);
// Use AutoCreateOption.DatabaseAndSchema if the database or tables do not exist.
// Use AutoCreateOption.SchemaAlreadyExists if the database already exists.
AutoCreateOption autoCreateOption = AutoCreateOption.DatabaseAndSchema;
IDataStore provider = XpoDefault.GetConnectionProvider(connStr, autoCreateOption);
return threadSafe ? (IDataLayer)new ThreadSafeDataLayer(dictionary, provider) : new SimpleDataLayer(dictionary, provider);
}
}
}
Imports DevExpress.Xpo
Imports DevExpress.Xpo.DB
Imports DevExpress.Xpo.Metadata
Imports System
Imports System.Configuration
Namespace Issues
Public Module ConnectionHelper
Private ReadOnly PersistentTypes As Type() = New Type() {GetType(Issue), GetType(User)}
Public Sub Connect()
XpoDefault.DataLayer = CreateDataLayer(True)
End Sub
Private Function CreateDataLayer(ByVal threadSafe As Boolean) As IDataLayer
Dim connStr As String = If(ConfigurationManager.ConnectionStrings("XpoTutorial")?.ConnectionString, "XpoProvider=InMemoryDataStore")
' Uncomment this line if you use a database server like SQL Server, Oracle, PostgreSql etc.
'connStr = XpoDefault.GetConnectionPoolString(connStr);
Dim dictionary As ReflectionDictionary = New ReflectionDictionary()
' Pass all of your persistent object types to this method.
dictionary.GetDataStoreSchema(PersistentTypes)
' Use AutoCreateOption.DatabaseAndSchema if the database or tables do not exist.
' Use AutoCreateOption.SchemaAlreadyExists if the database already exists.
Dim autoCreateOption As AutoCreateOption = AutoCreateOption.DatabaseAndSchema
Dim provider As IDataStore = XpoDefault.GetConnectionProvider(connStr, autoCreateOption)
Return If(threadSafe, CType(New ThreadSafeDataLayer(dictionary, provider), IDataLayer), New SimpleDataLayer(dictionary, provider))
End Function
End Module
End Namespace
DemoDataHelper.cs / DemoDataHelper.vb
using DevExpress.Xpo;
using System;
using System.Collections.Generic;
using System.Linq;
namespace XPOIssues.Issues {
public static class DemoDataHelper {
public static void Seed() {
using(var uow = new DevExpress.Xpo.UnitOfWork()) {
var users = OutlookDataGenerator.Users
.Select(x =>
{
var split = x.Split(' ');
return new User(uow)
{
FirstName = split[0],
LastName = split[1]
};
})
.ToArray();
uow.CommitChanges();
var rnd = new Random(0);
var issues = Enumerable.Range(0, 1000)
.Select(i => new Issue(uow)
{
Subject = OutlookDataGenerator.GetSubject(),
UserId = users[rnd.Next(users.Length)].Oid,
Created = DateTime.Today.AddDays(-rnd.Next(30)),
Priority = OutlookDataGenerator.GetPriority(),
Votes = rnd.Next(100),
})
.ToArray();
uow.CommitChanges();
}
}
}
}
Imports DevExpress.Xpo
Imports System
Imports System.Linq
Namespace Issues
Public Module DemoDataHelper
Public Sub Seed()
Using uow = New DevExpress.Xpo.UnitOfWork()
Dim users = OutlookDataGenerator.Users.[Select](Function(x)
Dim split = x.Split(" "c)
Return New User(uow) With {
.FirstName = split(0),
.LastName = split(1)
}
End Function).ToArray()
uow.CommitChanges()
Dim rnd = New Random(0)
Dim issues = Enumerable.Range(0, 1000).[Select](Function(i) New Issue(uow) With {
.Subject = OutlookDataGenerator.GetSubject(),
.UserId = users(rnd.Next(users.Length)).Oid,
.Created = Date.Today.AddDays(-rnd.Next(30)),
.Priority = OutlookDataGenerator.GetPriority(),
.Votes = rnd.Next(100)
}).ToArray()
uow.CommitChanges()
End Using
End Sub
End Module
End Namespace
Issue.cs / Issue.vb
using System;
using DevExpress.Xpo;
namespace XPOIssues.Issues {
public class Issue : XPObject {
public Issue(Session session) : base(session) {
Created = DateTime.Now;
}
string _Subject;
[Size(200)]
public string Subject
{
get { return _Subject; }
set { SetPropertyValue(nameof(Subject), ref _Subject, value); }
}
int _UserId;
public int UserId
{
get { return _UserId; }
set { SetPropertyValue(nameof(UserId), ref _UserId, value); }
}
User _User;
[Association("UserIssues")]
public User User
{
get { return _User; }
set { SetPropertyValue(nameof(User), ref _User, value); }
}
DateTime _Created;
public DateTime Created
{
get { return _Created; }
set { SetPropertyValue(nameof(Created), ref _Created, value); }
}
int _Votes;
public int Votes
{
get { return _Votes; }
set { SetPropertyValue(nameof(Votes), ref _Votes, value); }
}
Priority _Priority;
public Priority Priority
{
get { return _Priority; }
set { SetPropertyValue(nameof(Priority), ref _Priority, value); }
}
}
public enum Priority { Low, BelowNormal, Normal, AboveNormal, High }
}
Imports DevExpress.Xpo
Namespace Issues
Public Class Issue
Inherits XPObject
Public Sub New(ByVal session As Session)
MyBase.New(session)
Created = Date.Now
End Sub
Private _Subject As String
<Size(200)>
Public Property Subject As String
Get
Return _Subject
End Get
Set(ByVal value As String)
SetPropertyValue(NameOf(Issue.Subject), _Subject, value)
End Set
End Property
Private _UserId As Integer
Public Property UserId As Integer
Get
Return _UserId
End Get
Set(ByVal value As Integer)
SetPropertyValue(NameOf(Issue.UserId), _UserId, value)
End Set
End Property
Private _User As User
<Association("UserIssues")>
Public Property User As User
Get
Return _User
End Get
Set(ByVal value As User)
SetPropertyValue(NameOf(Issue.User), _User, value)
End Set
End Property
Private _Created As Date
Public Property Created As Date
Get
Return _Created
End Get
Set(ByVal value As Date)
SetPropertyValue(NameOf(Issue.Created), _Created, value)
End Set
End Property
Private _Votes As Integer
Public Property Votes As Integer
Get
Return _Votes
End Get
Set(ByVal value As Integer)
SetPropertyValue(NameOf(Issue.Votes), _Votes, value)
End Set
End Property
Private _Priority As Priority
Public Property Priority As Priority
Get
Return _Priority
End Get
Set(ByVal value As Priority)
SetPropertyValue(NameOf(Issue.Priority), _Priority, value)
End Set
End Property
End Class
Public Enum Priority
Low
BelowNormal
Normal
AboveNormal
High
End Enum
End Namespace
Customer.cs / Customer.vb
using DevExpress.Xpo;
namespace XPOIssues.Issues {
public class User : XPObject {
public User(Session session) : base(session) { }
string _FirstName;
public string FirstName
{
get { return _FirstName; }
set { SetPropertyValue(nameof(FirstName), ref _FirstName, value); }
}
string _LastName;
public string LastName
{
get { return _LastName; }
set { SetPropertyValue(nameof(LastName), ref _LastName, value); }
}
[Association("UserIssues")]
public XPCollection<Issue> Issues
{
get { return GetCollection<Issue>(nameof(Issues)); }
}
}
}
Imports DevExpress.Xpo
Namespace Issues
Public Class User
Inherits XPObject
Public Sub New(ByVal session As Session)
MyBase.New(session)
End Sub
Private _FirstName As String
Public Property FirstName As String
Get
Return _FirstName
End Get
Set(ByVal value As String)
SetPropertyValue(NameOf(User.FirstName), _FirstName, value)
End Set
End Property
Private _LastName As String
Public Property LastName As String
Get
Return _LastName
End Get
Set(ByVal value As String)
SetPropertyValue(NameOf(User.LastName), _LastName, value)
End Set
End Property
<Association("UserIssues")>
Public ReadOnly Property Issues As XPCollection(Of Issue)
Get
Return GetCollection(Of Issue)(NameOf(User.Issues))
End Get
End Property
End Class
End Namespace
If you store data in a local database, you can use XPCollection as a data source and bind it to the GridControl.
using System.Windows;
using XPOIssues.Issues;
using DevExpress.Xpo;
using System.Linq;
namespace XPOIssues {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
LoadData();
}
UnitOfWork _UnitOfWork;
void LoadData() {
_UnitOfWork = new UnitOfWork();
var xpCollection = new XPCollection<User>(_UnitOfWork);
xpCollection.Sorting.Add(new SortProperty(nameof(User.Oid), DevExpress.Xpo.DB.SortingDirection.Ascending));
grid.ItemsSource = xpCollection;
}
}
}
Imports XPOIssues.Issues
Imports DevExpress.Xpo
Imports System.Linq
Class MainWindow
Public Sub New()
InitializeComponent()
LoadData()
End Sub
Private _UnitOfWork As UnitOfWork
Private Sub LoadData()
_UnitOfWork = New UnitOfWork()
Dim xpCollection = New XPCollection(Of User)(_UnitOfWork)
xpCollection.Sorting.Add(New SortProperty(NameOf(User.Oid), DevExpress.Xpo.DB.SortingDirection.Ascending))
grid.ItemsSource = xpCollection
End Sub
End Class
In Server Mode, the GridControl loads data in small portions on demand. To activate this mode, you can use DevExpress.Xpo.XPServerModeView as a data source. Initialize the data source and bind it to the GridControl as follows:
using System.Windows;
using XPOIssues.Issues;
using DevExpress.Data.Filtering;
using DevExpress.Xpo;
using System.Linq;
using DevExpress.Xpf.Grid;
namespace XPOIssues {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var properties = new ServerViewProperty[] {
new ServerViewProperty("Subject", SortDirection.None, new OperandProperty("Subject")),
new ServerViewProperty("UserId", SortDirection.None, new OperandProperty("UserId")),
new ServerViewProperty("Created", SortDirection.None, new OperandProperty("Created")),
new ServerViewProperty("Votes", SortDirection.None, new OperandProperty("Votes")),
new ServerViewProperty("Priority", SortDirection.None, new OperandProperty("Priority")),
new ServerViewProperty("Oid", SortDirection.Ascending, new OperandProperty("Oid"))
};
var session = new Session();
var source = new XPServerModeView(session, typeof(Issue), null);
source.Properties.AddRange(properties);
grid.ItemsSource = source;
LoadLookupData();
}
void LoadLookupData() {
var session = new Session();
usersLookup.ItemsSource = session.Query<User>().OrderBy(user => user.Oid).Select(user => new { Id = user.Oid, Name = user.FirstName + " " + user.LastName }).ToArray();
}
}
}
Imports XPOIssues.Issues
Imports DevExpress.Data.Filtering
Imports DevExpress.Xpo
Imports System.Linq
Imports DevExpress.Xpf.Grid
Class MainWindow
Public Sub New()
InitializeComponent()
Dim properties = New ServerViewProperty() {
New ServerViewProperty("Subject", SortDirection.None, New OperandProperty("Subject")),
New ServerViewProperty("UserId", SortDirection.None, New OperandProperty("UserId")),
New ServerViewProperty("Created", SortDirection.None, New OperandProperty("Created")),
New ServerViewProperty("Votes", SortDirection.None, New OperandProperty("Votes")),
New ServerViewProperty("Priority", SortDirection.None, New OperandProperty("Priority")),
New ServerViewProperty("Oid", SortDirection.Ascending, New OperandProperty("Oid"))
}
Dim session = New Session()
Dim source = New XPServerModeView(session, GetType(Issue), Nothing)
source.Properties.AddRange(properties)
grid.ItemsSource = source
LoadLookupData()
End Sub
Private Sub LoadLookupData()
Dim session = New Session()
usersLookup.ItemsSource = session.Query(Of User).OrderBy(Function(user) user.Oid).[Select](Function(user) New With {
.Id = user.Oid,
.Name = user.FirstName & " " + user.LastName
}).ToArray()
End Sub
End Class
In Instant Feedback Mode, the GridControl loads data in a background thread. To activate this mode, you can use DevExpress.Xpo.XPInstantFeedbackView as a data source. Initialize the data source and bind it to the GridControl as follows:
using System.Windows;
using XPOIssues.Issues;
using DevExpress.Data.Filtering;
using DevExpress.Xpo;
using System.Linq;
using DevExpress.Xpf.Grid;
namespace XPOIssues {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var properties = new ServerViewProperty[] {
new ServerViewProperty("Subject", SortDirection.None, new OperandProperty("Subject")),
new ServerViewProperty("UserId", SortDirection.None, new OperandProperty("UserId")),
new ServerViewProperty("Created", SortDirection.None, new OperandProperty("Created")),
new ServerViewProperty("Votes", SortDirection.None, new OperandProperty("Votes")),
new ServerViewProperty("Priority", SortDirection.None, new OperandProperty("Priority")),
new ServerViewProperty("Oid", SortDirection.Ascending, new OperandProperty("Oid"))
};
var source = new XPInstantFeedbackView(typeof(Issue), properties, null);
source.ResolveSession += (o, e) => {
e.Session = new Session();
};
grid.ItemsSource = source;
LoadLookupData();
}
void LoadLookupData() {
var session = new Session();
usersLookup.ItemsSource = session.Query<User>().OrderBy(user => user.Oid).Select(user => new { Id = user.Oid, Name = user.FirstName + " " + user.LastName }).ToArray();
}
}
}
Imports XPOIssues.Issues
Imports DevExpress.Data.Filtering
Imports DevExpress.Xpo
Imports System.Linq
Imports DevExpress.Xpf.Grid
Class MainWindow
Public Sub New()
InitializeComponent()
Dim properties = New ServerViewProperty() {
New ServerViewProperty("Subject", SortDirection.None, New OperandProperty("Subject")),
New ServerViewProperty("UserId", SortDirection.None, New OperandProperty("UserId")),
New ServerViewProperty("Created", SortDirection.None, New OperandProperty("Created")),
New ServerViewProperty("Votes", SortDirection.None, New OperandProperty("Votes")),
New ServerViewProperty("Priority", SortDirection.None, New OperandProperty("Priority")),
New ServerViewProperty("Oid", SortDirection.Ascending, New OperandProperty("Oid"))
}
Dim source = New XPInstantFeedbackView(GetType(Issue), properties, Nothing)
AddHandler source.ResolveSession, Sub(o, e) e.Session = New Session()
grid.ItemsSource = source
LoadLookupData()
End Sub
Private Sub LoadLookupData()
Dim session = New Session()
usersLookup.ItemsSource = session.Query(Of User).OrderBy(Function(user) user.Oid).[Select](Function(user) New With {
.Id = user.Oid,
.Name = user.FirstName & " " + user.LastName
}).ToArray()
End Sub
End Class
Virtual Sources create a Session on each request. If the Session is disposed of, all Persistent Objects that it contains are also disposed of. As a result, you cannot retrieve the objects’ properties. Use the DetachedObjectsHelper class to store these properties and access them from the GridControl anytime.
The following code sample uses the GetClassInfo method to get Issue class’s metadata and saves it to the DetachedObjectsHelper instance.
namespace XPOIssues {
public partial class MainWindow : Window {
public MainWindow() {
// ...
using(var session = new Session()) {
var classInfo = session.GetClassInfo<Issue>();
var properties = classInfo.Members
.Where(member => member.IsPublic && member.IsPersistent)
.Select(member => member.Name)
.ToArray();
_DetachedObjectsHelper = DetachedObjectsHelper<Issue>.Create(classInfo.KeyProperty.Name, properties);
}
// ...
}
// ...
DetachedObjectsHelper<Issue> _DetachedObjectsHelper;
// ...
}
}
Class MainWindow
Public Sub New()
InitializeComponent()
Using session = New Session()
Dim classInfo = session.GetClassInfo(Of Issue)()
Dim properties = classInfo.Members.Where(Function(member) member.IsPublic AndAlso member.IsPersistent).[Select](Function(member) member.Name).ToArray()
_DetachedObjectsHelper = DetachedObjectsHelper(Of Issue).Create(classInfo.KeyProperty.Name, properties)
End Using
' ...
End Sub
' ...
Private _DetachedObjectsHelper As DetachedObjectsHelper(Of Issue)
' ...
End Class
Initialize InfiniteAsyncSource and bind it to the GridControl. Assign DetachedObjectsHelper.Properties to CustomProperties to populate the GridControl with the persistent objects’ properties. DetachedObjectsHelper.Properties contains their descriptors.
namespace XPOIssues {
public partial class MainWindow : Window {
public MainWindow() {
// ...
var source = new InfiniteAsyncSource {
CustomProperties = _DetachedObjectsHelper.Properties,
KeyProperty = nameof(Issue.Oid)
};
source.FetchRows += OnFetchRows;
source.GetTotalSummaries += OnGetTotalSummaries;
grid.ItemsSource = source;
LoadLookupData();
}
// ...
}
}
Class MainWindow
Public Sub New()
' ...
Dim source = New InfiniteAsyncSource With {
.CustomProperties = _DetachedObjectsHelper.Properties,
.KeyProperty = nameof(Issue.Oid)
}
AddHandler source.FetchRows, AddressOf OnFetchRows
AddHandler source.GetTotalSummaries, AddressOf OnGetTotalSummaries
grid.ItemsSource = source
' ...
End Sub
' ...
End Class
You can also use PagedAsyncSource that displays data in pages:
namespace XPOIssues {
public partial class MainWindow : Window {
public MainWindow() {
// ...
var source = new PagedAsyncSource {
CustomProperties = _DetachedObjectsHelper.Properties,
KeyProperty = nameof(Issue.Oid),
PageNavigationMode = PageNavigationMode.ArbitraryWithTotalPageCount
};
source.FetchPage += OnFetchPage;
source.GetTotalSummaries += OnGetTotalSummaries;
grid.ItemsSource = source;
// ...
}
// ...
}
}
Class MainWindow
Public Sub New()
' ...
Dim source = New PagedAsyncSource With {
.CustomProperties = _DetachedObjectsHelper.Properties,
.KeyProperty = nameof(Issue.Oid),
.PageNavigationMode = PageNavigationMode.ArbitraryWithTotalPageCount
}
AddHandler source.FetchPage, AddressOf OnFetchPage
AddHandler source.GetTotalSummaries, AddressOf OnGetTotalSummaries
grid.ItemsSource = source
' ...
End Sub
' ...
End Class
If you fetch new data from the database, use the ConvertToDetachedObjects(IEnumerable<T>) method to convert persistent objects to detached objects:
namespace XPOIssues {
public partial class MainWindow : Window {
// ...
void OnFetchRows(object sender, FetchRowsAsyncEventArgs e) {
e.Result = Task.Run<FetchRowsResult>(() => {
using(var session = new Session()) {
var queryable = session.Query<Issue>().SortBy(e.SortOrder, defaultUniqueSortPropertyName: nameof(Issue.Oid)).Where(MakeFilterExpression(e.Filter));
var items = queryable.Skip(e.Skip).Take(e.Take ?? 100).ToArray();
return _DetachedObjectsHelper.ConvertToDetachedObjects(items);
}
});
}
// ...
}
}
Class MainWindow
' ...
Private Sub OnFetchRows(ByVal sender As Object, ByVal e As FetchRowsAsyncEventArgs)
e.Result = Task.Run(Of FetchRowsResult)(Function()
Using session = New Session()
Dim queryable = session.Query(Of Issue)().SortBy(e.SortOrder, defaultUniqueSortPropertyName:=NameOf(Issue.Oid)).Where(MakeFilterExpression(e.Filter))
Dim items = queryable.Skip(e.Skip).Take(If(e.Take, 100)).ToArray()
Return _DetachedObjectsHelper.ConvertToDetachedObjects(items)
End Using
End Function)
End Sub
' ...
End Class
After you bind the Data Grid to a database, you can implement CRUD operations (create, read update, delete). Refer to the following topic for more information: CRUD Operations in a Data-Bound Grid.
View Example: Implement CRUD Operations in the WPF Data Grid
See Also