1

I am stumped yet again from WPF. Sometimes things just are not straight forward! I have searched high and low for an answer but everything I have tried has failed.

I am trying to create a generic Copy and Paste menu by using a datatemplate. Then each control I wish to use that contextmenu I can just set the template of that control and provide the correct function bindings and in theory it should just work. Instead of duplicating the same menu in each custom control file. But apparently ContextMenu's are just a nightmare.

What I know:

  1. Context menus do not inherit the data context because they are not part of the visual tree
  2. I can use DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}} to get around this and it should inherit.
  3. I cannot use WPF inspector to find out if the datacontext of the context menu is NULL. Which is annoying. Maybe there is another way. But there are no errors in the output so I have to assume its not null.

So based on my code it SHOULD WORK. Can somebody please point out the exceptionally magical combination of , "oh wait, in WPF you cant do this, and this and if this and this equals this". Answer? :)

Here is my custom control (Excuse the mess I have been hacking to try get this working!)

    /// <summary>
/// Interaction logic for Pointer.xaml
/// </summary>
public partial class Expander : UserControl
{
    /// <summary>
    /// Command_ArrayAdd
    /// </summary>
    public class PasteCommand : ICommand
    {
        public PasteCommand(Expander _parent)
        {
            m_Parent = _parent;
        }

        /// <summary>
        /// Execute
        /// </summary>
        public bool Execute()
        {
            return true;
        }

        /// <summary>
        /// UnExecute
        /// </summary>
        public void UnExecute()
        {
        }

        Expander m_Parent;
    }

    /// <summary>
    /// Command_ArrayAdd
    /// </summary>
    public class CopyCommand : ICommand
    {
        public CopyCommand()
        {
        }

        /// <summary>
        /// Execute the change on the field
        /// </summary>
        public bool Execute()
        {
            return true;
        }

        /// <summary>
        /// Undo the change on the field
        /// </summary>
        public void UnExecute()
        {
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public PasteCommand OnPasteCommand
    {
        get { return m_OnPasteCommand;  }
        set { m_OnPasteCommand = value; }
    }
    PasteCommand m_OnPasteCommand;

    /// <summary>
    /// 
    /// </summary>
    public CopyCommand OnCopyCommand
    {
        get { return m_OnCopyCommand; }
        set { m_OnCopyCommand = value; }
    }
    CopyCommand m_OnCopyCommand;

    public Expander(Database.DatabaseInstance.Struct _dbStruct, string _headerName)
    {
        try
        {
            InitializeComponent();
        }
        catch (System.Exception ex)
        {
            ErrorConsole.Instance.LogError(ex.Message + "\n" + ex.InnerException.Message);
        }

        m_dbStruct = _dbStruct;
        DataContext = this;
        m_HeaderName = _headerName;
        ExpanderLabel.Content = m_HeaderName;

        //AddHandler(CustomEvents.Copy, new RoutedEventHandler(OnCopyEvent));

        m_OnCopyCommand = new CopyCommand();
        m_OnPasteCommand = new PasteCommand(this);
    }

    private void OnUnloaded(object obj, RoutedEventArgs e)
    {
        //RemoveHandler(CustomEvents.Copy, new RoutedEventHandler(OnCopyEvent));
    }

    void OnMenuOpened(object obj, RoutedEventArgs e)
    {
        //string _clipboardData = (string)Clipboard.GetDataObject().GetData(DataFormats.Text);
        //((MenuItem)ExpanderLabel.Template.FindName("PasteMenuItem", ExpanderLabel)).IsEnabled = View.ViewModel.CanCopyFromTo(_clipboardData, m_dbStruct);
    }

    void _OnCopyCommand()
    {
        Clipboard.SetDataObject(m_dbStruct.Serialized);
    }

    void _OnPasteCommand()
    {
        CommandManager.Instance.Queue(new Database.DatabaseCommands.Command_PasteStruct(m_dbStruct, null));
    }

    Database.DatabaseInstance.Struct m_dbStruct;
    string m_HeaderName;
}

Here is my data template:

    <Style x:Key="RightClickCopyPasteLabel" TargetType="{x:Type Label}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Label">
                <Label>
                <Label.ContextMenu>
                        <ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
                            <MenuItem x:Name="CopyMenuItem" Command="{Binding DataContext.OnCopyCommand, PresentationTraceSources.TraceLevel=High}" InputGestureText="Ctrl+C" Header="Copy" />
                            <MenuItem x:Name="PasteMenuItem" Command="{Binding DataContext.OnPasteCommand, PresentationTraceSources.TraceLevel=High}" InputGestureText="Ctrl+P" Header="Paste" />
                        </ContextMenu>
                </Label.ContextMenu>
                </Label>
            <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="true">
                        <Setter Property="FontWeight" Value="Bold"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

EDIT:

On another thread I found a way of outputting a bunch more data but unfortunately this doesn't mean anything to me. This is what I get when I right click:

System.Windows.Data Warning: 96 : BindingExpression (hash=13029119): Got PropertyChanged event from MenuItem (hash=24459401) for DataContext
System.Windows.Data Warning: 79 : BindingExpression (hash=13029119): Deactivate
System.Windows.Data Warning: 103 : BindingExpression (hash=13029119): Replace item at level 0 with {NullDataItem}
System.Windows.Data Warning: 103 : BindingExpression (hash=13029119): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 78 : BindingExpression (hash=13029119): Activate with root item Label (hash=25041938)
System.Windows.Data Warning: 107 : BindingExpression (hash=13029119):   At level 0 using cached accessor for Label.DataContext: DependencyProperty(DataContext)
System.Windows.Data Warning: 104 : BindingExpression (hash=13029119): Replace item at level 0 with Label (hash=25041938), using accessor DependencyProperty(DataContext)
System.Windows.Data Warning: 101 : BindingExpression (hash=13029119): GetValue at level 0 from Label (hash=25041938) using DependencyProperty(DataContext): Expander (hash=20086501)
System.Windows.Data Warning: 107 : BindingExpression (hash=13029119):   At level 1 using cached accessor for Expander.OnCopyCommand: RuntimePropertyInfo(OnCopyCommand)
System.Windows.Data Warning: 104 : BindingExpression (hash=13029119): Replace item at level 1 with Expander (hash=20086501), using accessor RuntimePropertyInfo(OnCopyCommand)
System.Windows.Data Warning: 101 : BindingExpression (hash=13029119): GetValue at level 1 from Expander (hash=20086501) using RuntimePropertyInfo(OnCopyCommand): CopyCommand (hash=9651034)
System.Windows.Data Warning: 80 : BindingExpression (hash=13029119): TransferValue - got raw value CopyCommand (hash=9651034)
System.Windows.Data Warning: 84 : BindingExpression (hash=13029119): TransferValue - implicit converter produced <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=13029119): TransferValue - using final value <null>
System.Windows.Data Warning: 96 : BindingExpression (hash=58885068): Got PropertyChanged event from MenuItem (hash=65477567) for DataContext
System.Windows.Data Warning: 79 : BindingExpression (hash=58885068): Deactivate
System.Windows.Data Warning: 103 : BindingExpression (hash=58885068): Replace item at level 0 with {NullDataItem}
System.Windows.Data Warning: 103 : BindingExpression (hash=58885068): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 78 : BindingExpression (hash=58885068): Activate with root item Label (hash=25041938)
System.Windows.Data Warning: 107 : BindingExpression (hash=58885068):   At level 0 using cached accessor for Label.DataContext: DependencyProperty(DataContext)
System.Windows.Data Warning: 104 : BindingExpression (hash=58885068): Replace item at level 0 with Label (hash=25041938), using accessor DependencyProperty(DataContext)
System.Windows.Data Warning: 101 : BindingExpression (hash=58885068): GetValue at level 0 from Label (hash=25041938) using DependencyProperty(DataContext): Expander (hash=20086501)
System.Windows.Data Warning: 107 : BindingExpression (hash=58885068):   At level 1 using cached accessor for Expander.OnPasteCommand: RuntimePropertyInfo(OnPasteCommand)
System.Windows.Data Warning: 104 : BindingExpression (hash=58885068): Replace item at level 1 with Expander (hash=20086501), using accessor RuntimePropertyInfo(OnPasteCommand)
System.Windows.Data Warning: 101 : BindingExpression (hash=58885068): GetValue at level 1 from Expander (hash=20086501) using RuntimePropertyInfo(OnPasteCommand): PasteCommand (hash=50152377)
System.Windows.Data Warning: 80 : BindingExpression (hash=58885068): TransferValue - got raw value PasteCommand (hash=50152377)
System.Windows.Data Warning: 84 : BindingExpression (hash=58885068): TransferValue - implicit converter produced <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=58885068): TransferValue - using final value <null>
System.Windows.Data Warning: 96 : BindingExpression (hash=13029119): Got PropertyChanged event from MenuItem (hash=24459401) for DataContext
System.Windows.Data Warning: 79 : BindingExpression (hash=13029119): Deactivate
System.Windows.Data Warning: 103 : BindingExpression (hash=13029119): Replace item at level 0 with {NullDataItem}
System.Windows.Data Warning: 103 : BindingExpression (hash=13029119): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 78 : BindingExpression (hash=13029119): Activate with root item <null>
System.Windows.Data Warning: 106 : BindingExpression (hash=13029119):   Item at level 0 is null - no accessor
System.Windows.Data Warning: 103 : BindingExpression (hash=13029119): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 80 : BindingExpression (hash=13029119): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=13029119): TransferValue - using fallback/default value <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=13029119): TransferValue - using final value <null>
System.Windows.Data Warning: 96 : BindingExpression (hash=58885068): Got PropertyChanged event from MenuItem (hash=65477567) for DataContext
System.Windows.Data Warning: 79 : BindingExpression (hash=58885068): Deactivate
System.Windows.Data Warning: 103 : BindingExpression (hash=58885068): Replace item at level 0 with {NullDataItem}
System.Windows.Data Warning: 103 : BindingExpression (hash=58885068): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 78 : BindingExpression (hash=58885068): Activate with root item <null>
System.Windows.Data Warning: 106 : BindingExpression (hash=58885068):   Item at level 0 is null - no accessor
System.Windows.Data Warning: 103 : BindingExpression (hash=58885068): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 80 : BindingExpression (hash=58885068): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=58885068): TransferValue - using fallback/default value <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=58885068): TransferValue - using final value <null>
Asheh
  • 1,547
  • 16
  • 25
  • What is the DataContext of the Label? I think changing the command to: Command="{Binding DataContext.OnCopyCommand, ElementName=NameOfMyUserControl}" or Command="{Binding DataContext.OnCopyCommand, RelativeSource={RelativeSource FindAncestor={x:Type UserControl}}}" can help. – JPOne Aug 04 '14 at 08:34
  • Thanks for the speedy response. I assumed the – Asheh Aug 04 '14 at 08:35
  • Hmm your second chunk of code produces an error. "Find Ancestor was not part of relative source" And the first part. ElementName, is that the user control in the parent? – Asheh Aug 04 '14 at 08:38
  • But you changed the datacontext of the parenting MenuItems so they don't know the ment datacontext. – JPOne Aug 04 '14 at 08:38
  • I set the DataContext to the placement target's data context. So this should be the DataContext of its parent. And this contains the commands I am trying to call. Right? – Asheh Aug 04 '14 at 08:43
  • You're supposed to use the `Tag` property of the `PlacementTarget` to pass the `DataContext` to the `ContextMenu`... I don't see you doing that. See my answer to the [Add context menu in datagrid, how to get the select Item value](http://stackoverflow.com/questions/18610905/add-context-menu-in-datagrid-how-to-get-the-select-item-value/18611364#18611364) question for further help. – Sheridan Aug 04 '14 at 08:45
  • I am not using a data template? – Asheh Aug 04 '14 at 08:49
  • The DataContext of the MenuItem is "Control.Label" and not the DataContext of UserControl (with the commands) – JPOne Aug 04 '14 at 08:49
  • I think I understand. I thought DataContext was inherited from the parent. So – Asheh Aug 04 '14 at 08:53
  • could you please remove `PresentationTraceSources.TraceLevel=High` and show us what data errors you see in the output window? information in question is very detailed to analyse for this issue. – pushpraj Aug 04 '14 at 08:53
  • is it possible for you to post a working sample of your app which can reproduce the same issue you are facing? May I try to debug for you. – pushpraj Aug 04 '14 at 09:07
  • 1
    I notice that `ICommand` implementation does not have `Execute(object)` method, so wondering what `ICommand` you are using here? it should be `System.Windows.Input.ICommand` – pushpraj Aug 04 '14 at 09:09
  • A ContextMenu is a Popup with a new DataContext. If you set you have the datacontext of the expander. – JPOne Aug 04 '14 at 09:10
  • PUSHPRAJ!!!!!!! YOU ARE A GENIUS!!! I have my own implementation of ICOMMAND and the namespace wasn't set. And I had made a mistake on the implementation. Everything else was correct and I am not crazy. Thank you! – Asheh Aug 04 '14 at 09:16

1 Answers1

1

By looking at your code everything looks OK at first glance but there is still an issue

the issue is because of incorrect ICommand interface was used to implement Commands. An implementation of System.Windows.Input.ICommand is used to bind Commands in WPF

Let me now try to explain how I figured out the issue.

look at these 3 lines from the trace

System.Windows.Data Warning: 80 : BindingExpression (hash=58885068): TransferValue - got raw value PasteCommand (hash=50152377)

above say that it got a value of type PasteCommand from the binding

System.Windows.Data Warning: 84 : BindingExpression (hash=58885068): TransferValue - implicit converter produced

above line says that it will try to convert the value received to the appropriate type i.e. 'System.Windows.Input.ICommand` in this case

System.Windows.Data Warning: 89 : BindingExpression (hash=58885068): TransferValue - using final value

above line says that it is using the final value as <null> which means that conversion to appropriate type is failed

analyzing above lines tells that the binding is correct to resolve the value however the value does not match the source type so it can't be used.

this forced me to look at the implementation of the command i.e. PasteCommand where i discovered that the implementation does not match the required interface.

pushpraj
  • 13,458
  • 3
  • 33
  • 50