RadMenu and HierarchicalDataTemplate

22 posts, 1 answers
  1. Nic
    Nic avatar
    32 posts
    Member since:
    Oct 2008

    Posted 16 Jan 2009 Link to this post

    Hi,

    Is it possible to setup some properties in the objects contained in HierarchicalDataTemplate so that when assigned to the RadMenu, the individual menu items are enabled/disabled and their click events are setup automatically?

    Furthermore, can these properties by dynamically changed at runtime, and the menu change accordingly. I'm thinking enabling/disabling a given menu item based on some user context.

    On the same note, even add (or remove) objects from the HierarchicalData at runtime and reflect this in the menu as well. Perhaps a list of recently opened files...

    We're developing an enterprise app with modular components. Ideally, each component would add it's own menu items to the menu tree at the right insertion point, and take care of enabling/disabling its own menu items, as well as click events. Would something like this be possible via the HierarchicalDataTemplate? Would appreciate ideas on how to go about doing this.


    Thanks,
    Nic
  2. Hristo
    Admin
    Hristo avatar
    832 posts

    Posted 19 Jan 2009 Link to this post

    Hello Nic,

    Sorry for the late response.
    Yes you can create collection from some business objects and bind it to RadMenu.
    If this collection implements INotifyCollectionChanged then RadMenu will update its items when you change the collection.
    Also if the items in this collection implement INotifyPropertyChanged then the corresponding RadMenuItems will update its content.

    You can see how to create and you RadMenu and HierarchicalDataTemplate from this example:
    http://demos.telerik.com/silverlight/#Menu/DataSource

    For my regret Silverlight 2 do not support Bindings in Style setters. So you wont be able to bind RadMenuItem IsEnabled property to some property of your business object. But we are evaluating a way to overcome this limitation. So stay tuned.

    One way to workaround this limitation now is to inherit from RadMenu and override PrepareContainerForItemOverride method. Then you can bind your business object with the corresponding RadMenuItem in code.
     
    As for the right insertion point - this is possible. All you need to do is to insert the item in the correct place in your collection (assuming that your collection implement INotifyCollectionChanged and you have set this collection as ItemsSource on RadMenu).

    RadMenuItem expose routed Click event so you can handle all menuItem clicks in one place of your code.
    You can achieve your scenario easily.

    Let me know if you need more help.


    Sincerely yours,
    Hristo
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  3. DevCraft banner
  4. Nic
    Nic avatar
    32 posts
    Member since:
    Oct 2008

    Posted 19 Jan 2009 Link to this post

    Hi Hristo,

    Thank you for your response and advice on how to go about my dynamic menu issues.

    I am not entirely sure how to make use of PrepareContainerForItemOverride though. Would you be able to provide an example project for me to build upon?

    thanks,
    Nic


  5. Hristo
    Admin
    Hristo avatar
    832 posts

    Posted 20 Jan 2009 Link to this post

    Hi Nic,

    Lets say that you have ObservableCollection<DataItem> as a source for RadMenu.ItemsSource.
    The code for DataItem is as follow:

    public class DataItem : INotifyPropertyChanged  
    {  
        public DataItem()  
        {  
            this.Items = new ObservableCollection<DataItem>();  
        }
        #region IsEnabled  
     
        private bool isEnabled;  
        /// <summary>  
        /// Gets or sets IsEnabled.  
        /// </summary>  
        public bool IsEnabled  
        {  
            get 
            {  
                return this.isEnabled;  
            }  
            set 
            {  
                if (this.isEnabled != value)  
                {  
                    this.isEnabled = value;  
                    OnPropertyChanged("IsEnabled");  
                }  
            }  
        }
        #endregion  
     
        #region Header  
     
        private string header;  
        /// <summary>  
        /// Gets or sets Header.  
        /// </summary>  
        public string Header  
        {  
            get 
            {  
                return this.header;  
            }  
            set 
            {  
                if (this.header != value)  
                {  
                    this.header = value;  
                    OnPropertyChanged("Header");  
                }  
            }  
        }
        #endregion  
     
        #region Items  
     
        private ObservableCollection<DataItem> items;  
        /// <summary>  
        /// Gets or sets Items.  
        /// </summary>  
        public ObservableCollection<DataItem> Items  
        {  
            get 
            {  
                return this.items;  
            }  
            set 
            {  
                if (this.items != value)  
                {  
                    this.items = value;  
                    OnPropertyChanged("Items");  
                }  
            }  
        }
        #endregion  
     
        #region INotifyPropertyChanged Members  
     
        /// <summary>  
        ///     Called when the value of a property changes.  
        /// </summary>  
        /// <param name="propertyName">The name of the property that has changed.</param>  
        protected virtual void OnPropertyChanged(String propertyName)  
        {  
            if (String.IsNullOrEmpty(propertyName))  
            {  
                return;  
            }  
            if (PropertyChanged != null)  
            {  
                PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));  
            }  
        }  
     
        /// <summary>  
        ///     Raised when the value of one of the properties changes.  
        /// </summary>  
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion  

    This class have three properties: IsEnabled - controls whether RadMenuItem is enabled, Header - the string for RadMenuItem Header property and Items - RadMenuItem child items.

    When you set the ObservableCollection<DataItem> as a ItemsSource to RadMenu, for every item it this collection PrepareContainerForItemOverride method will be called.
    There you have the RadMenuItem and the DataItem and you can bind them like this:

    public class RadMenuExtended : RadMenu  
    {  
        protected override void PrepareContainerForItemOverride(System.Windows.DependencyObject element, object item)  
        {  
            base.PrepareContainerForItemOverride(element, item);  
            RadMenuItem menuItem = element as RadMenuItem;  
     
            if (!object.ReferenceEquals(menuItem, item))  
            {  
                Binding binding = new Binding("IsEnabled");  
                binding.Source = item;  
                menuItem.SetBinding(RadMenuItem.IsEnabledProperty, binding);  
            }  
        }  

    The Header and Items property can be bound from the HierarchicalDataTempalte.

    This is the way for now. Please keep in mind that this way only the root MeunItems will be bound to IsEnabled property. If you want to bind all menuItems you should inherit from RadMenuItem and RadMenu and change them like follows:

    public class RadMenuExtended : RadMenu  
    {  
        protected override bool IsItemItsOwnContainerOverride(object item)  
        {  
            return (item is RadMenuItemExtended);  
        }  
     
        protected override System.Windows.DependencyObject GetContainerForItemOverride()  
        {  
            return new RadMenuItemExtended();  
        }  
     
        protected override void PrepareContainerForItemOverride(System.Windows.DependencyObject element, object item)  
        {  
            base.PrepareContainerForItemOverride(element, item);  
            RadMenuItem menuItem = element as RadMenuItem;  
     
            if (!object.ReferenceEquals(menuItem, item))  
            {  
                Binding binding = new Binding("IsEnabled");  
                binding.Source = item;  
                menuItem.SetBinding(RadMenuItem.IsEnabledProperty, binding);  
            }  
        }  
    }  
     
    public class RadMenuItemExtended : RadMenuItem  
    {  
        protected override bool IsItemItsOwnContainerOverride(object item)  
        {  
            return (item is RadMenuItemExtended);  
        }  
     
        protected override System.Windows.DependencyObject GetContainerForItemOverride()  
        {  
            return new RadMenuItemExtended();  
        }  
     
        protected override void PrepareContainerForItemOverride(System.Windows.DependencyObject element, object item)  
        {  
            base.PrepareContainerForItemOverride(element, item);  
     
            RadMenuItem menuItem = element as RadMenuItem;  
     
            if (!object.ReferenceEquals(menuItem, item))  
            {  
                Binding binding = new Binding("IsEnabled");  
                binding.Source = item;  
                menuItem.SetBinding(RadMenuItem.IsEnabledProperty, binding);  
            }  
        }  

    I hope this helps.
    Let me know if you need more help.

    Sincerely yours,
    Hristo
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  6. Nic
    Nic avatar
    32 posts
    Member since:
    Oct 2008

    Posted 22 Jan 2009 Link to this post

    Hi Hristo,

    Thank you very much for the detailed code. It got me thinking...

    I was looking at the logic for PrepareContainerForItemOverride() and wondering if it was possible to have another property in DataItem named IsVisible. When set to false, the idea would be for that menuitem's visibility to be collapsed and not visible at all in the menu tree.

    The bigger picture would be an application with plugins that could be enabled/disabled. One scenario is to add/remove a corresponding plugin's menu items to the ObservableCollection. The other, would be just to set/unset the IsVisible property for the plugin's menu items, and in PrepareContainerForItemOverride() bind this property accordingly to the UI element's visiblity property. Would this work?

    Thanks again,
    Nic

  7. Nic
    Nic avatar
    32 posts
    Member since:
    Oct 2008

    Posted 22 Jan 2009 Link to this post

    Hristo,

    I got the Visibility working using the same technique. Thanks for showing how the approach works!

    I do have another issue now and that is how to put a separator (RadSeparator?) between two menu items using the HierarchicalDataTemplate model.

    --Nic
  8. Hristo
    Admin
    Hristo avatar
    832 posts

    Posted 23 Jan 2009 Link to this post

    Hello Nic,

    I'm happy I was able to help. I hope that in Silverlight 3 you will be able to bind RadMenuItem to the data item so you won't need the code behind.
    As for the RadSeparator - right now the only way of doing it is to add RadSeparator objects in your source collection.

    We are thinking of other ways to do this - like adding IsSeparator property to RadMenuItem but eventually you will need to add data item that represent separator. So why using binding that will change the control template to separator when you can put directly the separator item?

    Let me know if this works for you.

    Sincerely yours,
    Hristo
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  9. Nic
    Nic avatar
    32 posts
    Member since:
    Oct 2008

    Posted 23 Jan 2009 Link to this post

    Hi Hristo,

    I'm not quite following your last point. If I'm using a HierarchicalDataTemplate, how do I now go about adding RadSeparator objects into DataItem's ObservableCollection<DataItem>?

    Do I change this to ObservableCollection <Object> and override some method in RadMenu and/or RadMenuItem to inspect the actual type of each item in the ObservableCollection? Can you please provide an example?

    Thanks,
    Nic



  10. Hristo
    Admin
    Hristo avatar
    832 posts

    Posted 23 Jan 2009 Link to this post

    Hello Nic,

    Just change the DataItem Items collection to ObservableCollection<object>. There is no need to override anything in RadMenu on RadMenuItem. The logic is already there.

    Please let me know if you need more help.

    All the best,
    Hristo
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  11. Nic
    Nic avatar
    32 posts
    Member since:
    Oct 2008

    Posted 23 Jan 2009 Link to this post

    Hello Hristo,

    I am not able to get this separator bit working. I have tried two approaches.

    1. Having DataItem's Item collection being an ObservableCollection<object> and inserting a RadSeparator. When the menu displays, then at the location of where the separator should be, I get this strange behaviour where it opens up as a submenu containing the same content. This occurs recusrively so at the position of the separator, it keeps having the same submenu open up several levels deep.

    2. Add a bool IsSeparator to DataItem. Then implement a DataTemplateSelector class to select the right HierarchicalDataTemplate. I have it select between these two...

    <core:HierarchicalDataTemplate x:Key="MenuItemHierarchicalDataTemplate" ItemsSource="{Binding Items}" >
                <TextBlock Text="{Binding Header}" Margin="4" HorizontalAlignment="Left" VerticalAlignment="Bottom" />
            </core:HierarchicalDataTemplate>

            <core:HierarchicalDataTemplate x:Key="SeparatorHierarchicalDataTemplate">
                <StackPanel>
                    <telerik:RadSeparator BorderBrush="Black" Background="Black" IsEnabled="False" />
                    <TextBlock Text="test text"/>
                </StackPanel>            
            </core:HierarchicalDataTemplate>

    I can see it use the SeparatorHierarchicalDataTemplate template but I guess i'm not using it correctly. Firstly, I need to add dummy text for the RadSeparator to draw out when it should just be the width of the largest menu item. It also highlights on mouseover and is clickable.

    Can you please advise further?

    thanks,
    Nic


  12. Answer
    Hristo
    Admin
    Hristo avatar
    832 posts

    Posted 27 Jan 2009 Link to this post

    Hi Nic,

    Sorry for the late response.

    I the attachments you will find a modified version of RadMenuExtended and RdaMenuItemExtended that allow you to put RadSeparator control. The change in both classes is in the IsItemItsOwnContainerOverride where I'm now checking for both RadMenuItemExtended and RadSeparator. If the item is one of these then no container is created.
    Also I have added TemplateSelector class that inherit from DataTemplateSelector and return HierarchicalDataTemplate from Page resources if the item is not RadSeparator. This is needed because you cannot put DataTemplate on UIElements (you can not put DataTemplate on RadSeparator).
    In the RadMenuExtended I have set the ItemTemplateSelector property instead of directly ItemTemplate to avoid setting ItemTemplate on RadSeparator.
    Also one more check in PrepareContainerForItemOverride method in both classes. I am checking if the cast to RadMenuItemExtended is not null now to avoid setting bindings on RadSeparator.

    The DataItem Items collection is now ObservableCollection<object> like discussed in the previous post.

    Please let me know if you need more help.

    Regards,
    Hristo
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  13. Nic
    Nic avatar
    32 posts
    Member since:
    Oct 2008

    Posted 27 Jan 2009 Link to this post

    Hi Hristo,

    Thank you very much - this solves the problem I was trying to fix.

    --Nic

  14. Rohit
    Rohit avatar
    12 posts
    Member since:
    Feb 2009

    Posted 10 Feb 2009 Link to this post

    Hi

    I want to know how to write the Click Event Handler for the menu items if I am using the
    HierarchicalDataTemplate. In this case all the menu items are created dynamicaly and I donot see a Click Event in RadMenu control. Please suggest how to attache event handler in this case. May be you can simply tell me if I want to do specific thing on click of

    "Item 2.3", may be I can get the DataItem in eventargs or something like that.

    Regards
    Rohit

  15. Rohit
    Rohit avatar
    12 posts
    Member since:
    Feb 2009

    Posted 10 Feb 2009 Link to this post

    Hi

    I want to know how to write the Click Event Handler for the menu items if I am using the
    HierarchicalDataTemplate. In this case all the menu items are created dynamicaly and I donot see a Click Event in RadMenu control. Please suggest how to attache event handler in this case. May be you can simply tell me if I want to do specific thing on click of

    "Item 2.3", may be I can get the DataItem in eventargs or something like that.

    Regards
    Rohit

  16. Hristo
    Admin
    Hristo avatar
    832 posts

    Posted 12 Feb 2009 Link to this post

    Hello Rohit,

    You can use AddHandler extension method to handle RadMenuItem.Click event.
    using Telerik.Windows;  
     
    public class Page1()  
    {  
       public Page1()  
       {  
          InitializeComponent();  
          radMenu1.AddHandler(radMenu1.AddHandler(RadMenuItem.ClickEvent, new RoutedEventHandler(OnMenuItemClick));  
       }  
     
       private void OnMenuItemClick(object sender, RoutedEventArgs e)  
       {  
        RadRoutedEventArgs args = e as RadRoutedEventArgs;  
        RadMenuItem menuItem = args.OriginalSource as RadMenuItem;  
       }  

    Let me know if you need more help.

    All the best,
    Hristo
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  17. Rohit
    Rohit avatar
    12 posts
    Member since:
    Feb 2009

    Posted 01 Apr 2009 Link to this post

    Hi Hristo,

    I have another problem. Lets say I have a top level menu and under it I have 3 sub menus and all these 3 are not visible. This may happen if my data is dynamic and its condional. Now I want, in this scenario, it should hide all the sub menu and should not display any arrow on the top level menu. Top level menu should be clickable now and should raise the Routed Click Event.

    Something like following which has only one child item and that is not visible, in this case it should allow user to click on Resize Canvas menu item.

     

     

     

    <

     

    telerik:RadMenu Name="MainMenu1" VerticalAlignment="Center" Height="31">

     

     

     

        <

     

    telerik:RadMenuItem Header="Resize Canvas" Click="Resize_RadMenuItem_Click" x:Name="ResizeMenu">

     

     

     

            <telerik:RadMenuItem Header="Sub menu Resize Canvas" Click="Resize_RadMenuItem_Click" Visibility="Collapsed" x:Name="ResizeMenu2"/>

     

     

     

        </telerik:RadMenuItem>

     

    </

     

    telerik:RadMenu>

     

     

     

     

     

     
    Please help me with it.

    Regards
    Rohit

     

  18. Hristo
    Admin
    Hristo avatar
    832 posts

    Posted 02 Apr 2009 Link to this post

    Hello Rohit,

    I see several problems with this approach:
    1. MenuItems that have children do not raise Click event (but you can use SubmenuOpened event instead). This is by design.
    2. You cannot have x:Name set to subItems (this is Silverlight 2 problem). If you are using ItemsSource to populate the Menu then this is not an issue.

    You can edit the control template and remove the Path (that is the arrow). But I'm not sure about dynamically changing the Path visibility (we should add more visual states to be able to handle this).

    Basically you can achieve this by removing (instead of hiding) the items that are not visible. This way you won't have arrow, and you will get Click event.

    I hope this help. Let me know if you need more information.


    All the best,
    Hristo
    the Telerik team

    Check out Telerik Trainer , the state of the art learning tool for Telerik products.
  19. Debbie
    Debbie avatar
    15 posts
    Member since:
    Mar 2009

    Posted 13 May 2009 Link to this post

    Hello

    I also have a RadMenu bound to 2 HierarchicalDataTemplates (each containing their own containerBindingCollection) and want to be able to bind my menuitems' Click event to my object's OnClick property. I've tried this in the ContainerBinding but to no avail... So now I've found this post and have tried to AddHandler to my RadMenu but that option is not available. Is this an old implementation? How would I be able to capture or set the MenuItems' Click events?

    Thanks!
    Debbie
  20. Valeri Hristov
    Admin
    Valeri Hristov avatar
    2252 posts

    Posted 13 May 2009 Link to this post

    Hi Debbie,

    The AddHandler() is an extension method from the Telerik.Windows namespace. You need to add the namespace to your usings at the beginning of the code behind - please, check Hristo Hristov's post from 2/12/2009 in this thread for a sample.

    Sincerely yours,
    Valeri Hristov
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  21. Debbie
    Debbie avatar
    15 posts
    Member since:
    Mar 2009

    Posted 13 May 2009 Link to this post

    Great, thanks! The Telerik.Windows was the missing key!
  22. Preethi Gracia
    Preethi Gracia avatar
    6 posts
    Member since:
    Jul 2009

    Posted 30 Jul 2009 Link to this post

    Hello,

        I am using Telerik RadControls for Silverlight Q2 2009. I have written code to dynamically to generate RadMenuItems in a RadMenu. Now to track the ClickEvent of the RadMenuItem, I have added a AddHandler as explained by Hristo. I have a added like
    appMenu.AddHandler(RadMenuItem.ClickEvent, new RoutedEventHandler(OnRadMenuItemClick), false); 
    where appMenu is the Name of the Telerik RadMenu. But I am getting an error at the new RoutedEventHandler(OnRadMenuItemClick) telling "No overload for 'OnRadMenuItemClick' matches delegate 'System.Windows.RoutedEventHandler'. Please help me and let me know what I am missing. I have also added reference to Telerik.Windows.

    Thanks,
    Preethi
  23. Rohit
    Rohit avatar
    12 posts
    Member since:
    Feb 2009

    Posted 30 Jul 2009 Link to this post

    Hello Preethi,

    I guess you need to write a method as below: be specific about the type of two arguments of the event handler. Mentioned error relates that your types of the event handler arguments not matching with the delegate RoutedEventHandler.

     private void OnRadMenuItemClick(object sender, RoutedEventArgs e)  
     {  
        //Add Code to Event Handler  
     } 

    If I am wrong then please mention which telerik version you are using as I am using Q1 2009 and it is working fine in that version.

    thanks
    Rohit
Back to Top
DevCraft banner