基于 XML Feed 在运行时绘制 Windows 控件
如何基于 XML Feed 在运行时绘制 Windows 控件。
引言
我们通常使用像 Visual Studio 这样的 IDE,通过拖放组件到表单上来设计 Windows 原生应用程序。这没什么新鲜的——每个 Windows 开发者都熟悉这种方式。它能够实现快速应用程序开发,并使我们有更多时间专注于高效地实现业务规则,从而驱动整个系统。到目前为止一切都很好。但在其他情况下,我们可能需要在运行时添加这些小部件。其中一个原因可能是我们希望系统尽可能灵活。在运行时绘制这些组件既有趣又具有挑战性。本文重点介绍设计一个简单的 GUI,其中表单组件是在运行时动态添加的,而不是在设计时添加的。
XML 数据源
对于这个应用程序,我们将使用 XML 文档来描述其内容和内容类型。这允许我们从单个表单启动不同的应用程序。例如,您可以创建将来可能需要的安装应用程序。XPath 用于遍历它。XML 文档如下所示
<?xml version="1.0"?>
<MenuItem BasePath="c:\SampleApplication">
<SideMenuItem Title="Documents">
<Item ID="Document"
LinkText="PDF Document"
DetailText="View my pdf document"
LinkType="pdf"
Icon="\Icons\pdf.ico"
ResourceLocation="\Resources\sampleDoc.pdf"
/>
</SideMenuItem>
<SideMenuItem Title="Softwares">
<Item ID="CoolApp"
LinkText="Next Cool Application"
DetailText="Launch Cool Application"
LinkType="exe"
Icon="\Icons\exe.ico"
ResourceLocation="="\Resources\CoolApp.exe"
/>
<Item ID="KillerApp"
LinkText="Next Killer application"
DetailText="Launch Killer Application"
LinkType="exe"
Icon="\Icons\exe.ico"
ResourceLocation="\Resources\KillerApp.exe "
/>
</SideMenuItem>
<SideMenuItem Title="Support">
<Item ID="SupportSite"
LinkText="Support Site"
DetailText="Visit Support Site"
LinkType="url"
Icon="\Icons\ie.ico"
ResourceLocation="\Resources\support.htm"
/>
</SideMenuItem>
</MenuItem>
上面的 XML 文档描述了两个类别——Document
和 Software
。在每个类别中,它都有两个项目。Category
或 “MainLink
” 标签创建侧边菜单项,而 “Item
” 标签描述了 Windows 表单的内容和内容链接。XPath 遍历每个节点并检索必要的值。
解析 XML 文档
XmlDocument
类用于加载 XML 数据源。
XmlDocument xDoc = new XmlDocument();
加载到 xDoc
对象后,以下 XPath
表达式检索所有 “MainLink
” 项。
获取所有 “<MainLink>
”
XmlNodeList nodeSideMenuItems = doc.SelectNodes("MenuItem/SideMenuItem");
然后它继续遍历其中的项目。
XmlNodeList nodes = sNode.SelectNodes("Item");
SideMenuItem
是代表 XML 文档的类。在 XML 解析期间,所有相关节点值都存储在 List<SideMenuItem>
中。类定义如下所示
public class SideMenuItem
{
public string TagName { get; set; }
public List<ContentMenuItem> MenuItemList { get; set; }
}
public class ContentMenuItem
{
public string TagName { get; set; }
public string LinkText { get; set; }
public string DetailText { get; set; }
public string LinkType { get; set; }
public string IconLocation { get; set; }
public string ResourceLocation { get; set; }
}
运行时控件
我们已经完成了准备工作,通过解析 Feed.xml 文档并填充了 List<SideMenuItem>
列表。有趣的部分现在开始了。我们将把控件添加到侧边和内容面板,并注册所需的事件。负责的主要方法简要说明如下。
GenerateSidePanelControls()
代码片段——将类型为 “Panel
” 类的 “Label
” 控件添加到侧边面板,并注册 Click、MouseEnter 和 MouseLeave 事件。int topPosition = 15; foreach (SideMenuItem sItem in _sideMenuItemList) { Label objLabel = new Label(); objLabel.Name = sItem.TagName; objLabel.Text = sItem.TagName; objLabel.Left = 15; objLabel.Top = topPosition; objLabel.Font = _normalFont; sidePanel.Controls.Add(objLabel); topPosition += 25; objLabel.Click += new System.EventHandler(SideLabel_Click); objLabel.MouseEnter += new System.EventHandler(SideLabel_MouseEnter); objLabel.MouseLeave += new System.EventHandler(SideLabel_MouseLeave); }
SideLabel_Click()
代码片段——当用户点击侧边菜单项时,添加主要内容控件。Label objLabel = (Label)sender; objLabel.Font = _boldFont; //Make rest of the Side Label have normal font foreach (Control ctrl in sidePanel.Controls) { if (ctrl is Label) { if (ctrl.Name != objLabel.Name) { ctrl.Font = _normalFont; } } } GenerateContentPanelControls(objLabel.Name);
GenerateContentPanelControls(string sTagName)
代码片段——根据侧边菜单项标题或标签名,当用户点击侧边菜单项时,添加主要内容控件。//Get the side menu item based on tagName SideMenuItem sMenuItem = null; foreach (SideMenuItem sItem in _sideMenuItemList) { if (sItem.TagName == sTagName) { sMenuItem = sItem; break; } } contentPanel.Controls.Clear(); Label spacer = new Label(); spacer.Height = 10; spacer.Width = 562; contentPanel.Controls.Add(spacer); foreach (ContentMenuItem cItem in sMenuItem.MenuItemList) { FlowLayoutPanel flowLayoutPanel = new FlowLayoutPanel(); flowLayoutPanel.Width = 550; flowLayoutPanel.AutoSize = true; // <IconLocation> tag control string iconLocation = cItem.IconLocation; PictureBox icon = new PictureBox(); icon.Size = new Size(50, 50); icon.Image = new Bitmap(iconLocation); icon.SizeMode = PictureBoxSizeMode.CenterImage; flowLayoutPanel.Controls.Add(icon); // innerFlowPanel FlowLayoutPanel innerFlowPanel = new FlowLayoutPanel(); innerFlowPanel.Width = 500; // <LinkText> tag control string linkText = cItem.LinkText; Label lblLinkText = new Label(); lblLinkText.Name = cItem.TagName; lblLinkText.Text = linkText; lblLinkText.Font = _normalFont; lblLinkText.ForeColor = Color.Blue; lblLinkText.AutoSize = true; innerFlowPanel.Controls.Add(lblLinkText); // linebreak Label lineBreak = new Label(); lineBreak.Height = 0; lineBreak.Width = 562 - (lblLinkText.Width + icon.Width); innerFlowPanel.Controls.Add(lineBreak); // <DetailText> string detailText = cItem.DetailText; Label lblDetailText = new Label(); lblDetailText.Text = detailText; lblDetailText.Font = _normalFont; lblDetailText.AutoSize = true; innerFlowPanel.Controls.Add(lblDetailText); innerFlowPanel.Height = lblLinkText.DisplayRectangle.Height + lblDetailText.DisplayRectangle.Height + 5; flowLayoutPanel.Controls.Add(innerFlowPanel); contentPanel.Controls.Add(flowLayoutPanel); //Register events lblLinkText.Click += new System.EventHandler(ContentLabel_Click); lblLinkText.MouseEnter += new System.EventHandler(ContentLabel_MouseEnter); lblLinkText.MouseLeave += new System.EventHandler(ContentLabel_MouseLeave); }
UI 界面
根据 feed.xml,以下组件将在运行时绘制到表单上

结论
在运行时添加控件提供了不同的好处。当内容更改时,我们不必一遍又一遍地设计表单。我们只需注入不同的 XML 数据源即可。
让我们分享想法。
历史
- 2011 年 7 月 31 日:初始版本