ContextMenu Item Command MVVM

BigH61 581 Reputation points
2022-02-16T12:45:38.07+00:00

I feel as though I am rally not getting WPF and particularly MVVM. Every time I feel I have got my head around it something comes along to set me back.
I am currently trying to move my code from code behind and have the RelayCommand for the New Connection but cannot get ContextMenu to work.
The full original code is below:

<Window.Resources>
        <ContextMenu x:Key="contextMenu">
            <MenuItem Header="View"
                      Click="View_OnClick" 
            CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}"/>
            <MenuItem Header="Edit"
                      Click="Edit_OnClick"
            CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}" />
            <MenuItem Header="Delete"
                      Click="Delete_OnClick"
            CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Parent}"/>
        </ContextMenu>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="318*" />
        </Grid.ColumnDefinitions>
        <ScrollViewer VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled">
            <StackPanel Name="listConnections" Grid.Column="0" Background="#4682b4" Margin="0,0,0,-0.2" >
                <Button Click="BtnAdd_OnClick">New Connection</Button>
            </StackPanel>
        </ScrollViewer>
    </Grid>

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

    private static Label FindClickedItem(object sender)
    {
        var mi = sender as MenuItem;
        if (mi == null)
        {
            return null;
        }

        var cm = mi.CommandParameter as ContextMenu;
        if (cm == null)
        {
            return null;
        }
        return cm.PlacementTarget as Label;
    }

    private void BtnAdd_OnClick(object sender, RoutedEventArgs e)
    {
        listConnections.Children.Add(new Label
        {
            Content = "New Connection",
            ContextMenu = (ContextMenu)Resources["contextMenu"]
        });
    }

    private void View_OnClick(object sender, RoutedEventArgs e)
    {
        var clickedItem = FindClickedItem(sender);
        if (clickedItem != null)
        {
            MessageBox.Show(" Viewing: " + clickedItem.Content);
        }
    }

    private void Edit_OnClick(object sender, RoutedEventArgs e)
    {
        var clickedItem = FindClickedItem(sender);
        if (clickedItem != null)
        {
            string newName = "Connection edited on " + DateTime.Now.ToLongTimeString();
            string oldName = Convert.ToString(clickedItem.Content);
            clickedItem.Content = newName;
            MessageBox.Show(string.Format("Changed name from '{0}' to '{1}'", oldName, newName));
        }
    }

    private void Delete_OnClick(object sender, RoutedEventArgs e)
    {
        var clickedItem = FindClickedItem(sender);
        if (clickedItem != null)
        {
            string oldName = Convert.ToString(clickedItem.Content);
            listConnections.Children.Remove(clickedItem);
            MessageBox.Show(string.Format("Removed '{0}'", oldName));
        }
    }

Any assistance much appreciated.

Developer technologies Windows Presentation Foundation
0 comments No comments
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2022-02-16T14:14:47.577+00:00

    Hi,
    try following demo with your code sequences:

    XAML:

    <Window x:Class="WpfApp1.Window106"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp106"
            mc:Ignorable="d"
            Title="Window106" Height="450" Width="800">
      <Window.Resources>
        <local:ViewModel x:Key="vm"/>
        <ContextMenu x:Key="contextMenu">
          <MenuItem Header="View"
                    Command="{Binding View_OnClick, Source={StaticResource vm}}" 
                    CommandParameter="{Binding}"/>
          <MenuItem Header="Edit"
                    Command="{Binding Edit_OnClick, Source={StaticResource vm}}" 
                    CommandParameter="{Binding}" />
          <MenuItem Header="Delete"
                    Command="{Binding Delete_OnClick, Source={StaticResource vm}}" 
                    CommandParameter="{Binding}"/>
        </ContextMenu>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="200"/>
          <ColumnDefinition Width="318*" />
        </Grid.ColumnDefinitions>
        <ScrollViewer VerticalScrollBarVisibility="Visible" 
                      HorizontalScrollBarVisibility="Disabled">
          <StackPanel Grid.Column="0" 
                      Background="#4682b4" 
                      Margin="0,0,0,-0.2" >
            <Button Content="New Connection"
                    Command="{Binding BtnAdd_OnClick}"
                    CommandParameter="{StaticResource contextMenu}"/>
            <ListBox ItemsSource="{Binding View}"/>
          </StackPanel>
        </ScrollViewer>
      </Grid>
    </Window>
    

    and code:

    using System;
    using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    
    namespace WpfApp106
    {
      public class ViewModel
      {
        public ObservableCollection<Label> View { get; } = new ObservableCollection<Label>();
        public ICommand BtnAdd_OnClick
        {
          get => new RelayCommand((state) =>
          {
            View.Add(new Label
            {
              Content = "New Connection",
              ContextMenu = (ContextMenu)state
            });
          });
        }
        public ICommand View_OnClick
        {
          get => new RelayCommand((state) =>
          {
            var clickedItem = state as Label;
            if (clickedItem != null)
            {
              MessageBox.Show(" Viewing: " + clickedItem.Content);
            }
          });
        }
        public ICommand Edit_OnClick
        {
          get => new RelayCommand((state) =>
          {
            var clickedItem = state as Label;
            if (clickedItem != null)
            {
              string newName = "Connection edited on " + DateTime.Now.ToLongTimeString();
              string oldName = Convert.ToString(clickedItem.Content);
              clickedItem.Content = newName;
              MessageBox.Show(string.Format("Changed name from '{0}' to '{1}'", oldName, newName));
            }
          });
        }
        public ICommand Delete_OnClick
        {
          get => new RelayCommand((state) =>
          {
            var clickedItem = state as Label;
            if (clickedItem != null)
            {
              string oldName = Convert.ToString(clickedItem.Content);
              View.Remove(clickedItem);
              MessageBox.Show(string.Format("Removed '{0}'", oldName));
            }
          });
        }
      }
    
      public class RelayCommand : ICommand
      {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _action;
        public RelayCommand(Action<object> action) { _action = action; _canExecute = null; }
        public RelayCommand(Action<object> action, Predicate<object> canExecute) { _action = action; _canExecute = canExecute; }
        public void Execute(object o) => _action(o);
        public bool CanExecute(object o) => _canExecute == null ? true : _canExecute(o);
        public event EventHandler CanExecuteChanged
        {
          add { CommandManager.RequerySuggested += value; }
          remove { CommandManager.RequerySuggested -= value; }
        }
      }
    }
    
    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2022-02-16T16:51:40.987+00:00

    Hi,
    replace ViewModel in my present demo:

      public class ViewModel
      {
        public ObservableCollection<Label> View { get; } = new ObservableCollection<Label>();
    
        public ICommand BtnAdd_OnClick { get => new RelayCommand(ExecAdd); }
        public ICommand View_OnClick { get => new RelayCommand(ExecView); }
        public ICommand Edit_OnClick { get => new RelayCommand(ExecEdit); }
        public ICommand Delete_OnClick { get => new RelayCommand(ExecDelete); }
    
        private void ExecAdd(object obj)
        {
          View.Add(new Label
          {
            Content = "New Connection",
            ContextMenu = (ContextMenu)obj
          });
        }
    
        private void ExecView(object obj)
        {
          var clickedItem = obj as Label;
          if (clickedItem != null) MessageBox.Show(" Viewing: " + clickedItem.Content);
        }
    
        private void ExecEdit(object obj)
        {
          var clickedItem = obj as Label;
          if (clickedItem != null)
          {
            string newName = "Connection edited on " + DateTime.Now.ToLongTimeString();
            string oldName = Convert.ToString(clickedItem.Content);
            clickedItem.Content = newName;
            MessageBox.Show(string.Format("Changed name from '{0}' to '{1}'", oldName, newName));
          }
        }
    
        private void ExecDelete(object obj)
        {
          var clickedItem = obj as Label;
          if (clickedItem != null)
          {
            string oldName = Convert.ToString(clickedItem.Content);
            View.Remove(clickedItem);
            MessageBox.Show(string.Format("Removed '{0}'", oldName));
          }
        }
      }
    
    1 person found this answer helpful.

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.