Example T1015880
Visible to All Users

XAF Blazor - Use a Custom Component to Implement List Editor

This example demonstrates how to implement a custom List Editor that shows images in an ASP.NET Core Blazor application.
The List Editor displays a Razor component with custom objects. These objects implement a custom IPictureItem interface to store images and their captions.

See the following help topic for more information: How to: Use a Custom Component to Implement List Editor (Blazor).

Files to Review

Documentation

More GitHub Examples

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

EF/CustomEditorEF/CustomEditorEF.Module/BusinessObjects/IPictureItem.cs
C#
namespace CustomEditorEF.Module.BusinessObjects; public interface IPictureItem { byte[] Image { get; } string Text { get; } }
EF/CustomEditorEF/CustomEditorEF.Blazor.Server/Editors/CustomList/PictureItemListView.razor
Razor
@using CustomEditorEF.Module.BusinessObjects; @using Microsoft.AspNetCore.Components.Web @if (Data is not null) { <div class="row"> @foreach (var item in Data) { <DxCheckBox Checked="@selectedItems.Contains(item)" CssClass="col-auto" CheckedChanged="@(async (bool isSelected) => await SelectItem(item, isSelected))"> <div style="cursor: pointer;" @onclick=@(async () => await ItemClick.InvokeAsync(item))> @if (item.Image is null) { <div class="border d-flex justify-content-center align-items-center" style="height:150px; width: 104px;"> No image </div> } else { <img src="data:image/png;base64,@Convert.ToBase64String(item.Image)" alt=@item.Text style="height:150px; width: 104px;"> } <div class="text-center" style="width: 104px;"> @item.Text </div> </div> </DxCheckBox> } </div> } @code { [Parameter] public IEnumerable<IPictureItem> Data { get; set; } [Parameter] public EventCallback<IPictureItem> ItemClick { get; set; } [Parameter] public EventCallback<IEnumerable<IPictureItem>> SelectionChanged { get; set; } private List<IPictureItem> selectedItems = new(); protected override async Task OnParametersSetAsync() { await base.OnParametersSetAsync(); var newSelectedItems = new List<IPictureItem>(); if (Data is not null) { foreach (var item in Data) { if (selectedItems.Contains(item)) { newSelectedItems.Add(item); } } } if (!newSelectedItems.SequenceEqual(selectedItems)) { await SelectionChanged.InvokeAsync(newSelectedItems); } selectedItems = newSelectedItems; } private async Task SelectItem(IPictureItem item, bool isSelected) { if (isSelected) { selectedItems.Add(item); } else { selectedItems.Remove(item); } await SelectionChanged.InvokeAsync(selectedItems); } }
EF/CustomEditorEF/CustomEditorEF.Blazor.Server/Editors/CustomList/PictureItemListViewModel.cs
C#
using CustomEditorEF.Module.BusinessObjects; using DevExpress.ExpressApp.Blazor.Components.Models; using Microsoft.AspNetCore.Components; namespace CustomEditorEF.Blazor.Server.Editors.CustomList { public class PictureItemListViewModel : ComponentModelBase { public IEnumerable<IPictureItem> Data { get => GetPropertyValue<IEnumerable<IPictureItem>>(); set => SetPropertyValue(value); } public EventCallback<IPictureItem> ItemClick { get => GetPropertyValue<EventCallback<IPictureItem>>(); set => SetPropertyValue(value); } public EventCallback<IEnumerable<IPictureItem>> SelectionChanged { get => GetPropertyValue<EventCallback<IEnumerable<IPictureItem>>>(); set => SetPropertyValue(value); } public override Type ComponentType => typeof(PictureItemListView); } }
EF/CustomEditorEF/CustomEditorEF.Blazor.Server/Editors/CustomList/BlazorCustomListEditor.cs
C#
using CustomEditorEF.Module.BusinessObjects; using DevExpress.ExpressApp; using DevExpress.ExpressApp.Blazor; using DevExpress.ExpressApp.Blazor.Components; using DevExpress.ExpressApp.Blazor.Components.Models; using DevExpress.ExpressApp.Editors; using DevExpress.ExpressApp.Model; using Microsoft.AspNetCore.Components; using System.Collections; using System.ComponentModel; namespace CustomEditorEF.Blazor.Server.Editors.CustomList { [ListEditor(typeof(IPictureItem))] public class BlazorCustomListEditor : ListEditor, IComponentContentHolder { private RenderFragment _componentContent; private IPictureItem[] selectedObjects = Array.Empty<IPictureItem>(); public PictureItemListViewModel ComponentModel { get; private set; } public RenderFragment ComponentContent { get { _componentContent ??= ComponentModelObserver.Create(ComponentModel, ComponentModel.GetComponentContent()); return _componentContent; } } public BlazorCustomListEditor(IModelListView model) : base(model) { } private void BindingList_ListChanged(object sender, ListChangedEventArgs e) { UpdateDataSource(DataSource); } private void UpdateDataSource(object dataSource) { if(ComponentModel is not null) { ComponentModel.Data = (dataSource as IEnumerable)?.OfType<IPictureItem>().OrderBy(i => i.Text); } } protected override object CreateControlsCore() { ComponentModel = new PictureItemListViewModel(); ComponentModel.ItemClick = EventCallback.Factory.Create<IPictureItem>(this, (item) => { selectedObjects = new IPictureItem[] { item }; OnSelectionChanged(); OnProcessSelectedItem(); }); ComponentModel.SelectionChanged = EventCallback.Factory.Create<IEnumerable<IPictureItem>>(this, (items) => { selectedObjects = items.ToArray(); OnSelectionChanged(); }); return ComponentModel; } protected override void AssignDataSourceToControl(object dataSource) { if(ComponentModel is not null) { if(ComponentModel.Data is IBindingList bindingList) { bindingList.ListChanged -= BindingList_ListChanged; } UpdateDataSource(dataSource); if(dataSource is IBindingList newBindingList) { newBindingList.ListChanged += BindingList_ListChanged; } } } public override void BreakLinksToControls() { AssignDataSourceToControl(null); base.BreakLinksToControls(); } public override void Refresh() => UpdateDataSource(DataSource); public override SelectionType SelectionType => SelectionType.Full; public override IList GetSelectedObjects() => selectedObjects; } }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.