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
TabPanelhosting 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!');