Skip to main content
All docs
V23.2
.NET 6.0+

Display and Edit Simple Type Values in a Lookup Property Editor

  • 9 minutes to read

This help topic describes how to allow end users to select a property value of a simple type (string, integer, enumeration, and so on) in a lookup editor.

Important

The examples demonstrated in this help topic require calculation on the client side and do not support Server, ServerView, InstantFeedback, and InstantFeedbackView modes.

Populate the Lookup Editor with Values from Another Property

This section demonstrates how to display user-friendly strings instead of simple type values in a lookup editor.

Scenario

Suppose you have the following class and want to display user-friendly strings for the Position integer property:

File: MySolution.Module\BusinessObjects\DemoClass.cs(.vb).

using DevExpress.Persistent.Base;
using System.ComponentModel.DataAnnotations;
//...
[DefaultClassOptions]
public class DemoClass : BaseObject {
    public virtual int Position { get; set; }
}

// Make sure that you use options.UseChangeTrackingProxies() in your DbContext settings.

Solution

  1. Apply the Browsable attribute to the Position property to hide its editor from the UI:

    using System.ComponentModel;
    //...
    public class DemoClass : BaseObject {
        //...
        [Browsable(false)]
        public virtual int Position { get; set; }
    }
    
    // Make sure that you use options.UseChangeTrackingProxies() in your DbContext settings.
    
  2. Create an additional PositionPropertyWrapper class that is a wrapper for the DemoClass.Position property. Apply the DomainComponent attribute to this class to make it non-persistent. In PositionPropertyWrapper, the Key property stores the original integer values, and the DisplayName property returns their user-friendly string representations.

    File: MySolution.Module\BusinessObjects\PositionPropertyWrapper.cs(.vb).

    using DevExpress.ExpressApp.DC;
    //...
    [DomainComponent, XafDefaultProperty(nameof(PositionName))]
    public class PositionPropertyWrapper {
        private int _key;
        private string _positionName;
        public PositionPropertyWrapper(string positionName, int key) {
            this._key = key;
            this._positionName = positionName;
        }
        [DevExpress.ExpressApp.Data.Key]
        public int Key { get { return _key; } }
        public string PositionName { get { return _positionName; } }
    }
    

    Important

    Use the Key attribute from the DevExpress.ExpressApp.Data namespace only (not from the System.ComponentModel.DataAnnotations or DevExpress.Xpo namespaces). This attribute is required for XAF ASP.NET Web Forms and ASP.NET Core Blazor applications.

  3. Extend DemoClass with the PositionDataSource property that stores the collection of non-persistent PositionPropertyWrapper objects. The Position lookup editor displays these objects. Apply the Browsable attribute to the PositionDataSource property to hide its editor from the UI:

    File: MySolution.Module\BusinessObjects\DemoClass.cs(.vb).

    using System.ComponentModel;
    using System.ComponentModel.Annotations;
    //...
    public class DemoClass : BaseObject {
        //...
        private BindingList<PositionPropertyWrapper> _positionDataSource;
        [NotMapped, Browsable(false)]
        public BindingList<PositionPropertyWrapper> PositionDataSource {
            get {
                if (_positionDataSource == null) {
                    _positionDataSource = new BindingList<PositionPropertyWrapper>();
                    for (int i = 0; i < 5; i++) {
                        _positionDataSource.Add(new PositionPropertyWrapper("Position" + i.ToString(), i));
                    }
                }
                return _positionDataSource;
            }
        }
    }
    
    // Make sure that you use options.UseChangeTrackingProxies() in your DbContext settings.
    
  4. Extend DemoClass with the non-persistent PositionWrapper property of the PositionPropertyWrapper type to update the persistent Position property. Apply the DataSourceProperty attribute to PositionWrapper to populate the lookup editor data source:

    using System.Linq;
    using System.ComponentModel.Annotations;
    // ...
    public class DemoClass : BaseObject {
        // ...
        private PositionPropertyWrapper _positionPropertyWrapper;
        [NotMapped, XafDisplayName("Position")]
        [DataSourceProperty(nameof(PositionDataSource))]
        public PositionPropertyWrapper PositionWrapper {
            get {
                if (_positionPropertyWrapper == null || _positionPropertyWrapper.Key != Position) {
                    _positionPropertyWrapper = PositionDataSource.FirstOrDefault(i => i.Key == Position);
                }
                return _positionPropertyWrapper;
            }
            set {
                _positionPropertyWrapper = value;
                Position = value.Key;
            }
        }
    }
    
    // Make sure that you use options.UseChangeTrackingProxies() in your DbContext settings.
    
  5. Optional. You can also hide the lookup editor’s Clear button for a non-nullable PositionWrapper property. To do this, set its AllowClear property to false in the Model Editor or apply ModelDefaultAttribute to PositionWrapper:

    using DevExpress.ExpressApp.Model;
    // ...
    public class DemoClass : BaseObject {
        // ...
        [ModelDefault("AllowClear", "false")]
        public PositionPropertyWrapper PositionWrapper {
            // ...
        }
    }
    

Populate the Lookup Editor with Persistent Business Objects

This section demonstrates how to display a lookup editor with persistent objects instead of a simple type editor. Use this technique if you have legacy databases and cannot modify their schemas to create associations between tables.

Scenario

Suppose you have the following classes and want to display the Position.Title lookup editor instead of the DemoClass.PositionTitle string editor.

using DevExpress.ExpressApp.DC;
using DevExpress.Persistent.Base;
using DevExoress.Persistent.BaseImpl.EF;
using System.ComponentModel.DataAnnotations;
//...
[DefaultClassOptions]
public class DemoClass : BaseObject {
    public virtual string PositionTitle { get; set; }
}

[DefaultClassOptions, XafDefaultProperty(nameof(Title))]
public class Position : BaseObject {
    public virtual string Title { get; set; }
}

// Make sure that you use options.UseChangeTrackingProxies() in your DbContext settings.

Solution

  1. Apply the Browsable attribute to the PositionTitle property to hide its editor from the UI:

    using System.ComponentModel;
    //...
    public class DemoClass : BaseObject {
        //...
        [Browsable(false)]
        public string PositionTitle {
            //...
        }
    }
    
  2. Create a non-persistent wrapper property (LookupPropertyForDisplay) to fetch records from the Position data table:

    using DevExpress.Data.Filtering;
    using DevExpress.ExpressApp.DC;
    //...
    public class DemoClass : BaseObject {
        //...
        private Position _LookupPropertyForDisplay;
        [NotMapped, XafDisplayName("Position")]
        public virtual Position LookupPropertyForDisplay {
            get {
                if ((_LookupPropertyForDisplay == null && !string.IsNullOrEmpty(positionTitle)) || 
                (_LookupPropertyForDisplay != null && _LookupPropertyForDisplay.Title != PositionTitle)) {
                    _LookupPropertyForDisplay = 
                        ObjectSpace.GetObjectsQuery<Position>().FirstOrDefault(t => t.Title == positionTitle);
                }
                return _LookupPropertyForDisplay;
            }
            set {
                _LookupPropertyForDisplay = value;
                PositionTitle = value != null ? value.Title : string.Empty;
            }
        }
    }
    
    // Make sure that you use options.UseChangeTrackingProxies() and options.UseObjectSpaceLinkProxies() in your DbContext settings.
    

Notes