RadContextMenu and data binding

9 posts, 2 answers
  1. hwsoderlund
    hwsoderlund avatar
    419 posts
    Member since:
    Aug 2006

    Posted 20 Oct 2008 Link to this post

    I've put the RadContextMenu inside a DataGridTemplateRow, and was expecting the data context of the parent row to propagate to the context menu, but the data context on the menu is always null (I'm doing this in the "Opened" event of RadContextMenu). When debugging, I can see the non-public member "elementWithContextMenu" which holds a reference to the parent object (in my case a StackPanel). If this property was public I could just grab the DataContext from there. However, I cannot find a way to get to it in code. Please consider making this member public so that we can access it. It would also be a good thing if the data context was inherited to the menu from the parent container. Also, if you can think of a better way to accomplish the scenario, please let me know. What I'm trying to do is to have a clickable area (probably a small arrow) in each data grid row that will pop up a context menu with options for each row (such as "Delete", "Edit" etc.). I cannot have a single context menu for the data grid since the access rights of the logged in user may vary for each object in the grid.
  2. hwsoderlund
    hwsoderlund avatar
    419 posts
    Member since:
    Aug 2006

    Posted 20 Oct 2008 Link to this post

    Also, please create a forum for RadContextMenu.
  3. DevCraft banner
  4. Answer
    Miroslav
    Admin
    Miroslav avatar
    922 posts

    Posted 23 Oct 2008 Link to this post

    Hello Henrik,

    Thanks again for the great Feedback and sorry for not replying sooner.

    - The RadContextMenu forum will be the RadMenu forum, it will be renamed soon.

    - I am not sure why elementWithContextMenu is hidden, surely a public getter would not hurt. As far as I remember a ContextMenuService (like in WPF) is planned so one and the same menu can be used for multiple elements, so maybe this is why the property is internal. I will make it public if it's ok.

    - Inheriting the DataContext is a great idea! Why didn't we  think of it :).

    I tried and implemented your specific scenario: The context menu is bound to the DataGridRow:



    I used the EventManager and ClassHandlers. Handling a routed event on a class level means that you can add a method that will be invoked every time a specific event goes through an instance of a given class. This means that you can handle the ContextmenuOpen event on the GridRow and have both the ContextMenu (OriginalSource) and the GridRow there.

    Handling Events at class level are helpful because you don't have to add a handler for each instance and you do not need direct access to the instances.

    One more thing: Whenever you see RoutedEventArgs, cast them to RadRoutedEventArgs or their subclass. Some of the RoutedEventArgs's properties were made internal and we cannot set them anymore, this is not a problem with our event args though.

    Here is the code for the example:

     //Static Constructor:  
     static ContextMenuInDataGrid()  
     {  
         EventManager.RegisterClassHandler(typeof(DataGridRow), RadContextMenu.OpenedEvent, new RoutedEventHandler(OnContextMenuOpen));  
     }  
     
     private static void OnContextMenuOpen(object sender, RoutedEventArgs e)  
     {  
         var radE = e as RadRoutedEventArgs;  
         var contextMenu = radE.OriginalSource as RadContextMenu;  
         var gridRow = sender as DataGridRow;  
     
         contextMenu.DataContext = gridRow.DataContext;  
     }  
     

    And the xaml:

    <data:DataGrid x:Name="dataGrid" AutoGenerateColumns="False">  
        <data:DataGrid.Columns> 
            <data:DataGridTemplateColumn Header="E">  
                <data:DataGridTemplateColumn.CellTemplate> 
                    <DataTemplate> 
                        <Button Content="E">  
                            <nav:RadContextMenu.ContextMenu> 
                                <nav:RadContextMenu EventName="Click" 
                                                    ItemsSource="{Binding MenuItems}" /> 
                            </nav:RadContextMenu.ContextMenu> 
                        </Button> 
                    </DataTemplate> 
                </data:DataGridTemplateColumn.CellTemplate> 
            </data:DataGridTemplateColumn> 
            <data:DataGridTextColumn Binding="{Binding Name}" Header="Name"/>  
            <data:DataGridCheckBoxColumn Binding="{Binding IsSomething}" Header="Is Something"/>  
            <data:DataGridTextColumn Binding="{Binding Value}" 
                                     Header="Value" /> 
        </data:DataGrid.Columns> 
    </data:DataGrid> 

    All the best,
    Miroslav
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  5. hwsoderlund
    hwsoderlund avatar
    419 posts
    Member since:
    Aug 2006

    Posted 23 Oct 2008 Link to this post

    Hi, thanks for an excellent solution to my problem. I never fully understood the power of routed events until now. It's a pity they're not fully supported in Silverlight out of the box.

    However, when I implemented your solution another problem began occuring. The OnContextMenuOpen event were thrown several times. This had to do with the fact that I'm creating multiple instances of my datagrid page. Since the class handler is registered in the constructor of the data grid page, it is registered several times. Is there a way to either unregister a class handler before registering the new one, or to detect whether a class handler already exists?

    I guess a better solution is to move the class handler registration to the constructor of the root application object, like so:

    public BaseApplication() 
        this.Startup += new StartupEventHandler(BaseApplication_Startup); 
        this.UnhandledException += new EventHandler<ApplicationUnhandledExceptionEventArgs>(BaseApplication_UnhandledException); 
        this.Exit += new EventHandler(BaseApplication_Exit); 
     
        EventManager.RegisterClassHandler(typeof(DataGridRow), RadContextMenu.OpenedEvent, new RoutedEventHandler(new BaseDataGridPage().OnContextMenuOpen)); 


    It seems ugly though, having to create the new instance just to hook up the event. Is there a better way to do this?

  6. Miroslav
    Admin
    Miroslav avatar
    922 posts

    Posted 23 Oct 2008 Link to this post

    Hi Henrik,

    Yes, Routed events can be helpful not only in controls but in an application as well! Creating your own routed events is very easy.

    Back to your app: You do not need an instance of an object to create the handler. Registering for a class handler normally happens in static constructors (or application startup) and you normally pass a static method as a handler as well (as in my example).

    The static constructor of BaseDataGridPage seems like a good place for that. Then in the handler you have an instance of the GridRow and the ContextMenu. So at the time of registering of the event, you do not need an instance of any object. If you for some reason need the specific BaseDataGridPage instance, just add a class handler for this class as well.

    Removing or testing for class handlers is not supported.

    Do not hesitate to ask if you need help with the routed events.

    Greetings,
    Miroslav
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  7. hwsoderlund
    hwsoderlund avatar
    419 posts
    Member since:
    Aug 2006

    Posted 23 Oct 2008 Link to this post

    Got it working now using static constructors. Thanks!
  8. hwsoderlund
    hwsoderlund avatar
    419 posts
    Member since:
    Aug 2006

    Posted 13 Nov 2008 Link to this post

    I noticed that DataContext is still not inherited to the context menu in the Q3-release. Is there something explicit I need to do to make it work, or didn't it make the release?

    Also: elementWithContextMenu is still non-public. Any chance of getting that public accessor?

    Best regards, /Henrik
  9. Answer
    Hristo
    Admin
    Hristo avatar
    832 posts

    Posted 14 Nov 2008 Link to this post

    Hello Henrik,

    I'm glad to inform you that DataContext is inherited always. Now if you want RadContextMenu to have different DataContext you should attach handler to Opened event and set the specific DataContext.
    We will release this update (which include RadGridView) later or on Monday.

    Best wishes,
    Hristo
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  10. hwsoderlund
    hwsoderlund avatar
    419 posts
    Member since:
    Aug 2006

    Posted 14 Nov 2008 Link to this post

    That's great news! Thanks!
Back to Top
DevCraft banner