Geeks With Blogs
Shatthi
| Home |

Its all about creating a dynamic menu control in WPF. The source for this menu control is xml file.

This sample is also an good example of adding controls dynamically and assigning animations to them dynamically.

sample xml file is given below.

<MenuList>
    <Menu id="2" image="images/Firefox-48x48.png" name="browser">
        <SubMenu image="images/Firefox-48x48.png" file="firefox.exe">Firefox</SubMenu>
        <SubMenu image="images/Internet-Explorer-48x48.png" file="IExplore.exe">IE</SubMenu>
    </Menu>
    <Menu id="3" image="images/Folder1.png" name="Folder">
               <SubMenu image="images/Folder1.png" file="explorer.exe" args="C:\Documents and Settings">Documents</SubMenu>      
    </Menu>
    <Menu id="4" image="images/visualStudioIcon.png" name="VisualStudio" file="devenv.exe"></Menu>
</MenuList>

In this Menu tag is the main category, submenu tag under menu are sub category. As shown in pic above, "Browser" is Main category. Under this Browser category Firefox and IE are sub categories.

Code:

I have one usercontrol named Category. This has one stack panel for holding main category menu and canvas for holding sub category menu. Actual controls holding the menu are created dynamically. Code is given below.

Category.xaml

<UserControl x:Class="WPFCustomDockPanel.Category"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="380" Height="260">
    <Grid Width="350">
        <Grid.RowDefinitions >
            <RowDefinition Height="85"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>         
        </Grid.RowDefinitions>
        <Border BorderThickness="1" BorderBrush="White" CornerRadius="0,0,15,15" Grid.Row="0">
            <StackPanel x:Name="StackCategory" Orientation="Horizontal" Background="Transparent">
            </StackPanel>
        </Border>       
        <Canvas  Grid.Row="1">
            <Canvas x:Name="CanvaseRoot" Visibility="Visible" MouseLeave="CanvasSubCategory_MouseLeave"></Canvas>
            </Canvas>
    </Grid>
</UserControl>


Category.xaml.cs

string menuListPath = "MenuList.xml";
        public Category()
        {
            InitializeComponent();
            BindMenus();//method called to bind all main menu.
        }

        private void BindMenus()
        {
            var doc = XDocument.Load(menuListPath);

            var query = from menulist in doc.Descendants("Menu")
                        select menulist;
            int menuIndex = 0;
            foreach (var item in query) //loops for each menu node from the resultant query
            {
                Border brdrMenu = new Border();//child of StackCategory                
                StackPanel stackMenu = new StackPanel();//child of brdrMenu                
                Image imgMainMenu = new Image();//child of stackMenu                
                TextBlock textMenu = new TextBlock();//child of stackMenu                
                stackMenu.Orientation = Orientation.Vertical;
                stackMenu.Margin = new Thickness(0, 0, 0, 3);

                BindIndvidualMenuToGroup(menuIndex.ToString() + item.Attribute("name").Value, item.Attribute("image").Value, brdrMenu, imgMainMenu, textMenu, true);

                brdrMenu.MouseLeftButtonDown += new MouseButtonEventHandler(brdrMenu_MouseLeftButtonDown);
                brdrMenu.MouseEnter += new MouseEventHandler(brdrMenu_MouseEnter);
                //Child controls added to their respective parent control.
                brdrMenu.Child = stackMenu;
                stackMenu.Children.Add(imgMainMenu);
                stackMenu.Children.Add(textMenu);
                StackCategory.Children.Add(brdrMenu);
                menuIndex += 1;
            }
        }

        void brdrMenu_MouseEnter(object sender, MouseEventArgs e)
        {
            //(sender as Border).SwingAnimation();
        }

        void brdrMenu_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)//main menu click event
        {
            ClearMainMenuSelection(); //called to clear the selection of main menu, if anything selected already.
            double subMenuWidth = 0.0;
            var menuName = (sender as Border).Name;
            int menuIndex = Convert.ToInt32(menuName.Substring(4, 1));//zero based index
            int menuCenterPoint = ((menuIndex + 1) * 60) / 2;
            menuName = menuName.Remove(0, 5);
            CanvaseRoot.Children.Clear();
           
            var doc = XDocument.Load(menuListPath);
            var menu = from menulist in doc.Descendants("Menu") //Querying for the submenu, based on the main menu item clicked.
                       where menulist.Attribute("name").Value == menuName
                       select menulist.Elements("SubMenu");
            if (menu.ElementAt(0).Count() > 0)
            {
                int subMenuCanvasCenterPoint = (menu.ElementAt(0).Count() * 50) / 2;
                int subMenuCanvasPosition = menuCenterPoint - subMenuCanvasCenterPoint;
                Canvas.SetLeft(CanvaseRoot, subMenuCanvasPosition);
                foreach (var subMenu in menu.ElementAt(0))
                {
                    string imagePath = subMenu.Attribute("image").Value;
                    string subMenuName = subMenu.Value;

                    Border brdrSubMenu = new Border();//child of CanvaseRoot        
                    brdrSubMenu.Width = 60;
                    brdrSubMenu.Height = 70;
                    Canvas canvasSubCategory = new Canvas();//child of brdrSubMenu
                    canvasSubCategory.Width = ((menu.ElementAt(0).Count() * 60) - 10);
                    Image imgSubMenu = new Image();//child of canvasSubCategory
                    imgSubMenu.Width = 40;
                    imgSubMenu.Height = 50;
                    TextBlock txtSubMenuName = new TextBlock();//child of canvasSubCategory
                    txtSubMenuName.Width = 50;
                    txtSubMenuName.Height = 20;

                    BindIndvidualMenuToGroup(subMenuName, imagePath, brdrSubMenu, imgSubMenu, txtSubMenuName, false);//method called to bind submenu
                    brdrSubMenu.MouseLeftButtonDown += new MouseButtonEventHandler(brdrSubMenu_MouseLeftButtonDown);
                    brdrSubMenu.MouseEnter += new MouseEventHandler(brdrSubMenu_MouseEnter);
                    brdrSubMenu.MouseLeave += new MouseEventHandler(brdrSubMenu_MouseLeave);
                    txtSubMenuName.Foreground = Brushes.WhiteSmoke;
                    //positioning of controls
                    Canvas.SetLeft(imgSubMenu, subMenuWidth);
                    Canvas.SetTop(imgSubMenu, 2);
                    Canvas.SetLeft(txtSubMenuName, subMenuWidth);
                    Canvas.SetTop(txtSubMenuName, 50);
                    Canvas.SetLeft(brdrSubMenu, menuIndex * 80.0);

                    canvasSubCategory.Children.Add(imgSubMenu);
                    canvasSubCategory.Children.Add(txtSubMenuName);
                    brdrSubMenu.Child = canvasSubCategory;
                    CanvaseRoot.Children.Add(brdrSubMenu);
                    CanvaseRoot.Width = (menu.ElementAt(0).Count() * 60);

                    subMenuWidth += 50;//incremental for positioning the next control
                }
            }
            else
            {
                var rootMenu = from menulist in doc.Descendants("Menu") //Querying for the menu, based on the main menu item clicked.
                               where menulist.Attribute("name").Value == menuName
                               select menulist;
                StartProcess(rootMenu, "file");
            }

            ((Border)sender).Background = Brushes.WhiteSmoke;
            CanvaseRoot.Visibility = Visibility.Visible;
            CanvaseRoot.Background = Brushes.Red;
        }

        void brdrSubMenu_MouseLeave(object sender, MouseEventArgs e)
        {
            (sender as Border).Background = Brushes.Red;
        }

        void brdrSubMenu_MouseEnter(object sender, MouseEventArgs e)
        {
            (sender as Border).Background = Brushes.WhiteSmoke;
        }

        void brdrSubMenu_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //process start needs to implemented here  
            string subMenuName = (((sender as Border).Child as Canvas).Children[1] as TextBlock).Text;
            var doc = XDocument.Load(menuListPath);
            var menu = from subMmenulist in doc.Descendants("SubMenu") //Querying for the submenu, based on the main menu item clicked.
                       where subMmenulist.Value == subMenuName
                       select subMmenulist;
            StartProcess(menu, "file");
        }

        private static void StartProcess(IEnumerable<XElement> menu, string attributeName)
        {
            ProcessStartInfo processInfo = new ProcessStartInfo();

            if (menu.Count() > 0)
            {
                processInfo.FileName = (menu.First()).Attribute(attributeName).Value;
                if ((menu.First()).Attribute("args") != null)
                {
                    processInfo.Arguments = (menu.First()).Attribute("args").Value;
                }
                Process.Start(processInfo);
            }

        }

        /// <summary>
        /// method to bind the menu
        /// </summary>
        /// <param name="menuName">menu name</param>
        /// <param name="menuImage">image path for the menu item</param>
        /// <param name="brdrMenu">border control to hold menu's image and name</param>
        /// <param name="imgMainMenu">image control to render menu's image</param>
        /// <param name="textMenu">textblock control to render menu's name</param>
        /// <param name="isMainMenu">true for main menu, false for submenu item</param>
        private void BindIndvidualMenuToGroup(string menuName, string menuImage, Border brdrMenu, Image imgMainMenu, TextBlock textMenu, bool isMainMenu)
        {
            brdrMenu.Name = string.Concat("brdr", menuName);
            brdrMenu.BorderThickness = new Thickness(0.5);
            brdrMenu.BorderBrush = new SolidColorBrush(Colors.Transparent);
            if (isMainMenu)
            {
                brdrMenu.CornerRadius = new CornerRadius(0, 0, 15, 15);
                brdrMenu.Margin = new Thickness(5, 0, 5, 5);
                imgMainMenu.Width = 60.0;
                imgMainMenu.Height = 60.0;
                textMenu.Text = menuName.Remove(0, 1);
                textMenu.TextAlignment = TextAlignment.Center;
                textMenu.Foreground = Brushes.Black;
            }
            else
            {
                brdrMenu.CornerRadius = new CornerRadius(15, 15, 15, 15);
                brdrMenu.Margin = new Thickness(5, 0, 5, 5);
                textMenu.Text = menuName;
            }

            textMenu.TextAlignment = TextAlignment.Center;
            textMenu.Foreground = Brushes.Black;
            textMenu.FadeIn();

            imgMainMenu.Name = menuName.Remove(0, 1);
            Uri imageUri = new Uri(menuImage, UriKind.Relative);
            ImageSource imageSource = new System.Windows.Media.Imaging.BitmapImage(imageUri);
            imgMainMenu.SetValue(Image.SourceProperty, imageSource);
        }

        private void CanvasSubCategory_MouseLeave(object sender, MouseEventArgs e)
        {
            CanvaseRoot.Visibility = Visibility.Collapsed;
            ClearMainMenuSelection();
        }

        private void ClearMainMenuSelection()
        {
            foreach (var item in StackCategory.Children)
            {
                (item as Border).Background = Brushes.Transparent;
            }
        }
   
    //  I am doing these as extension methods because, they
    //   will be available to all UIElement objects, which are
    //   basically all controls that can be added to the GUI
    public static class ControlAnimationExtensionMethods
    {
        public static void FadeIn(this UIElement targetControl)
        {
            DoubleAnimation fadeInAnimation = new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(1.5)));
            Storyboard.SetTarget(fadeInAnimation, targetControl);
            Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty));
            Storyboard sb = new Storyboard();
            sb.Children.Add(fadeInAnimation);
            sb.Begin();
        }

        public static void FadeOut(this UIElement targetControl)
        {
            DoubleAnimation fadeInAnimation = new DoubleAnimation(1, 0, new Duration(TimeSpan.FromSeconds(1.5)));
            Storyboard.SetTarget(fadeInAnimation, targetControl);
            Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty));
            Storyboard sb = new Storyboard();
            sb.Children.Add(fadeInAnimation);
            sb.Begin();
        }      
}

Posted on Monday, October 4, 2010 3:58 AM | Back to top


Comments on this post: Dynamic Menu Control in WPF

# re: Dynamic Menu Control in WPF
Requesting Gravatar...
This is one of the best articles so far I have read online. Just useful information. Very well presented. This provides me basic idea of Menu control in wpf as well as clear all doubts related this section. There are some other good articles too helped me lot which I founded during searching. Check that post link too it might be useful for you too...
http://mindstick.com/Articles/91ec7c15-c848-423c-9dfc-0609015210e1/?Menu%20control%20in%20WPF
and
http://www.c-sharpcorner.com/uploadfile/mahesh/menus-in-wpf/
Left by Graviel Sansu on Mar 02, 2012 2:49 AM

# re: Dynamic Menu Control in WPF
Requesting Gravatar...
excelente el ejemplo
Left by alex huaraca apaza on Jun 24, 2013 8:57 AM

Your comment:
 (will show your gravatar)


Copyright © shatthi | Powered by: GeeksWithBlogs.net