Skip to main content

Customize CRUD Forms

  • 5 minutes to read

This topics explains how to customize built-in CRUD forms and how to create custom forms.

Configure Form Templates

You can specify the DataGridView.DetailFormTemplate, DataGridView.DetailNewItemFormTemplate, and DetailEditFormTemplate properties to implement custom view and edit detail forms.

The DataGridView passes a view model to the template’s BindingContext. Use this object to access information about the edited or displayed item and call such commands as Delete, Edit, and Close from your custom form template. Depending on the form type, the DataGridView passes a DetailFormViewModel (View form) or DetailEditFormViewModel (Edit and Create forms) object as a BindingContext for form templates.

The following example shows how to invoke a custom edit form page for a tapped item. To implement a custom edit form, a DataFormView object is used:

DevExpress DataGridView for MAUI - Custom edit form

<ContentPage.BindingContext>
    <local:TestOrderRepository/>
</ContentPage.BindingContext>
    <dxg:DataGridView ItemsSource="{Binding Orders}"
                        RowTapCommand="{Binding Source={RelativeSource Mode=Self}, Path=Commands.ShowDetailEditForm}" 
                        DetailEditFormTemplate="{DataTemplate local:OrderEditForm}">
    </dxg:DataGridView>
Show OrderEditForm implementation
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:dxdf="clr-namespace:DevExpress.Maui.DataForm;assembly=DevExpress.Maui.Editors"
            x:Class="DXFilteringApp.OrderEditForm"
            Title="Edit order details"
            BackgroundColor="#F4F1F5">
   <ContentPage.ToolbarItems>
       <ToolbarItem Text="Save" Clicked="ToolbarItem_Clicked"/>
   </ContentPage.ToolbarItems>
   <VerticalStackLayout>
       <dxdf:DataFormView x:Name="dataForm" 
                          DataObject="{Binding Item}" 
                          IsAutoGenerationEnabled="False" 
                          BackgroundColor="#F4F1F5"
                          ValidateProperty="dataForm_ValidateProperty">
           <!-- This combo box obtains available items from a collection of values: -->
           <dxdf:DataFormComboBoxItem FieldName="ProductName" 
                                      ItemsSource="{Binding DataControlContext.Products}" 
                                      LabelText="Product" 
                                      IsFilterEnabled="True" 
                                      PickerShowMode="BottomSheet"/>
           <dxdf:DataFormDateItem FieldName="Date" LabelText="Date" 
                                  DisplayFormat="{}{0:MMMM d, yyyy}"/>
           <dxdf:DataFormNumericItem FieldName="Quantity" LabelText="Quantity"/> 
           <dxdf:DataFormSwitchItem FieldName="FastDelivery" LabelText="Fast Delivery"/>
           <!-- This combo box obtains available items from an enum: -->
           <dxdf:DataFormComboBoxItem FieldName="PackagingType" LabelText="Packaging Type" 
                                      PickerShowMode="BottomSheet"/>
       </dxdf:DataFormView>
   </VerticalStackLayout>
</ContentPage>
using DevExpress.Maui.Core;
namespace GridCrudTestApp;
public partial class OrderEditForm : ContentPage {
   DetailEditFormViewModel ViewModel => (DetailEditFormViewModel)BindingContext;
   public OrderEditForm() {
       InitializeComponent();
   }
   private void ToolbarItem_Clicked(object sender, EventArgs e) {
       if (!this.dataForm.Validate())
           return;
       this.dataForm.Commit();
       ViewModel.Save();
   }
   private void dataForm_ValidateProperty(object sender, DevExpress.Maui.DataForm.DataFormPropertyValidationEventArgs e) {
       if (e.PropertyName == "Quantity" && (decimal)e.NewValue <= 0) {
           e.ErrorText = "Cannot be less or equal to 0";
           e.HasError = true;
       }
   }
}
Show MainPage BindingContext implementation
using DevExpress.Maui.DataForm;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace GridCrudTestApp;
public abstract class OrderRepository {
   readonly ObservableCollection<Order> orders;
   ObservableCollection<string> products = new ObservableCollection<string> { "Tofu", "Chocolade", "Ikura", "Chai", "Boston >Crab Meat", "Ravioli Angelo", "Ipon Coffee", "Queso Cabrales" };
   public OrderRepository() {
       this.orders = new ObservableCollection<Order>();
   }
   public ObservableCollection<Order> Orders {
       get { return orders; }
   }
   public ObservableCollection<string> Products { get { return products; } }
}
public class TestOrderRepository : OrderRepository {
   const int orderCount = 20;
   readonly Random random;
   public TestOrderRepository() : base() {
       this.random = new Random((int)DateTime.Now.Ticks);
       for (int i = 0; i < orderCount; i++)
           Orders.Add(GenerateOrder(i));
   }
   Order GenerateOrder(int number) {
       Order order = new Order(
           new DateTime(2022, 9, 1).AddDays(random.Next(0, 60)),
           number % 3 == 0,
           RandomItem<string>(Products),
           random.Next(1, 100),
           (PackagingType)random.Next(Enum.GetNames(typeof(PackagingType)).Length));
       return order;
   }
   T RandomItem<T>(IList<T> list) {
       int index = (int)(random.NextDouble() * 0.99 * (list.Count));
       return list[index];
   }
}
public class Order : INotifyPropertyChanged {
   string productName;
   DateTime date;
   bool fastDelivery;
   int quantity;
   PackagingType packagingType;
   public Order() {
       this.productName = "Tofu";
       this.date = DateTime.Today;
       this.fastDelivery = false;
       this.quantity = 0;
       this.packagingType = PackagingType.CardboardBox;
   }
   public Order(DateTime date, bool fastDelivery, string productName, int quantity, PackagingType packagingType) {
       this.productName = productName;
       this.date = date;
       this.fastDelivery = fastDelivery;
       this.quantity = quantity;
       this.packagingType = packagingType;
   }
   public string ProductName {
       get { return productName; }
       set {
           if (productName != value) {
               productName = value;
               RaisePropertyChanged("ProductName");
           }
       }
   }
   public int Quantity {
       get { return quantity; }
       set {
           if (quantity != value) {
               quantity = value;
               RaisePropertyChanged("Quantity");
           }
       }
   }
   [DataFormDateEditor(DisplayFormat = "{0:MMMM dd}")]
   public DateTime Date {
       get { return date; }
       set {
           if (date != value) {
               date = value;
               RaisePropertyChanged("Date");
           }
       }
   }
   public bool FastDelivery {
       get { return fastDelivery; }
       set {
           if (fastDelivery != value) {
               fastDelivery = value;
               RaisePropertyChanged("FastDelivery");
           }
       }
   }
   public PackagingType PackagingType {
       get { return packagingType; }
       set {
           if (packagingType != value) {
               packagingType = value;
               RaisePropertyChanged("PackagingType");
           }
       }
   }
   public event PropertyChangedEventHandler PropertyChanged;
   protected void RaisePropertyChanged(string name) {
       if (PropertyChanged != null)
           PropertyChanged(this, new PropertyChangedEventArgs(name));
   }
}
public enum PackagingType { CardboardBox, CorrugatedBox, ClingFilm, PlasticBox, Chipboard }

If you use the DataFormView component to implement custom edit forms, you can use its built-in mechanisms to validate input values. For more information, refer to the following help topic: Validate User Input in Data Form for .NET MAUI.

In other cases, you can handle the DataGridView.ValidateAndSave event to validate data before it is saved to the source. For more information, refer to the following help topic: Validate and Save Data.

Use Data Form View Attributes

DataGridView uses the DataFormView component to generate the predefined detail forms. You can assign attributes to the source object class properties to configure generated editors. For more information, refer to the following section: Specify Editor Properties.

The example below configures the display format for a source item’s DateTime property:

DevExpress DataGridView for MAUI -- Apply DataForm attributes to source item properties

using DevExpress.Maui.DataForm;
//...
    public class Order : INotifyPropertyChanged {
        DateTime date;
        [DataFormDateEditor(DisplayFormat = "{0:MMMM dd}")]
        public DateTime Date {
            get { return date; }
            set {
                if (date != value) {
                    date = value;
                    RaisePropertyChanged("Date");
                }
            }
        }
        //...
    }

Specify Form Settings

Handle the DetailFormShowing event to customize view/edit form options. The following example specifies the page title and Save button caption:

DevExpress DataGridView for MAUI -- Customize default form

<dxg:DataGridView ...
                  DetailFormShowing="grid_DetailFormShowing">
    <!--...-->
</dxg:DataGridView>
private void grid_DetailFormShowing(object sender, DetailFormShowingEventArgs e) {
    if (e.ViewModel is not DetailEditFormViewModel editViewModel)
        return;
    bool isNew = editViewModel.IsNew;
    if (isNew) {
        DetailEditFormPage form = (DetailEditFormPage)e.Content;
        form.TitleLabel.Text = "Configure order parameters";
        form.SaveButton.Text = "Save my order";
    }
}