Ext.NET Dashboard Layout with Dynamic Tab Management and Modal Dialogs

This article concludes the Ext.NET dashboard layout series, focusing on practical client-side interaction patterns for enterprise web applications.

Core JavaScript Utilities

1. Dynamic Tab Creation

The following function adds or activates a tab in the central TabPanel, loading content via iframe with visual feedback:

function createOrActivateTab(tabId, sourceUrl, displayTitle) {
    const tabContainer = Ext.getCmp('mainTabPanel');
    let existingTab = tabContainer.getComponent(tabId);

    if (!existingTab) {
        existingTab = tabContainer.add({
            id: tabId,
            title: displayTitle,
            closable: true,
            autoLoad: {
                showMask: true,
                url: sourceUrl,
                mode: 'iframe',
                maskMsg: `Loading ${displayTitle}...`
            }
        });
    }

    tabContainer.setActiveTab(existingTab);
}

2. Custom Notification System

A reusable notification utility with animated entry/exit effects and timestamped titles:

function displayNotification(header, bodyText) {
    Ext.net.Notification.show({
        hideFx: { fxName: 'switchOff', args: [{}] },
        showFx: { 
            fxName: 'frame',
            args: ['C3DAF9', 1, { duration: 2.0 }]
        },
        closeVisible: true,
        html: bodyText,
        title: `${header} — ${new Date().format('g:i:s A')}`
    });
}

3. Configurable Modal Window

Generates an iframe-based modal window with optional maximization, cleanup logic, and cross-frame communication:

function launchModalFrame(targetUrl, windowId, windowTitle, shouldMaximize = false) {
    // Append cache-busting parameter
    const urlWithRand = targetUrl + (targetUrl.includes('?') ? '&' : '?') + `rand=${Math.floor(Math.random() * 10000)}`;

    const modalWindow = new Ext.Window({
        id: windowId,
        title: windowTitle,
        width: 572,
        height: 290,
        iconCls: 'addicon',
        resizable: false,
        draggable: true,
        collapsible: false,
        closeAction: 'close',
        closable: true,
        maximizable: true,
        maximized: shouldMaximize,
        modal: true,
        buttonAlign: 'center',
        bodyStyle: 'padding:0',
        listeners: {
            beforedestroy: function() {
                const parentTabs = top.mainTabPanel;
                if (parentTabs && parentTabs.items.getCount() > 1) {
                    parentTabs.remove(parentTabs.getActiveTab());
                }
                displayNotification('System Alert', 'Window closed successfully.');
            }
        },
        html: `<iframe id="frm${windowId}" name="${windowId}" src="${urlWithRand}" style="border:0;width:100%;height:100%"></iframe>`
    });

    modalWindow.show();
}

Viewport and BorderLayout Structure

The application uses Ext.Viewport with BorderLayout to achieve full-browser responsiveness. The layout consists of five regions:

  • North: Header bar
  • West: Collapsible navigation tree (AccordionLayout)
  • Center: Main TabPanel hosting dynamic content
  • East: Optional sidebar (collapsible)
  • South: Status bar

The center region contains a TabPanel with built-in tab scrolling support:

<ext:TabPanel ID="mainTabPanel" runat="server" 
    ResizeTabs="true" MinTabWidth="75" TabWidth="135" EnableTabScroll="true">
    <Plugins>
        <ext:TabScrollerMenu />
    </Plugins>
    <Items>
        <ext:Panel Title="Dashboard" Closable="false">
            <AutoLoad Url="/Dashboard.aspx" Mode="IFrame" TriggerEvent="show" ShowMask="true" />
        </ext:Panel>
    </Items>
</ext:TabPanel>

Dashboard-Specific Enhancements

Conditional Cell Styling

Custom renderer for grid cells that applies color based on data values:

const statusTemplate = '<b style="color:{1};">{0}</b>';
const renderStatus = function(value, meta, record) {
    const color = value === 'Active' ? 'green' : value === 'Inactive' ? 'red' : 'orange';
    return String.format(statusTemplate, value, color);
};

Responsive Grid Sizing

Function to resize multiple grids proportionally when viewport dimensions change:

function adjustGridHeights() {
    const viewportHeight = Ext.getCmp('mainViewport').getHeight();
    const contentHeight = viewportHeight - 150;

    ['grid1', 'grid2', 'grid3', 'grid4', 'grid5'].forEach(id => {
        const grid = Ext.getCmp(id);
        if (grid) grid.setHeight(contentHeight);
    });
}

Context-Aware Input Prompt

Reusable modal input dialog enforcing validation rules based on operation type:

function promptForComment(triggerEl, selectedRecords, actionType, iconClass, message, progressLabel, 
                          taskIdField, objectIdField, driverIdField, taskTitleField) {
    
    Ext.MessageBox.show({
        title: 'Add Comment',
        msg: 'Please enter your comment:',
        width: 300,
        buttons: Ext.MessageBox.OKCANCEL,
        multiline: true,
        animEl: triggerEl,
        fn: function(btn, text) {
            const cleanedText = (text || '').trim();

            if (actionType === 'Reject' || actionType === 'Repeal') {
                const prefix = actionType === 'Reject' ? '【Rejected】' : '【Revoked】';
                
                if (btn === 'ok' && !cleanedText) {
                    promptForComment(triggerEl, selectedRecords, actionType, iconClass, message, 
                                     progressLabel, taskIdField, objectIdField, driverIdField, taskTitleField);
                    Ext.Msg.alert('Validation Required', `${prefix} comment is mandatory.`);
                    return;
                }

                if (btn === 'cancel') {
                    Ext.Msg.alert('Operation Cancelled', `(${prefix}) comment required.`);
                    return;
                }

                if (cleanedText && btn === 'ok') {
                    executeAction(selectedRecords, actionType, iconClass, message, progressLabel, 
                                  taskIdField, objectIdField, driverIdField, taskTitleField, cleanedText);
                }
            } else {
                if (btn === 'ok') {
                    executeAction(selectedRecords, actionType, iconClass, message, progressLabel, 
                                  taskIdField, objectIdField, driverIdField, taskTitleField, cleanedText);
                } else {
                    Ext.Msg.alert('Operation Cancelled', 'User cancelled.');
                }
            }
        }
    });
}

Dynamic Toolbar Construction

Server-side method to inject permission-aware toolbar buttons into panels:

private void AddActionButton(Icon icon, string label, string operationCode, Toolbar toolbar, string panelId) {
    if (toolbar == null) throw new ArgumentNullException(nameof(toolbar));

    var button = new Button {
        Icon = icon,
        Text = label,
        Listeners = {
            Click = {
                Handler = $"executeOperation({{#{panelId}}}.getSelectionModel().getSelections(), '{operationCode}');"
            }
        }
    };

    if (toolbar.Items.Count > 0) {
        toolbar.Items.Add(new ToolbarSeparator());
    }
    toolbar.Items.Add(button);
}

Embedded Form Communication

Within iframe-loaded forms, use these scripts to interact with the parent dashboard:

  • Close the modal window:
    top.Ext.getCmp('frmStatesRequestList').destroy();
  • Trigger parent notifications:
    top.displayNotification('Success', 'Data saved successfully!');

Tags: ext.net extjs ASP.NET tabpanel modal-dialog

Posted on Sat, 16 May 2026 09:00:07 +0000 by Bhoomika