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
C#namespace CustomEditorEF.Module.BusinessObjects;
public interface IPictureItem {
byte[] Image { get; }
string Text { get; }
}
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);
}
}
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);
}
}
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;
}
}