Number8
Number8

Reputation: 12910

How to specify a DataContext whet adding the ContextMenu to the DataGridTemplateColumn?

I am cooking up DataGridTemplateColumns programmatically via
DataTemplate dtStringTemplate = (DataTemplate)XamlReader.Load(sr, pc); dataGridTemplateColumn.CellTemplate = dtStringTemplate;

I tried adding the ContextMenu to the DataGrid, but any editable cells used their own context menu.

So far, this post has gotten me as far as getting the TextBox context menu to appear as expected: How to add a ContextMenu in the WPF DataGridColumn in MVVM?

Using the post mentioned above as a guide, I have created the Style and the ContextMenu in App.xaml; when I right-click on the Cell in the DataGrid, my context menu appears. However, I can't get the associated command to fire, and I suspect the binding is not right. Here's the xaml in App.xaml:

<ContextMenu x:Key="DataGridContextMenu">
        <MenuItem Header="MenuItem One" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.CmdMenuItemOne}" />
        <MenuItem Header="MenuItem Two" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.CmdMenuItemOne}" />
    </ContextMenu>

The DataContext for the DataGrid is MyViewModel; MyViewModel has a public DelegateCommand named CmdMenuItemOne.
Unfortunately, CmdMenuItemOne is never called.
What am I misunderstanding in the binding? Thanks ...

Upvotes: 0

Views: 470

Answers (2)

AnjumSKhan
AnjumSKhan

Reputation: 9827

Use very simple approach given below.

<Window.Resources>
    <FrameworkElement x:Key="DCKey" />
</Window.Resources>

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = vm;

        ((FrameworkElement)this.Resources["DCKey"]).DataContext = vm;
    }

<MenuItem Header="DoSomething" Command="{Binding DataContext.Cmd, Source={StaticResource DCKey}}"/>

Upvotes: 1

Ilan
Ilan

Reputation: 2782

since the fact that a ContextMenu isn't a part of a DataGrid's visual tree, we cannot access property defined in the DataGrid's DataContext. But we can do the next workaround to do that.

  1. Create a Boolean attached property to define the next thing; if the value of added property is true, it will find the visual parent of the object this property is attached to, and assign a parent's DataContext to the Tag property of the object this property is attached to, from the other hand if the value of the attached property is false , the Tag property will be assign to null.
  2. Create an extension helper method that will scan the visual tree of caller and will return it's(caller) parent of specific type.
  3. Create default style for DataGridCell object that will use a dependency property which was described above and will define the ContextMenu. Set this style as resource in App.xaml(take in account that this style will be used by all DataGridCell objects in your project).

style code (should be in App.xaml)

<Style TargetType="DataGridCell">
        <Setter Property="dataGridCreateColumnAndBindIteFromCodeBehind:DataGridAttached.SetDataGridDataContextToTag" Value="True"></Setter>
         <Setter Property="ContextMenu">
             <Setter.Value>
                <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                    <MenuItem Header="MenuItem One"
                              Command="{Binding CmdMenuItemOne}" />
                    <MenuItem Header="MenuItem Two" 
                              Command="{Binding CmdMenuItemTwo}" />
                </ContextMenu>
            </Setter.Value>
         </Setter></Style>

attached property code

public class DataGridAttached
{

    public static readonly DependencyProperty SetDataGridDataContextToTagProperty = DependencyProperty.RegisterAttached(
        "SetDataGridDataContextToTag", typeof (bool), typeof (DataGridAttached), new PropertyMetadata(default(bool), SetParentDataContextToTagPropertyChangedCallback));

    public static void SetSetDataGridDataContextToTag(DependencyObject element, bool value)
    {
        element.SetValue(SetDataGridDataContextToTagProperty, value);
    }

    public static bool GetSetDataGridDataContextToTag(DependencyObject element)
    {
        return (bool) element.GetValue(SetDataGridDataContextToTagProperty);
    }

    private static void SetParentDataContextToTagPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {
        PerformDataContextToTagAssignment(dependencyObject as FrameworkElement, (bool)args.NewValue);
    }

    private static void PerformDataContextToTagAssignment(FrameworkElement sender, bool isAttaching)
    {
        var control = sender;
        if (control == null) return;
        if (isAttaching == false)
        {
            control.Tag = null;
        }
        else
        {
            var dataGrid = control.FindParent<DataGrid>();
            if (dataGrid == null) return;
            control.Tag = dataGrid.DataContext;
        }
    }
}

helper code

public static class VisualTreeHelperExtensions
{
    public static T FindParent<T>(this DependencyObject child) where T : DependencyObject
    {
        while (true)
        {
            //get parent item
            DependencyObject parentObject = VisualTreeHelper.GetParent(child);

            //we've reached the end of the tree
            if (parentObject == null) return null;

            //check if the parent matches the type we're looking for
            T parent = parentObject as T;
            if (parent != null)
                return parent;
            child = parentObject;
        }
    }
}

Regards.

Upvotes: 0

Related Questions