Example T1192313
Visible to All Users

XAF - How to Display an Event with Custom Fields in a Scheduler List View

This example demonstrates how to extend Scheduler events with custom fields and display these values in UI.

ASP.NET Core Blazor

|

Windows Forms

  1. Inherit from the Event class and implement new properties. For details, see the following file: ExtendedEvent.cs.
  2. Map new properties to appropriate data fields.
    In an ASP.NET Core Blazor application, access CustomFieldMappings in the AddScheduler() method. For details, see the following file: Startup.cs.
    In a Windows Forms application, specify CustomFieldMappings. For details, see the following file: SchedulerCustomFieldMappingsController.cs.
  3. Display field values in event cards of a Scheduler List View.
    In an XAF ASP.NET Core Blazor application:

Files to Review

Does this example address your development requirements/objectives?

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

Example Code

EFCore/ExtendedEvents.Module/BusinessObjects/ExtendedEvent.cs
C#
using DevExpress.Persistent.Base; using DevExpress.Persistent.BaseImpl.EF; using DevExpress.Persistent.Validation; using DevExpress.Xpo; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ExtendedEvents.Module.BusinessObjects; [DefaultClassOptions] public class ExtendedEvent : Event { public virtual string CustomSimpleTypeField { get; set; } public virtual CustomReferenceTypeField CustomReferenceTypeField { get; set; } } public class CustomReferenceTypeField : BaseObject { public virtual string Name { get; set; } }
EFCore/ExtendedEvents.Blazor.Server/Startup.cs
C#
using DevExpress.ExpressApp.Security; using DevExpress.ExpressApp.ApplicationBuilder; using DevExpress.ExpressApp.Blazor.ApplicationBuilder; using DevExpress.ExpressApp.Blazor.Services; using DevExpress.Persistent.Base; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Components.Server.Circuits; using Microsoft.EntityFrameworkCore; using ExtendedEvents.Blazor.Server.Services; using DevExpress.Persistent.BaseImpl.EF.PermissionPolicy; using DevExpress.Blazor; using ExtendedEvents.Module.BusinessObjects; namespace ExtendedEvents.Blazor.Server; public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddSingleton(typeof(Microsoft.AspNetCore.SignalR.HubConnectionHandler<>), typeof(ProxyHubConnectionHandler<>)); services.AddRazorPages(); services.AddServerSideBlazor(); services.AddHttpContextAccessor(); services.AddScoped<CircuitHandler, CircuitHandlerProxy>(); services.AddXaf(Configuration, builder => { builder.UseApplication<ExtendedEventsBlazorApplication>(); builder.Modules .AddConditionalAppearance() .AddScheduler(options => { options.Events.OnSchedulerDataStorageCreated += context => { context.SchedulerDataStorage.AppointmentMappings.CustomFieldMappings = new[] { new DxSchedulerCustomFieldMapping { Name = "SimpleField", Mapping = nameof(ExtendedEvent.CustomSimpleTypeField) }, new DxSchedulerCustomFieldMapping { Name = "ReferenceField", Mapping = $"{nameof(ExtendedEvent.CustomReferenceTypeField)}.{nameof(CustomReferenceTypeField.Name)}" } }; }; }) .AddValidation(options => { options.AllowValidationDetailsAccess = false; }) .Add<ExtendedEvents.Module.ExtendedEventsModule>() .Add<ExtendedEventsBlazorModule>(); builder.ObjectSpaceProviders .AddSecuredEFCore(options => options.PreFetchReferenceProperties()) .WithDbContext<ExtendedEvents.Module.BusinessObjects.ExtendedEventsEFCoreDbContext>((serviceProvider, options) => { // Uncomment this code to use an in-memory database. This database is recreated each time the server starts. With the in-memory database, you don't need to make a migration when the data model is changed. // Do not use this code in production environment to avoid data loss. // We recommend that you refer to the following help topic before you use an in-memory database: https://docs.microsoft.com/en-us/ef/core/testing/in-memory //options.UseInMemoryDatabase("InMemory"); string connectionString = null; if(Configuration.GetConnectionString("ConnectionString") != null) { connectionString = Configuration.GetConnectionString("ConnectionString"); } #if EASYTEST if(Configuration.GetConnectionString("EasyTestConnectionString") != null) { connectionString = Configuration.GetConnectionString("EasyTestConnectionString"); } #endif ArgumentNullException.ThrowIfNull(connectionString); options.UseSqlServer(connectionString); options.UseChangeTrackingProxies(); options.UseObjectSpaceLinkProxies(); options.UseLazyLoadingProxies(); }) .AddNonPersistent(); builder.Security .UseIntegratedMode(options => { options.RoleType = typeof(PermissionPolicyRole); // ApplicationUser descends from PermissionPolicyUser and supports the OAuth authentication. For more information, refer to the following topic: https://docs.devexpress.com/eXpressAppFramework/402197 // If your application uses PermissionPolicyUser or a custom user type, set the UserType property as follows: options.UserType = typeof(ExtendedEvents.Module.BusinessObjects.ApplicationUser); // ApplicationUserLoginInfo is only necessary for applications that use the ApplicationUser user type. // If you use PermissionPolicyUser or a custom user type, comment out the following line: options.UserLoginInfoType = typeof(ExtendedEvents.Module.BusinessObjects.ApplicationUserLoginInfo); options.Events.OnSecurityStrategyCreated += securityStrategy => { // Use the 'PermissionsReloadMode.NoCache' option to load the most recent permissions from the database once // for every DbContext instance when secured data is accessed through this instance for the first time. // Use the 'PermissionsReloadMode.CacheOnFirstAccess' option to reduce the number of database queries. // In this case, permission requests are loaded and cached when secured data is accessed for the first time // and used until the current user logs out. // See the following article for more details: https://docs.devexpress.com/eXpressAppFramework/DevExpress.ExpressApp.Security.SecurityStrategy.PermissionsReloadMode. ((SecurityStrategy)securityStrategy).PermissionsReloadMode = PermissionsReloadMode.NoCache; }; }) .AddPasswordAuthentication(options => { options.IsSupportChangePassword = true; }); }); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => { options.LoginPath = "/LoginPage"; }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if(env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. To change this for production scenarios, see: https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseRequestLocalization(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseXaf(); app.UseEndpoints(endpoints => { endpoints.MapXafEndpoints(); endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); endpoints.MapControllers(); }); } }
EFCore/ExtendedEvents.Win/Controllers/SchedulerCustomFieldMappingsController.cs
C#
using DevExpress.ExpressApp; using DevExpress.ExpressApp.Scheduler.Win; using DevExpress.XtraScheduler; using ExtendedEvents.Module.BusinessObjects; namespace ExtendedEvents.Win.Controllers; public class SchedulerCustomFieldMappingsController : ObjectViewController<ListView, ExtendedEvent> { private SchedulerListEditor listEditor; protected override void OnViewControlsCreated() { base.OnViewControlsCreated(); if (View.Editor is SchedulerListEditor listEditor) { listEditor.SchedulerControl.Storage.Appointments.CustomFieldMappings.AddRange(new[] { new AppointmentCustomFieldMapping("SimpleField", nameof(ExtendedEvent.CustomSimpleTypeField)), new AppointmentCustomFieldMapping("ReferenceField", nameof(ExtendedEvent.CustomReferenceTypeField)) }); SchedulerControl scheduler = listEditor.SchedulerControl; scheduler.InitAppointmentDisplayText -= scheduler_InitAppointmentDisplayText; scheduler.InitAppointmentDisplayText += scheduler_InitAppointmentDisplayText; } } protected override void OnDeactivated() { base.OnDeactivated(); if (listEditor != null && listEditor.SchedulerControl != null) { SchedulerControl scheduler = listEditor.SchedulerControl; scheduler.InitAppointmentDisplayText -= scheduler_InitAppointmentDisplayText; } } private void scheduler_InitAppointmentDisplayText(object sender, AppointmentDisplayTextEventArgs e) { Appointment appointment = e.Appointment; if (appointment.IsRecurring) appointment = e.Appointment.RecurrencePattern; // Obtain source object if needed //ExtendedEvent obj = (ExtendedEvent)listEditor.SourceObjectHelper.GetSourceObject(appointment); var referencePropertyValue = ((CustomReferenceTypeField)appointment.CustomFields["ReferenceField"])?.Name; e.Text = $"{appointment.CustomFields["SimpleField"]} - {referencePropertyValue}"; } }
EFCore/ExtendedEvents.Blazor.Server/CustomAppointmentTemplate.razor
Razor
<div class="card shadow-sm bg-white p-2" style="overflow: hidden; height: 100%;box-shadow: .125rem .25rem rgba(34,34,34,0.15)"> <div><span class="badge mb-1 @Context.Status.CssClass" style="white-space: pre-wrap;"></span></div> <span class="text-dark ps-0 mb-1">@Context.Appointment.CustomFields["SimpleField"]</span> <span class="text-dark ps-0 mb-1">@Context.Appointment.CustomFields["ReferenceField"]</span> @*obtain source object if needed @Context.Appointment.SourceObject*@ </div> @code { public static RenderFragment<DxSchedulerAppointmentView> Create() { return (DxSchedulerAppointmentView schedulerAppointmentView) => @<CustomAppointmentTemplate Context="@schedulerAppointmentView" />; } [Parameter] public DxSchedulerAppointmentView Context { get; set; } }
EFCore/ExtendedEvents.Blazor.Server/Controllers/SchedulerCustomFieldMappingsController.cs
C#
using DevExpress.Blazor; using DevExpress.ExpressApp; using DevExpress.ExpressApp.Scheduler.Blazor.Editors; using ExtendedEvents.Module.BusinessObjects; namespace ExtendedEvents.Blazor.Server.Controllers; public class SchedulerCustomFieldMappingsController : ObjectViewController<ListView, ExtendedEvent> { protected override void OnViewControlsCreated() { base.OnViewControlsCreated(); var schedulerAdapter = ((SchedulerListEditor)View.Editor).GetSchedulerAdapter(); schedulerAdapter.DayViewModel.VerticalAppointmentTemplate = CustomAppointmentTemplate.Create(); schedulerAdapter.DayViewModel.HorizontalAppointmentTemplate = CustomAppointmentTemplate.Create(); } }

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.