DataGridColumn from ContextMenu.MenuItem Click

Nov 9, 2008 at 8:10 PM
Hi,

I am trying to add a feature to enable users to hide columns by right clicking on the DataGridColumnHeader and opening a ContextMenu. I have managed to work out which column was 'clicked on' by using the code below, but I don't think it is very tidy and I'm sure there must be a simpler and/or better way. I have a DataTable bound to the DataGrid, which has a Style and DataTemplate applied to the DataGridColumnHeader. The ContextMenu is applied in the DataGridHeaderTemplate.

 

private void MenuItem_Click(object sender, RoutedEventArgs e)

 

{

 

MenuItem mi = sender as MenuItem;

 

 

ContextMenu cm = (ContextMenu)Helpers.FindElementOfTypeUp(mi, typeof(ContextMenu));

 

 

 

string s = cm.DataContext.ToString();

 

 

foreach (DataGridColumn dgc in this.dataGrid.Columns)

 

{

 

if ((string)dgc.Header == s)

 

dgc.Visibility =

Visibility.Hidden;

 

}

}


Any ideas for a better way to do this?

Thanks
Nov 10, 2008 at 5:15 PM
This also took me some time to figure out.

where my header template for the grid has:
               <MenuItem Header=""Hide"" ToolTip=""Hide"" Command=""ContextMenu"" CommandParameter=""{0}""></MenuItem>


and cell template has:
<Border x:Name=""{0}""


The way i solved is as follows:
I iterate through all columns looking in the cell template for the name passed through the command parameter.

                GridViewRowPresenter rowpresenter = Utilities.GetVisualChild<GridViewRowPresenter>(myListBoxItem);
                for (int columnIndex = 0; columnIndex < rowpresenter.Columns.Count; columnIndex++)
                {
                    ContentPresenter cp = Utilities.GetVisualChild<ContentPresenter>(rowpresenter, columnIndex);
                    object test = cp.ContentTemplate.FindName(args.Parameter.ToString(), cp);
                    if (test != null)
                    {
                        rowpresenter.Columns[columnIndex].Width = 0;
                    }
                }

The above works...


==============================================
here's an open issue i would like to fundamentally understand.

I originally tried to use GridViewHeaderRowPresenter to extract the column's ContentPresenter and kept getting null.
Can I not get to the header's content presenter??

                GridViewHeaderRowPresenter headerpresenter = Utilities.GetVisualChild<GridViewHeaderRowPresenter>(myListBoxItem);
                for (int columnIndex = 0; columnIndex < headerpresenter.Columns.Count; columnIndex++)
                {
                    ContentPresenter cp = Utilities.GetVisualChild<ContentPresenter>(headerpresenter, columnIndex);
// cp is null so i get exception when the next line is run. ... ? why???
                    object test = cp.ContentTemplate.FindName(args.Parameter.ToString(), cp);
                    if (test != null)
                    {
                        headerpresenter.Columns[columnIndex].Width = 0;
                    }
                } 

Nov 11, 2008 at 12:52 PM
Hi,

Leblanc - thanks for the reply. Using the CommandParameter in my case can simplify getting the Column Binding, I tried using the following in the XAML:

CommandParameter="{Binding}" which gets me the Binding for the column. I still have to iterate through the columns in the DataGrid to find the appropriate one though. Does anyone know how to eliminate the need to iterate over the columns collection and get a direct reference to the DataGridColumn?


Thanks
Nov 11, 2008 at 6:11 PM
Hi again,

I am also seeing another problem regarding a ContextMenu applied to a DataGridColumnnHeader. I have two DataGrids in a TabControl and unless I actually click somewhere in the desired grid before I right click on the header the 'sender' object references the other grid, yet the ContextMenu appears in the correct place.

If I use the 'click' event rather than the RoutedCommand it is OK, but then I can't use the CommandParameter to get the column binding!

Help!
Coordinator
Nov 13, 2008 at 3:14 PM
This doesn't support the MVVM pattern so well, but if you listen to the ContextMenuOpening event you can keep track of the column that has been clicked.

 

 

<Style x:Key="dgColumnHeaderStyle_Default" TargetType="{x:Type toolkit:DataGridColumnHeader}">
    <EventSetter Event="ContextMenuOpening" Handler="columnHeader_ContextMenuOpening"/>
    <Setter Property="ContextMenu" Value="{DynamicResource cm_columnHeaderMenu}" />
</Style>

 

private

 

void columnHeader_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
    DataGridColumnHeader columnHeader = sender as DataGridColumnHeader;
    _curColHeaderFromContextMenu = columnHeader;
}

Then when a menu item is clicked you can access _curColHeaderFromContextMenu.Column.  Still looking for a better solution though.










Aug 25, 2009 at 7:39 AM
Edited Aug 25, 2009 at 7:41 AM

any updates on this one? i refuse to believe that there is no OOP way to to this.

using a temporary variable to share data between those two events pretty much violates every OOP paradigm i've ever heard/read about ...

there has to be some way to pull the required information from the click event of the contextmenu item ...

Sep 15, 2009 at 9:08 PM
Edited Sep 30, 2009 at 5:53 AM
I was looking for a way to get the WPF DataGrid column header from which the user clicked the context menu.
This is the solution I used.
XML:

<ContextMenu x:Key="ColumnHeaderContextMenu">
  <MenuItem Header="Menu Item..." Click="MenuItem_Click" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}" />
</ContextMenu>


<Style x:Key="ColumnHeaderStyle" TargetType="{x:Type dg:DataGridColumnHeader}">
  <Setter Property="ContextMenu" Value="{StaticResource ColumnHeaderContextMenu}" />
</Style>


<dg:DataGrid 
...
  ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"

Code:

private void MenuItem_Click(object sender, RoutedEventArgs e) {
  MenuItem mi = sender as MenuItem;
  ContextMenu cm = mi.CommandParameter as ContextMenu;
  DataGridColumnHeader ch = cm.PlacementTarget as DataGridColumnHeader;
  int datagridHeaderColumnNumber = ch.DisplayIndex;

  // do something...
}

Jun 19, 2010 at 10:45 AM

Hi,

 

I've been revisiting my code for this and was hoping that there might be an update on how to achieve this (i.e. get the DataColumn from an event on the DataGridColumnHeader) in a more "MVVM" way? Or this that a contradiciton in terms?

 

Thanks