none
Silverlight 4: How to find source UI element from contextmenu’s menuitem_click?

    Question

  • I have a datagrid and I added silverlight 4 toolkit contextmenu to textbox in datagrid as follows. When users right click on the textbox, contextmenu is being displayed. When users click the menu item with Header "Test", "MenuItem_Click" is getting executed. Now I want to access the textbox from the MenuItem_Click and modify its properties like background etc. Is there anyway to find textbox element(which is contextmenu's parent) from MenuItem_Click event?

    It appears to me that I am missing something very simple.

     
    1    <my:DataGridTemplateColumn.CellTemplate>
    2    <DataTemplate>
    3        <TextBox Text="{Binding AcctId}"
    4                 Style="{StaticResource documentTextBoxStyle}"
    5                 ToolTipService.ToolTip="Right Click to modify parameters" >
    6            <toolkit:ContextMenuService.ContextMenu >
    7                <toolkit:ContextMenu >
    8                    <toolkit:MenuItem Header="Test" Click="MenuItem_Click"/>
    9                </toolkit:ContextMenu>
    10           </toolkit:ContextMenuService.ContextMenu>
    11       </TextBox>
    12   </DataTemplate>
    13   
    14   
    
     

    Tuesday, April 20, 2010 5:03 PM

Answers

  •  @bouNcInEsS,  they are explicity passing the name of the control as parameter, so in the case of datatemplate, it wont work.

    I saw the sources of the contextmenu in SL toolkit and found that there is a internal property called Owner which contains the reference to the owner, in our case the textbox, I made it public, now I am able to access the textbox.  Probably writing an additional method to ContextMenuService to fetch the owner of a contextmenu may be a better idea than to make Owner public.

    I figured out that it is also possible to  traverse all the UIElements and get reference to the textbox but it might not be the best way to do it.

    Thanks for you support guys.

     

     

    Wednesday, April 21, 2010 5:45 PM
  • You can try like below in MenuItem_Click event  

    //Get clicked row
    var dataGridrow = DataGridRow.GetRowContainingElement(sender as FrameworkElement);
    
    //Suppose column1 contains a TextBox
    var textBox = myDataGrid.Columns[1].GetCellContent(dataGridrow) as TextBox;
    
    
     
    Thursday, April 22, 2010 4:39 AM

All replies

  •  You can set the DataContext of your MenuItem to your TextBox, similar to this:

    ((MenuItem)sender).DataContext = MyTB;

    and the do some stuff in your handler, like:

    TextBox tb = ((MenuItem)sender).DataContext as TextBox;

    tb.Text = "Nice Stuff!";

    Tuesday, April 20, 2010 6:55 PM
  •  DataContext as TextBox ??? Why the datacontext of MenuItem will be TextBox?

    Tuesday, April 20, 2010 8:59 PM
  • You can change MenuItem's Background, Font etc.

     

    Tuesday, April 20, 2010 9:21 PM
  •  It will be the DataContext if you set it! That's an easy way to get to the TextBox in your handler and that's what you wanted to!?

    Wednesday, April 21, 2010 8:18 AM
  • Thank you for your answer. But how can we make the textbox as the datacontext for the MenuItem given that textbox is in datatemplate?

    Could you please share some XAML/code?

     

    Wednesday, April 21, 2010 3:03 PM
  • DataContext won't be TextBox. DataContext is the DataObject you Bind to the MenuItem. Since you did not use DataBinding, the DataContext is null. You won't get any control back by access DataContext.

    There is no TextBox in the MenuItem control. The Header is displayed in a ContentPresenter. If you take a look at the default Template of MenuItem it is like this (use Blend you can get any default template for any control):

    <Style x:Key="MenuItemStyle" TargetType="toolkit:MenuItem">
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="BorderBrush" Value="Transparent"/>
                <Setter Property="Padding" Value="4,3,2,3"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="toolkit:MenuItem">
                            <Grid>
                                <VisualStateManager.VisualStateGroups>
                                    <VisualStateGroup x:Name="CommonStates">
                                        <VisualState x:Name="Normal"/>
                                        <VisualState x:Name="Disabled">
                                            <Storyboard>
                                                <DoubleAnimation Duration="0" To="0.5" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Presenter"/>
                                            </Storyboard>
                                        </VisualState>
                                    </VisualStateGroup>
                                    <VisualStateGroup x:Name="FocusStates">
                                        <VisualState x:Name="Unfocused"/>
                                        <VisualState x:Name="Focused">
                                            <Storyboard>
                                                <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Bg"/>
                                                <ColorAnimation Duration="0" To="#40FFFFFF" Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)" Storyboard.TargetName="InnerBorder"/>
                                            </Storyboard>
                                        </VisualState>
                                    </VisualStateGroup>
                                </VisualStateManager.VisualStateGroups>
                                <Rectangle Fill="{TemplateBinding Background}" RadiusY="2" RadiusX="2" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="1"/>
                                <Rectangle x:Name="Bg" Opacity="0" RadiusY="2" RadiusX="2" Stroke="#8071CBF1" StrokeThickness="1">
                                    <Rectangle.Fill>
                                        <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
                                            <GradientStop Color="#34C5EBFF" Offset="0"/>
                                            <GradientStop Color="#3481D8FF" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Rectangle.Fill>
                                </Rectangle>
                                <Rectangle x:Name="InnerBorder" Margin="1" RadiusY="2" RadiusX="2" Stroke="Transparent"/>
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition MinWidth="24" Width="Auto"/>
                                        <ColumnDefinition Width="4"/>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="17"/>
                                    </Grid.ColumnDefinitions>
                                    <ContentPresenter Content="{TemplateBinding Icon}" Margin="1" VerticalAlignment="Center"/>
                                    <ContentPresenter x:Name="Presenter" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="2" Margin="{TemplateBinding Padding}"/>
                                </Grid>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

    As I said, you can set Background on the MenuItem itself, Or you can modify the Style use VisualState to define the look of the clicked item.

     

     

    Wednesday, April 21, 2010 3:41 PM
  •  WoW. Excellent answer!

    However, what good is it going to bring if we cant access the UIElement for which we are attaching context menu. Context menu is used for contextual purposes and hence the name context menu and if we cant access the UIElement that owns it, then it beats the purpose of context menu. M$ guyz please fix this.

     

    Wednesday, April 21, 2010 4:49 PM
  • @sladapter: i don't think funwithcoding intention is to change some propertys of the menueitem, that would be easy.

    @funwithcoding: there is a sample in the toolkits sample folder where they did exactly what i told you, by setting the Datacontext either on the menuitem or directly on the contextmenue to that control you wanna reference in the clickhandler,...but i havn't verified this using a Datatemplate, but i agree there should be an easy way to access the contextmenues root control, and maybe there is but i don't know, yet!

    Maybe commanding is the right answer here.

    Wednesday, April 21, 2010 5:09 PM
  •  @bouNcInEsS,  they are explicity passing the name of the control as parameter, so in the case of datatemplate, it wont work.

    I saw the sources of the contextmenu in SL toolkit and found that there is a internal property called Owner which contains the reference to the owner, in our case the textbox, I made it public, now I am able to access the textbox.  Probably writing an additional method to ContextMenuService to fetch the owner of a contextmenu may be a better idea than to make Owner public.

    I figured out that it is also possible to  traverse all the UIElements and get reference to the textbox but it might not be the best way to do it.

    Thanks for you support guys.

     

     

    Wednesday, April 21, 2010 5:45 PM
  • Which control your want to access? The controls in the MenuItem Template or the Control that owns the ContextMenu?

    OK, I re-read your first post. Do you mean the TextBox on the DataGrid.SelectedRow when you popup the ConextMenu? I thought you meant the control that displayed your Header in the MenuItem.


     

     

     

    Wednesday, April 21, 2010 5:55 PM
  •  Maybe i'm wrong but they are using a Datatemplate in the sample too. However glad u found a working solution. In time i will digg little more into commands.

    Wednesday, April 21, 2010 5:57 PM
  • You can try like below in MenuItem_Click event  

    //Get clicked row
    var dataGridrow = DataGridRow.GetRowContainingElement(sender as FrameworkElement);
    
    //Suppose column1 contains a TextBox
    var textBox = myDataGrid.Columns[1].GetCellContent(dataGridrow) as TextBox;
    
    
     
    Thursday, April 22, 2010 4:39 AM
  •  Thank you amyo.

    Tuesday, May 04, 2010 5:16 PM