Skip to main content
All docs
V24.1

WPF Best Practices

  • 4 minutes to read

#Create a Data Model for WPF Applications

#Recommendation

When you create data model classes for WPF applications, inherit XPO classes from the PersistentBase class.

It is not recommended to use the following classes as base classes:

#Detailed Explanation

Classes that inherit XPObject, XPLiteObject, XPCustomObject or XPBaseObject have the following limitations:

  • The TwoWay binding mode does not work with nullable properties: a user cannot clear the editor’s value. Editors with empty values display a validation error. Binding to nullable properties works correctly if a persistent class inherits PersistentBase.
  • You should use Virtual Properties (PropertyName! and PropertyName!Key) to bind a collection to a property that references another XPO class. See the following article for more information: How to: Bind an XPCollection to a LookUp.

If you change a base class to PersistentBase, you can lose some functionaly. Follow the recommendations below:

  • The autoincrementing OID property.

    Add a property of the Int32 type to your class and decorate this property with the Key attribute.

  • Optimistic Concurrency Control.

    Decorate a class with the OptimisticLocking attribute.

  • Deferred Object Deletion.

    Decorate a class with the DeferredDeletion attribute.

  • The GetCollection method that is required to build a relationship between objects.

    To implement the “Many” side of a relationship, declare a property of the IList<T> type and use the GetList method instead of GetCollection.

  • The Save and Delete methods.

    Use the corresponding Session methods instead.

  • The EvaluateAlias method.

    It is not necessary to use the EvaluateAlias method to implement a calculated property because you can calculate a property value in code. If you want to use EvaluateAlias, you can implement it as demonstrated in the example below.

The code sample below shows how to implement a custom base class that enables only the required features:

using System.Runtime.CompilerServices;

using DevExpress.Data.Filtering;
using DevExpress.Data.Filtering.Helpers;
using DevExpress.Xpo;
using DevExpress.Xpo.Metadata;
//...
    [NonPersistent]
    [DeferredDeletion]
    [OptimisticLocking]
    public class BaseObject : PersistentBase {
        public BaseObject(Session session) : base(session) { }

        [Key(true)]
        [Persistent("OID")]
        private int fOid;
        [PersistentAlias("fOid")]
        public int Oid {
            get => fOid;
        }
        public object EvaluateAlias([CallerMemberName] string memberName = null) {
            XPMemberInfo mi = ClassInfo.GetMember(memberName);
            PersistentAliasAttribute aa = (PersistentAliasAttribute)mi.GetAttributeInfo(typeof(PersistentAliasAttribute));
            EvaluatorContextDescriptor descriptor = ClassInfo.GetEvaluatorContextDescriptor();
            CriteriaOperator criteria = CriteriaOperator.Parse(aa.AliasExpression);
            ExpressionEvaluator evaluator = new ExpressionEvaluator(descriptor, criteria, Session.CaseSensitive, Session.Dictionary.CustomFunctionOperators, Session.Dictionary.CustomAggregates);
            return evaluator.Evaluate(this);

        }
    }

#Query Data in WPF Applications

#Recommendation

To bind a data-aware control to a collection of objects, use the ObservableCollection<T> class instead of XPCollection or XPView.

Use LINQ to XPO to populate ObservableCollection<T> with data.

// a helper method to support anonymous types
static ObservableCollection<T> ToObservableCollection<T> (this IEnumerable<T> en) { 
    return new ObservableCollection<T>(en); 
}

// examples
var orders = session.Query<Order>().ToObservableCollection();
var customerNames = session.Query<Customer>()
    .Select(c => string.Concat(c.FirstName, " ", c.LastName))
    .ToObservableCollection();

#Detailed Explanation

XPCollection does not raise the ListChanged event when a user changes an editor value if a persistent class is inherited from XPObject, XPLiteObject, XPCustomObject or XPBaseObject. As a result, other editors or controls bound to the modified property may display the previous value until the IEditableObject.EndEdit method is called.

XPView contains ViewRecords instead of XPO objects. This feature is not useful in MVVM applications where the business logic in a ViewModel requires a Model instance (an XPO object).