65.9K
CodeProject 正在变化。 阅读更多。
Home

基于 XML Feed 在运行时绘制 Windows 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.43/5 (3投票s)

2011 年 8 月 1 日

CPOL

2分钟阅读

viewsIcon

22364

downloadIcon

716

如何基于 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> 列表。有趣的部分现在开始了。我们将把控件添加到侧边和内容面板,并注册所需的事件。负责的主要方法简要说明如下。

  1. 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);
             }
  2. 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);
  3. 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,以下组件将在运行时绘制到表单上

image001.gif

结论

在运行时添加控件提供了不同的好处。当内容更改时,我们不必一遍又一遍地设计表单。我们只需注入不同的 XML 数据源即可。

让我们分享想法。

历史

  • 2011 年 7 月 31 日:初始版本
© . All rights reserved.