1
votes

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>
1
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
Thanks for the speedy response. I assumed the <Label> inside the control template would inherit the DataContext of its parent because its in the same visual tree? If I use WPF inspector. The Datacontext of that label is =this.Asheh
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
But you changed the datacontext of the parenting MenuItems so they don't know the ment datacontext.JPOne
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.ICommandpushpraj

1 Answers

1
votes

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.