Implementing and Using Canvas Dialogs in Dynamics 365 with Custom Ribbon Actions

Facebook
Twitter
LinkedIn
  1. Introduction

In modern business applications, user experience is just as important as functionality. With Microsoft Dynamics 365, organizations are empowered to tailor their CRM interface and workflows to match internal processes and user needs. One of the more powerful features supporting this flexibility is Canvas Dialogs—configurable, interactive panels that can be embedded into user workflows.

Canvas Dialogs allow for the presentation of custom forms, data inputs, and actions in a streamlined, embedded format within the Dynamics 365 interface. Unlike traditional dialogs, these are more flexible, modern in design, and can be fully customized to display only what’s needed—when it’s needed.

Enter Modern Dialogs, a tailored implementation that leverages the capabilities of Canvas Dialogs while enabling dynamic integration with form ribbons. These dialogs aren’t just static UI forms—they can dynamically receive parameters, link to records, adapt based on context, and even respond to JavaScript logic for advanced control.

In this blog post, we’ll walk through the end-to-end process of setting up Modern Dialogs, including:

  • How dialogs are structured and stored using custom tables like aqc_canvasdialog
  • How to configure metadata and parameters to ensure flexibility
  • How to use JavaScript to dynamically display and run dialogs from the ribbon

Whether you’re building service request workflows or custom input forms for business processes, this post will give you a practical, developer-friendly guide to building and launching Modern Dialogs in your Dynamics 365 environment.

 

  1. Understanding the Data Structure

Before diving into configuration and UI customization, it’s important to understand how the data behind Canvas Dialogs is structured. Dynamics 365 provides a highly relational model, and in this implementation, a few custom tables define the core of how dialogs are organized and executed.

  1. Key Tables and Relationships
  2. aqc_canvasdialog

This is the primary table that stores individual dialog definitions. Each record in this table represents a unique Canvas Dialog that can be invoked from the UI.

Key Fields:

  • aqc_canvasdialogid (Primary Key)
  • aqc_categoryid (Lookup to aqc_dialogcategory)
  • aqc_name – Dialog title
  • aqc_entitylogicalname – Tied to the entity (e.g., account, contact)
  • aqc_imageresourcename – Optional icon resource
  • aqc_designtype, aqc_height, aqc_width, aqc_position – Used to control dialog appearance
  1. aqc_dialogcategory

Used to categorize dialogs. These categories are displayed as grouped flyouts in the ribbon UI, helping users find related dialogs quickly.

Key Field:

  • aqc_dialogcategoryid (Primary Key)

This table is related to aqc_canvasdialog via the aqc_categoryid lookup.

  1. aqc_dialogconfigurationparameter

This table stores the configuration parameters associated with each dialog. Parameters can be passed dynamically to dialogs when they are launched, enabling contextual behavior.

Key Fields:

  • aqc_dialogconfigurationparameterid (Primary Key)
  • aqc_canvasdialogid (Lookup to aqc_canvasdialog)
  • aqc_name – Parameter name
  • aqc_value – Default or dynamic value
  1. Entity Relationship Overview

To clarify the relationships, here’s a simplified structure:

ruby

 

  • A category can have many dialogs.
  • A dialog can have many configuration parameters.
  • These parameters help personalize and drive dynamic behaviors when the dialog is opened.

This relational setup allows for high modularity. Admins or developers can add, remove, or reassign dialogs and parameters without modifying the core logic—keeping everything flexible and maintainable.

 

  1. The Modern Dialog Configuration UI

Once the data structure is in place, the next step is to configure the actual dialogs in Dynamics 365. This is done through a custom UI typically accessed via a solution component—often labeled “Modern Dialogs” or something similar, depending on your organization’s naming conventions.

  1. Where to Access

The configuration UI for Modern Dialogs is generally accessible from within the app’s sitemap or a specific configuration area. Navigating to this interface brings you to a list of all existing Canvas Dialog definitions.

Each record in this interface represents one dialog configuration that can later be executed through the form ribbon or other automation.

  1. Dialog Metadata Fields

When creating or editing a dialog, several important fields define how the dialog will behave and display:

Field Purpose
aqc_name Display name of the dialog. Appears in the ribbon flyout.
aqc_entitylogicalname Links the dialog to a specific entity (e.g., account, contact).
aqc_designtype Determines size units (true = pixels, false = percent).
aqc_width / aqc_height Sets the dialog dimensions (applies based on designtype).
aqc_position Positioning of the dialog (e.g., Center, Side).
aqc_imageresourcename (Optional) Web resource name for the dialog icon shown in the ribbon.
aqc_categoryid Lookup to aqc_dialogcategory, used to group dialogs in the UI.

These fields provide full control over the dialog’s appearance and contextual relevance within the application.

  1. Parameter Configuration

After setting up the dialog, you can define parameters using the aqc_dialogconfigurationparameter table. These are especially useful when you want to pass values from the current record to the dialog form.

Example Parameters:

Name Value/Usage
accountid Passes the ID of the current account
aqc_servicerequesttype Could define dialog behavior or UI mode

These parameters are read by the HTML page loaded in the dialog (in this case, RunCanvasApp.html) and can be used to pre-fill fields or trigger logic inside the Canvas App or custom form.

  1. Visual Example

Dialogs configured with:

  • Entity: Account
  • Category: Service Request
  • Parameter: accountid

Will automatically appear under the “Service Request” flyout on the Account form’s ribbon. This allows users to initiate different dialog workflows based on the current context—without navigating away from the record form.

 

  1. Ribbon Integration: Making Dialogs Accessible

After configuring your Canvas Dialogs, the final piece of the puzzle is making them available to users—ideally right where they work most: the form ribbon. This section explains how dialogs are dynamically added as flyout buttons, how visibility is controlled, and how users can trigger dialogs with a single click.

  1. Dropdown Integration in the Form

In the Dynamics 365 form ribbon (e.g., on the Account entity form), users will see a dropdown—typically labeled something like “Service Request”. This dropdown is dynamically populated with the Canvas Dialogs you’ve configured for that entity.

 

Each button in the dropdown corresponds to a dialog that:

  • Is active (statecode = 0)
  • Matches the entity (aqc_entitylogicalname = “account”)
  • Belongs to the current user’s security context
  • Optionally, is grouped by category (from aqc_dialogcategory)

When clicked, the dialog is opened in a modal form using parameters and styling defined in the configuration.

  1. JavaScript Entry Point

The functionality is wired using a custom JavaScript file:
ApplicationRibbonCustomizations.js, specifically the function:

AQualityCRMRibbon.onRunCanvasAppClick(props, context)

This function is called when a user clicks a dialog button. Here’s what it does:

  1. Saves the form (context.data.save()).
  2. Fetches dialog config (design type, width, height, position).
  3. Prepares navigation parameters.
  4. Launches the dialog via Xrm.Navigation.navigateTo(…).

The dialog is rendered as a web resource page, typically named:
aqc_/html/RunCanvasApp.html

This page receives JSON data, including the dialog ID and the record context, allowing it to behave dynamically.

  1. Example Command Checker Metadata

The ribbon button uses the following metadata configuration:

  • FunctionName: AQualityCRMRibbon.onRunCanvasAppClick
  • Library: $webresource:aqc_/Scripts/ApplicationRibbonCustomizations.js
  • CrmParameter: PrimaryControl and CommandProperties
  • EnableRule: tam.ApplicationRibbon.ServiceRequest.EnableRule
  • DisplayRule: Mscrm.OutlookRenderTypeWeb

These configurations are typically set in the Ribbon Workbench or via solution XML.

  1. Why This Matters

This approach has several benefits:

  • Dynamic Behavior: Dialogs are tied to metadata, not hardcoded logic.
  • Reusable UI: The same JavaScript and web resource can serve multiple dialogs.
  • User-Friendly: Grouped buttons and icons improve usability and clarity.
  • Low Maintenance: Adding or updating dialogs doesn’t require redeployment—just data updates.

 

  1. Visibility and Population Logic

To provide a seamless and intelligent user experience, the ribbon integration doesn’t just statically show dialog buttons—it dynamically determines whether to show the flyout menu, and which dialogs to include, all based on the current entity and user context.

This dynamic behavior is controlled entirely through JavaScript, ensuring the UI stays clean, relevant, and adaptable.

  1. Dynamic Visibility

The function responsible for showing or hiding the Run Canvas App flyout is:

AQualityCRMRibbon.isRunCanvasAppVisible(context)

This function is linked to the ribbon’s Enable Rule. It performs the following steps:

  1. Retrieves the entity name from the current form.
  2. Calls getAppsConfigurations() to query the backend for active dialog records (statecode = 0) that match the entity.
  3. Sets a flag (configurationExist) to determine whether any dialogs are found.
  4. If dialogs exist, it refreshes the ribbon and returns true—allowing the button to appear.

This prevents users from seeing a ribbon option that has no available dialogs, keeping the UI clean and intuitive.

  1. Flyout Menu Generation

If dialogs are found for the current entity, the code generates an XML-based flyout menu using the following flow:

Key Function:

getAppsConfigurations(entityLogicalName, callback)

Steps Performed:

  • Sends a REST API request to fetch dialog records for the current entity.
  • For each dialog, retrieves:
    • Dialog name
    • Unique ID
    • Optional image icon
    • Category name (via formatted OData annotation)
  • Groups dialogs by category using a helper groupBy() function.
  • For each group:
    • Builds a <FlyoutAnchor> element if categorized.
    • Builds <Button> elements with labels, tooltips, and optional icons.
  • Assembles the XML into a global variable menuXml.
  • Updates the flyout’s XML via:

props.PopulationXML = menuXml;

This menu structure is what users ultimately see in the ribbon dropdown.

  1. Example: Grouped Menu Output

Dialogs with a aqc_categoryid appear under a grouped heading. Those without a category appear as ungrouped buttons. For example:

<MenuSection>

<Controls>

<FlyoutAnchor Id=”Service_Request”>

<Menu>

<Button LabelText=”Create Ticket” … />

<Button LabelText=”Escalate Case” … />

</Menu>

</FlyoutAnchor>

<Button LabelText=”General Feedback” … /> <!– No category –>

</Controls>

</MenuSection>

  1. Efficient UI Refresh

To avoid excessive API calls:

  • The system caches the result in configurationExist.
  • The ribbon only refreshes once per session unless a change is detected.

This ensures performance remains optimal, even with complex entities and multiple dialogs.

 

  1. Deep Dive: JavaScript Implementation

The power of this dialog system lies in its modular, metadata-driven JavaScript. The custom script—ApplicationRibbonCustomizations.js—handles everything from fetching dialog data to rendering flyout buttons and launching dialogs. Let’s break down the key components and how they work together.

  1. onRunCanvasAppClick: Launching the Dialog

 


AQualityCRMRibbon.onRunCanvasAppClick = function(props, context) {

// 1. Save current form

context.data.save().then(() => {

// 2. Retrieve dialog settings

Xrm.WebApi.online.retrieveRecord(“aqc_canvasdialog”, props.SourceControlId, “ ? $select = aqc_designtype, aqc_height, aqc_position, aqc_width”)

.then(result => {

// 3. Set dialog title dynamically

const titleContainer = parent.document.getElementById(“defaultDialogChromeTitle”);

if (titleContainer) {

titleContainer.innerHTML = result.aqc_name;

}

// 4. Build dialog parameters

const data = {

entityname: context.data.entity.getEntityName(),

entityid: context.data.entity.getId().replace(“{
“,
“”).replace(“
}”, “”),

dialogid: props.SourceControlId

};

// 5. Configure dialog dimensions and position

const dialogParameters = {

pageType: “webresource”,

webresourceName: “aqc_ / html / RunCanvasApp.html”,

data: JSON.stringify(data)

};

const navigationOptions = {

target: AQualityCRM.Crm.Common.Constants.navigateOptionsTarget.Dialog,

width: {

unit: result.aqc_designtype ? “px” : “ % ”,

value: result.aqc_width ?? 50

},

height: {

unit: result.aqc_designtype ? “px” : “ % ”,

value: result.aqc_height ?? 61

},

position: result.aqc_position ?? AQualityCRM.Crm.Common.Constants.navigateOptionsPosition.Center

};

// 6. Open dialog

Xrm.Navigation.navigateTo(dialogParameters, navigationOptions).catch(Xrm.Navigation.openErrorDialog);

})

.catch(Xrm.Navigation.openErrorDialog);

});

};

 

Summary: This function ensures the form is saved, then opens a web resource in a modal dialog using values from the selected Canvas Dialog record.

 

  1. isRunCanvasAppVisible: Control Ribbon Button Visibility

 

AQualityCRMRibbon.isRunCanvasAppVisible = function (context) {

const entityLogicalName = context.data.entity.getEntityName();

if (configurationExist === null) {

getAppsConfigurations(entityLogicalName, (dialogs) => {

configurationExist = dialogs.length > 0;

if (configurationExist) {

context.ui.refreshRibbon();

}

});

}

return configurationExist === true;

};

 

Purpose: Ensures that the ribbon flyout only appears if dialogs exist for the current entity.

 

  1. getAppsConfigurations: Fetching Available Dialogs

function getAppsConfigurations(entityLogicalName, callBack) {

const requestDialogs = new XMLHttpRequest();

requestDialogs.open(“GET”, Xrm.Utility.getGlobalContext().getClientUrl() + “/api/data/v9.0/aqc_canvasdialogs?$select=aqc_imageresourcename,aqc_entitylogicalname,aqc_name,aqc_canvasdialogid,_aqc_categoryid_value&$filter=aqc_entitylogicalname eq ‘” + entityLogicalName + “‘ and statecode eq 0&$orderby=aqc_name asc”, true);

requestDialogs.setRequestHeader(“Accept”, “application/json”);

requestDialogs.setRequestHeader(“Content-Type”, “application/json; charset=utf-8”);

requestDialogs.onreadystatechange = function () {

if (this.readyState === 4 && this.status === 200) {

const results = JSON.parse(this.responseText).value;

const dialogs = results.map(r => ({

entityLogicalName: r.aqc_entitylogicalname,

name: r.aqc_name,

id: r.aqc_canvasdialogid,

image: r.aqc_imageresourcename,

category: r[“[email protected]”]

}));

callBack(dialogs);

}

};

requestDialogs.send();

}

Purpose: Retrieves and formats the dialog data needed to build the menu.

 

  1. onRunCanvasAppPopulateFlyout: Setting the Menu XML

 

AQualityCRMRibbon.onRunCanvasAppPopulateFlyout = function (props) {

props.PopulationXML = menuXml;

};

 

This function applies the previously generated XML (menuXml) to populate the ribbon flyout.

 

  1. XML Menu Construction and Grouping

Dialogs are grouped and sorted using this helper:

function groupBy(xs, key) {

return xs.reduce((rv, x) => {

(rv[x[key]] = rv[x[key]] || []).push(x);

return rv;

}, {});

}

 

Each group is used to create a <FlyoutAnchor> with <Button> controls dynamically inserted into the ribbon.

Summary of Flow

  1. Ribbon loads → Checks visibility via isRunCanvasAppVisible().
  2. If visible, getAppsConfigurations() pulls dialog list.
  3. Dialogs grouped and rendered into XML.
  4. When user clicks dropdown, onRunCanvasAppPopulateFlyout() sets the flyout buttons.
  5. When a button is clicked, onRunCanvasAppClick() launches the dialog.

 

  1. Best Practices and Tips

Implementing Modern Canvas Dialogs in Dynamics 365 can add tremendous value—when done right. Here are some best practices and practical tips to ensure your dialog system is robust, scalable, and user-friendly.

✅ 1. Use Categories to Organize Dialogs

  • Group dialogs using the aqc_dialogcategory table.
  • Helps users quickly find the right dialog in the ribbon dropdown.
  • Categories become FlyoutAnchors, which are cleaner and more intuitive than a long list of flat buttons.

Tip: Use meaningful category names like “Service Requests”, “Feedback”, or “Internal Tools”.

✅ 2. Keep Dialog Dimensions Consistent

  • Be deliberate with aqc_width, aqc_height, and aqc_designtype:
    • Use % for responsive design.
    • Use px for fixed layout dialogs (like embedded forms or tightly controlled UI).

Recommended defaults:

  • Width: 50%
  • Height: 61%
  • Position: Center (unless you want a side panel)

✅ 3. Use Clear and Consistent Naming

  • Name your dialogs (aqc_name) clearly—this name appears in the ribbon.
  • Avoid special characters or cryptic codes unless standardized.
  • Use Pascal Case or Title Case (e.g., “Create New Request”, not “createnewrequest”).

✅ 4. Always Pass Contextual Parameters

  • Use aqc_dialogconfigurationparameter to send useful data like record IDs, types, or flags.
  • Avoid hardcoding anything in your HTML/web resource—rely on dynamic inputs.

Example parameters:

Parameter Name Purpose
accountid Passes current account ID
ownerid Passes user or team for assignment
mode Tells dialog to open in edit/view

✅ 5. Validate Web Resource References

  • Ensure image and script web resources are published and named correctly.
  • Missing icons or JS files will cause silent UI failures.
  • Use fallback images like aqc_/Images/Icons/Button/ServiceRequest.svg if needed.

✅ 6. Refresh the Ribbon After Adding Dialogs

  • The JavaScript automatically refreshes the ribbon (context.ui.refreshRibbon()), but this only works after the configurationExist flag is updated.
  • If your dialog doesn’t show up right away:
    • Check that the entity name (aqc_entitylogicalname) matches exactly.
    • Check that statecode = 0 (active).
    • Manually reload the form or publish customizations.

✅ 7. Test with Multiple Entities

  • Design dialogs to be reusable across entities if possible.
  • For example, a “Feedback” dialog could be attached to Accounts, Contacts, and Cases with minor config differences.

✅ 8. Keep the JavaScript Modular and Clean

  • Avoid embedding business logic inside the ribbon script.
  • Instead, push logic into the dialog web resource or Canvas App itself.
  • This keeps the ribbon script lightweight and easy to maintain.

 

Conclusion

Canvas Dialogs—when combined with custom ribbon integration—offer a powerful way to streamline user interactions in Microsoft Dynamics 365. By structuring dialogs around metadata, categories, and configuration parameters, you can create a dynamic, low-code interface that enhances productivity and supports consistent business processes.

Through this post, we’ve explored:

  • How dialogs are modeled using custom tables like aqc_canvasdialog and aqc_dialogcategory
  • How dialog metadata drives everything from layout to grouping
  • How JavaScript functions dynamically populate and control the ribbon UI
  • How to launch dialogs with precise control using parameters and runtime logic

What makes this approach stand out is its maintainability—adding or updating dialogs requires no code changes, just data updates. It also ensures user-friendliness with contextual dropdowns, dynamic visibility, and consistent styling.

Whether you’re building a service request interface, launching internal workflows, or guiding user actions through custom UIs, this pattern provides a solid, extensible foundation. Embrace it as a way to make your Dynamics 365 environment smarter, faster, and more aligned with your organization’s needs.