This is a migrated thread and some comments may be shown as answers.

RadContextMenu and data binding

8 Answers 310 Views
Menu
This is a migrated thread and some comments may be shown as answers.
hwsoderlund
Top achievements
Rank 1
hwsoderlund asked on 20 Oct 2008, 09:41 AM
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.

8 Answers, 1 is accepted

Sort by
0
hwsoderlund
Top achievements
Rank 1
answered on 20 Oct 2008, 09:43 AM
Also, please create a forum for RadContextMenu.
0
Accepted
Miroslav
Telerik team
answered on 23 Oct 2008, 08:45 AM
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.
0
hwsoderlund
Top achievements
Rank 1
answered on 23 Oct 2008, 10:10 AM
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?

0
Miroslav
Telerik team
answered on 23 Oct 2008, 10:43 AM
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.
0
hwsoderlund
Top achievements
Rank 1
answered on 23 Oct 2008, 11:15 AM
Got it working now using static constructors. Thanks!
0
hwsoderlund
Top achievements
Rank 1
answered on 13 Nov 2008, 01:32 PM
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
0
Accepted
Hristo
Telerik team
answered on 14 Nov 2008, 01:26 PM
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.
0
hwsoderlund
Top achievements
Rank 1
answered on 14 Nov 2008, 01:54 PM
That's great news! Thanks!
Tags
Menu
Asked by
hwsoderlund
Top achievements
Rank 1
Answers by
hwsoderlund
Top achievements
Rank 1
Miroslav
Telerik team
Hristo
Telerik team
Share this question
or