近日做了一个在Silverlight 2 beta 2中实现菜单功能的小程序,支持菜单折叠。如图:
菜单的配置信息可以从SharePoint中读取,SharePoint中创建一个List, 各字段如下:
Column Name |
Column Type |
Title |
Text Field |
Hyperlink |
Hyperlink |
TopLevelLink |
Binary (Y/N) |
Parent Link |
Text Field (only available if Top Level Link = N) |
在Silverlight程序中有一个MenuConfig.xml文件,用来配置SharePoint地址等相关信息:
- <?xml version="1.0" encoding="utf-8" ?>
- <MenuConfig>
- <ListsServiceUrl>http://localhost/Docs/_vti_bin/Lists.asmx</ListsServiceUrl>
- <MenuListName>MenuList</MenuListName>
- <MenuItemHorizontalSpace>20.0</MenuItemHorizontalSpace>
- <MenuItemVerticalSpace>25.0</MenuItemVerticalSpace>
- <MenuLinkTargetName>_blank</MenuLinkTargetName>
- </MenuConfig>
在Page.xaml.cs页中,首先下载并取得MenuConfig.xml信息(指USING_TEST_DATA = false 时;若为true,则表示不从SharePoint读取配置信息,而直接构造测试数据)
- private const bool USING_TEST_DATA = false; // True to use dummy test data, and False to use data from sharepoint server.
- private Configuration config = new Configuration();
- private Menu menu;
- public Page()
- {
- InitializeComponent();
- this.Loaded += new RoutedEventHandler(Page_Loaded);
- config.GetConfigurationCompleted += new RoutedEventHandler(config_GetConfigurationCompleted);
- }
- #region Events
- void Page_Loaded(object sender, RoutedEventArgs e)
- {
- if (!USING_TEST_DATA) // using datas from SharePoint
- config.LoadMenuConfig();
- else
- CreateMenuUsingTestData(); // using test data to create menu
- }
- void config_GetConfigurationCompleted(object sender, RoutedEventArgs e)
- {
- txtWait.Text = "Wait for pull informations from sharepoint...";
- GetMenuDatas();
- }
接着调用SharePoint的WebService来获取List里的菜单数据(关于调用WebService可能遇到的问题可以查看《Silverlight 2beta2 调用 SharePoint的WebService......》)
- private void GetMenuDatas()
- {
- BasicHttpBinding bind = new BasicHttpBinding();
- EndpointAddress endpoint = new EndpointAddress(config.ListsServiceUrl);
- SPListService.ListsSoapClient lsc = new MenuControlForSharePoint.SPListService.ListsSoapClient(bind, endpoint);
- lsc.GetListItemsCompleted += new EventHandler<MenuControlForSharePoint.SPListService.GetListItemsCompletedEventArgs>(lsc_GetListItemsCompleted);
- XElement viewFileds = new XElement("ViewFields",
- new XElement("FieldRef", new XAttribute("Name", "Title")),
- new XElement("FieldRef", new XAttribute("Name", "Hyperlink")),
- new XElement("FieldRef", new XAttribute("Name", "TopLevelLink")),
- new XElement("FieldRef", new XAttribute("Name", "ParentLink"))
- );
- lsc.GetListItemsAsync(config.MenuListName, null, null, viewFileds, null, null, null);
- }
- void lsc_GetListItemsCompleted(object sender, MenuControlForSharePoint.SPListService.GetListItemsCompletedEventArgs e)
- {
- XDocument doc = XDocument.Parse(e.Result.ToString());
- var lstMenuInfo = from menuInfo in doc.Descendants("{#RowsetSchema}row").ToList()
- select new MenuInfo
- {
- Title = (string)menuInfo.Attribute("ows_Title"),
- Hyperlink = (string)menuInfo.Attribute("ows_Hyperlink"),
- TopLevelLink = (int)menuInfo.Attribute("ows_TopLevelLink") == 1,
- ParentLink = (string)menuInfo.Attribute("ows_ParentLink"),
- };
- // render the menu items
- RenderMenu(lstMenuInfo);
- }
菜单数据获取完毕后就开始绘制菜单
- private void RenderMenu(IEnumerable<MenuInfo> lstMenuInfo)
- {
- // Init menu
- InitMenu(lstMenuInfo);
- //create menu
- menu.Render();
- //exapand all menu items
- menu.ExpandAll();
- }
- private void InitMenu(IEnumerable<MenuInfo> lstMenuInfo)
- {
- menu = new Menu(canMenu);
- menu.MenuItemHorizontalSpace = config.MenuItemHorizontalSpace;
- menu.MenuItemVerticalSpace = config.MenuItemVerticalSpace;
- menu.TargetName = config.MenuLinkTargetName;
- foreach (MenuInfo menuInfo in lstMenuInfo)
- {
- if (menuInfo.TopLevelLink)
- {
- MenuItem mi = GetMenuItem(menuInfo);
- mi.IndexInCurrentLevel = menu.TopMenuItems.Count;
- mi.Level = 0;
- menu.TopMenuItems.Add(mi);
- AddSumMenu(mi, lstMenuInfo);
- }
- }
- }
- private void AddSumMenu(MenuItem parentMenu, IEnumerable<MenuInfo> lstMenuInfo)
- {
- foreach (MenuInfo menuInfo in lstMenuInfo)
- {
- if (!menuInfo.TopLevelLink && menuInfo.ParentLink.Equals(parentMenu.Text))
- {
- MenuItem mi = GetMenuItem(menuInfo);
- mi.IndexInCurrentLevel = parentMenu.Children.Count;
- mi.Level = parentMenu.Level + 1;
- mi.ParentMenuItem = parentMenu;
- parentMenu.Children.Add(mi);
- AddSumMenu(mi, lstMenuInfo);
- }
- }
- }
- private MenuItem GetMenuItem(MenuInfo menuInfo)
- {
- string url = "", toolTip = "";
- Uri linkUri = null;
- string text = menuInfo.Title;
- string hyperLink = menuInfo.Hyperlink;
- if (!String.IsNullOrEmpty(hyperLink))
- {
- string[] arrLink = hyperLink.Split(new Char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
- if (arrLink.Length > 0)
- {
- url = arrLink[0];
- if (!String.IsNullOrEmpty(url))
- linkUri = new Uri(url);
- if (arrLink.Length > 1)
- toolTip = arrLink[1];
- }
- }
- return new MenuItem(text, linkUri, toolTip);
- }
其中private void InitMenu(IEnumerable<MenuInfo> lstMenuInfo) 方法将获取的菜单数据组织成一个有Menu.TopMenuItems负责的树型结构,然后交给Menu类来绘制菜单,并在绘制菜单完毕后将所有的子菜单都展开。
关于Menu类,主要代码如下:
- public void Render()
- {
- MenuHolder.Children.Clear();
- foreach (MenuItem mi in TopMenuItems)
- {
- AddMenuLink(MenuHolder, mi);
- AddSubMenu(mi);
- }
- }
- public void ExpandAll()
- {
- foreach (MenuItem mi in TopMenuItems)
- ExpandMenuAndChildren(mi);
- }
- #endregion
- #region Private Methods
- private void AddSubMenu(MenuItem parentMenu)
- {
- foreach (MenuItem mi in parentMenu.Children)
- {
- AddMenuLink(parentMenu.ChildrenHolder, mi);
- AddSubMenu(mi);
- }
- }
- private void AddMenuLink(Panel parentMenuHolder, MenuItem mi)
- {
- MenuLink ml = new MenuLink();
- ml.Text = mi.Text;
- ml.Url = mi.Url;
- ml.ToolTip = mi.ToolTip;
- ml.TargetName = _targetName;
- ml.SetValue(Canvas.TopProperty, mi.IndexInCurrentLevel * _menuItemVerticalSpace);
- ml.MouseLeftButtonUp += new MouseButtonEventHandler(MouseLink_MouseLeftButtonUp);
- Canvas childrenHolder = new Canvas();
- childrenHolder.Visibility = Visibility.Collapsed;
- mi.IsExpanded = false;
- childrenHolder.SetValue(Canvas.LeftProperty, _menuItemHorizontalSpace);
- childrenHolder.SetValue(Canvas.TopProperty, (mi.IndexInCurrentLevel + 1) * _menuItemVerticalSpace);
- mi.MenuLinkControl = ml;
- mi.ChildrenHolder = childrenHolder;
- ml.MenuItem = mi;
- parentMenuHolder.Children.Add(ml);
- parentMenuHolder.Children.Add(childrenHolder);
- }
- private void AdjustBrotherMenuTopProperties(MenuItem mi, double dreich)
- {
- List<MenuItem> brothers;
- if (mi.ParentMenuItem == null)
- brothers = TopMenuItems;
- else
- brothers = mi.ParentMenuItem.Children;
- for (int i = mi.IndexInCurrentLevel + 1; i < brothers.Count; i++)
- {
- MenuLink brogherItem = brothers[i].MenuLinkControl;
- double brotherItemOldTop = Convert.ToDouble(brogherItem.GetValue(Canvas.TopProperty));
- brogherItem.SetValue(Canvas.TopProperty, brotherItemOldTop + dreich);
- Panel brotherHolder = brothers[i].ChildrenHolder;
- double brotherHolderOldTop = Convert.ToDouble(brotherHolder.GetValue(Canvas.TopProperty));
- brotherHolder.SetValue(Canvas.TopProperty, brotherHolderOldTop + dreich);
- }
- if (mi.ParentMenuItem != null)
- AdjustBrotherMenuTopProperties(mi.ParentMenuItem, dreich);
- }
- private double GetChildrenHolderHeight(MenuItem mi)
- {
- double height = 0.0;
- if (mi.IsExpanded)
- {
- foreach (MenuItem child in mi.Children)
- {
- height += _menuItemVerticalSpace;
- height += GetChildrenHolderHeight(child);
- }
- }
- return height;
- }
- private void ExpandMenu(MenuItem mi)
- {
- if (mi.Children.Count > 0 && !mi.IsExpanded)
- {
- mi.IsExpanded = true;
- mi.ChildrenHolder.Visibility = Visibility.Visible;
- double dreich = GetChildrenHolderHeight(mi);
- AdjustBrotherMenuTopProperties(mi, dreich);
- }
- }
- private void ExpandMenuAndChildren(MenuItem menuItem)
- {
- ExpandMenu(menuItem);
- foreach (MenuItem mi in menuItem.Children)
- ExpandMenuAndChildren(mi);
- }
- private void CollapseMenu(MenuItem mi)
- {
- if (mi.Children.Count > 0 && mi.IsExpanded)
- {
- double dreich = GetChildrenHolderHeight(mi) * (-1.0);
- mi.IsExpanded = false;
- mi.ChildrenHolder.Visibility = Visibility.Collapsed;
- AdjustBrotherMenuTopProperties(mi, dreich);
- }
- }
- #endregion
- #region Events
- void MouseLink_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
- {
- MenuLink ml = sender as MenuLink;
- MenuItem mi = ml.MenuItem;
- if (mi.IsExpanded)
- CollapseMenu(mi);
- else
- ExpandMenu(mi);
- // redirect to the url
- if (mi.Url != null)
- HtmlPage.Window.Navigate(mi.Url, ml.TargetName);
- }
AdjustBrotherMenuTopProperties方法用来调整因某一项的子菜单高度改变而影响的本级兄弟菜单的Top位置。
该程序的源代码可以在 这里下载。