Skip to main content
.NET 8.0+

How to: Display a List of Non-Persistent Objects with an Intermediate Container Class and Its Detail View

  • 4 minutes to read

This example stores a list of books. When a user clicks the Show Duplicate Books action, a pop-up dialog displays a list of duplicate books and their copy counts. This list is implemented as a non-persistent object‘s Detail View.

Book duplicates in a dialog popup

This example uses an intermediate container class to display a list of non-persistent objects. In this case, the ObjectsGetting method cannot be applied. If your application logic does not require complex operations to create non-persistent objects, use the ObjectsGetting method. Refer to the following help topic for implementation details: How to: Display a Non-Persistent Object's List View from the Navigation.

Tip

You can find the complete example in the following GitHub repository: How to: Display a List of Non-Persistent Objects with an Intermediate Container Class and Its Detail View.

The following steps include the key implementation points:

Step 1: Declare the Book Persistent Class

Objects of this class denote books in a collection.

using DevExpress.Persistent.Base;
using DevExpress.Persistent.BaseImpl.EF;

namespace NonPersistentListView.Module {
    [DefaultClassOptions]
    public class Book : BaseObject {
        public virtual string Title { get; set; }
    }
}

Step 2: Declare Non-Persistent Classes

Declare two non-persistent classes—Duplicate and DuplicatesList, and decorate them with the DomainComponentAttribute.

  • The Duplicate class includes two properties—Title and Count. A class instance stores a unique book title and the total number of books with this title (if there is more than one book).
  • The DuplicatesList class aggregates the Duplicate objects.
using System.ComponentModel;
using DevExpress.ExpressApp.DC;
using DevExpress.ExpressApp;

namespace NonPersistentListView.Module {
    [DomainComponent]
    public class Duplicate: NonPersistentLiteObject {
        public string Title { get; set; }
        public int Count { get; set; }
    }
    [DomainComponent]
    public class DuplicatesList: NonPersistentLiteObject {
        private BindingList<Duplicate> duplicates;
        public DuplicatesList() {
            duplicates = new BindingList<Duplicate>();
        }
        public BindingList<Duplicate> Duplicates { get { return duplicates; } }
    }
}

Note

The INotifyPropertyChanged, IXafEntityObject and IObjectSpaceLink interface implementations were omitted in this example. However, it is recommended to support these interfaces in real-world applications (see The Importance of Property Change Notifications for Automatic UI Updates and Non-Persistent Objects).

Step 3: Register the Non-Persistent Object Space Provider

Ensure that the Non-Persistent Object Space Provider is registered in the Application Builder code. The Solution Wizard adds this code automatically.

// ...
builder.ObjectSpaceProviders
    // ...
    .AddNonPersistent();
// ...

Step 4: Count the Number of Book Copies

Create the ShowDuplicateBooksController View Controller. In the controller, implement a method that iterates through persistent Book objects and counts the number of copies of each book. Store this information in a dictionary.

// ...
private Dictionary<string, int> GetDuplicatesDictionary() {
    var dictionary = new Dictionary<string, int>();
    foreach(Book book in View.CollectionSource.List) {
        if(string.IsNullOrWhiteSpace(book.Title)) continue;
        dictionary[book.Title] = dictionary.GetValueOrDefault(book.Title) + 1;
    }
    return dictionary;
}
// ...

Step 5: Create a List of Duplicate Objects

Implement a method that iterates through dictionary items and creates Duplicate objects for items with a value greater than one (books with more than one copy). Add Duplicate objects into a DuplicatesList collection.

// ...
private DuplicatesList CreateDuplicatesList(Dictionary<string, int> duplicatesDictionary, IObjectSpace objectSpace) {
    DuplicatesList duplicatesList = objectSpace.CreateObject<DuplicatesList>();
    foreach(var (title, count) in duplicatesDictionary) {
        if(count <= 1) continue;

        var duplicate = objectSpace.CreateObject<Duplicate>();
        duplicate.Title = title;
        duplicate.Count = count;
        duplicatesList.Duplicates.Add(duplicate);
    }
    objectSpace.CommitChanges();
    return duplicatesList;
}
// ...

Step 6: Add PopupWindowShowAction

Add the PopupWindowShowAction to display a popup dialog when a user clicks the Show Duplicate Books action. Handle the CustomizePopupWindowParams event and call the CreateDetailView(IObjectSpace, Object) method to create a Detail View for the DuplicatesList object.

// ...
public ShowDuplicateBooksController() {
    PopupWindowShowAction showDuplicatesAction = new PopupWindowShowAction(this, "ShowDuplicateBooks", PredefinedCategory.View);
    showDuplicatesAction.CustomizePopupWindowParams += showDuplicatesAction_CustomizePopupWindowParams;
}
private void showDuplicatesAction_CustomizePopupWindowParams(object sender, CustomizePopupWindowParamsEventArgs e) {
    var duplicatesDictionary = GetDuplicatesDictionary();
    var nonPersistentObjectSpace = Application.CreateObjectSpace<DuplicatesList>();
    var duplicatesList = CreateDuplicatesList(duplicatesDictionary, nonPersistentObjectSpace);
    e.View = Application.CreateDetailView(nonPersistentObjectSpace, duplicatesList);
    e.DialogController.SaveOnAccept = false;
    e.DialogController.CancelAction.Active["NothingToCancel"] = false;
}
// ...
Show complete ShowDuplicateBooksController.cs code
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Actions;
using DevExpress.Persistent.Base;
using System.Collections;

namespace NonPersistentListView.Module {
    public class ShowDuplicateBooksController : ObjectViewController<ListView, Book> {
        public ShowDuplicateBooksController() {
            PopupWindowShowAction showDuplicatesAction = new PopupWindowShowAction(this, "ShowDuplicateBooks", PredefinedCategory.View);
            showDuplicatesAction.CustomizePopupWindowParams += showDuplicatesAction_CustomizePopupWindowParams;
        }
        private void showDuplicatesAction_CustomizePopupWindowParams(object sender, CustomizePopupWindowParamsEventArgs e) {
            var duplicatesDictionary = GetDuplicatesDictionary();
            var nonPersistentObjectSpace = Application.CreateObjectSpace<DuplicatesList>();
            var duplicatesList = CreateDuplicatesList(duplicatesDictionary, nonPersistentObjectSpace);
            e.View = Application.CreateDetailView(nonPersistentObjectSpace, duplicatesList);
            e.DialogController.SaveOnAccept = false;
            e.DialogController.CancelAction.Active["NothingToCancel"] = false;
        }
        private Dictionary<string, int> GetDuplicatesDictionary() {
            var dictionary = new Dictionary<string, int>();
            foreach(Book book in View.CollectionSource.List) {
                if(string.IsNullOrWhiteSpace(book.Title)) continue;
                if(dictionary.TryGetValue(book.Title, out int count)) {
                    dictionary[book.Title] = count + 1;
                } else {
                    dictionary[book.Title] = 1;
                }
            }
            return dictionary;
        }
        private DuplicatesList CreateDuplicatesList(Dictionary<string, int> duplicatesDictionary, IObjectSpace objectSpace) {
            DuplicatesList duplicatesList = objectSpace.CreateObject<DuplicatesList>();
            foreach(var (title, count) in duplicatesDictionary) {
                if(count <= 1) continue;
                var duplicate = objectSpace.CreateObject<Duplicate>();
                duplicate.Title = title;
                duplicate.Count = count;
                duplicatesList.Duplicates.Add(duplicate);
            }
            objectSpace.CommitChanges();
            return duplicatesList;
        }
    }
}

Result

The following images illustrate the implemented Action and its popup window.

Windows Forms Application

WinForms application: Non-persistent objects in a dialog popup

Blazor Application

Blazor application: Non-persistent objects in a dialog popup