1

I've done ContextMenus before but for a DataGrid they seem to be far too complex. I just want to copy the content of the cell into the clipboard. How can I get this working? Seems like there are many alternatives, but no set guaranteed method.

<DataGrid ItemsSource="{Binding MyStuff}">
      <DataGridTextColumn Binding="{Binding MyContentIWantToCopy}">
          <DataGridTextColumn.CellStyle>
              <Setter Property="ContextMenu">
                                <Setter.Value>
                                    <ContextMenu>
                                        <MenuItem Command="ApplicationCommands.Copy"/>
                                    </ContextMenu>
                                </Setter.Value>
                            </Setter>
          <DataGridTextColumn.CellStyle>
      </DataGridTextColumn>
</DataGrid>

Ideally this would be the simplest way. But of course, it does not work.

However, I've also tried Commands -

Paste from Excel to WPF DataGrid

MVVM binding command to contextmenu item

Create contextmenus for datagrid rows

None of these work. Typically the result is a context menu that appears and either a greyed out button or one that doesn't do anything, never triggers the command. I just want to copy the text inside the Datagrid cell into the clipboard. I've done this on all kinds of objects before in similar manner - even Listviews - but I can't find any solution for DataGrid and DataGridTextColumns.

Community
  • 1
  • 1
user99999991
  • 1,351
  • 3
  • 19
  • 43

2 Answers2

2

Your xaml code at start of message - contains errors. But even after fix it seems to takes and copy entire row instead one cell.

One of the straightforward variants - just write own command, for exapmle:

public class MyCopyCommand : ICommand
{
    public string Name { get { return "Copy"; } }

    public void Execute(object parameter)
    {
        Clipboard.SetText(parameter != null ? parameter.ToString() : "<null>");
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;
}

This command thinks that parameter already contain required string to copy. But you can customize it's logic as you need, for example - take entire DataGridCell and decide - what to copy from it. Second step - using our command in XAML, for example through Resources:

<Window 
x:Class="WpfApplication65.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"

xmlns:app="clr-namespace:WpfApplication65"
>
<Window.Resources>
    <app:MyCopyCommand x:Key="CopyCommand"/>
</Window.Resources>
<Grid>
    <DataGrid x:Name="MyDataGrid">
        <DataGrid.CellStyle>
            <Style TargetType="{x:Type DataGridCell}">
                <Setter Property="ContextMenu">
                    <Setter.Value>
                        <ContextMenu>
                            <MenuItem 
                                Command="{StaticResource CopyCommand}" 
                                CommandParameter="{Binding Path=Parent.PlacementTarget.Content.Text, RelativeSource={RelativeSource Self}}"
                                Header="{Binding Name, Source={StaticResource CopyCommand}}"
                                />
                        </ContextMenu>
                    </Setter.Value>
                </Setter>
            </Style>
        </DataGrid.CellStyle>
    </DataGrid>
</Grid>

Where "WpfApplication65" is just my project name, you must insert yours of course. Here i wrote CommandParameter binding path as it is, becouse i know, that DataGrid will automatically genereate DataGridCell with TextBlock inside it's Content property. If you want generic solution - bind CommandParameter to Parent.PlacementTarget and you will get DataGridCell instance to your MyCopyCommand.Execute method, and then, using it and it's Column property - take from it what you want.

And to make our DataGrid working - add to MainWindow class this code:

public class MyData
{
    public string Name { get; set; }
    public int Count { get; set; }
    public MyData(string name, int count)
    {
        Name = name;
        Count = count;
    }
}

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

        var data = new List<MyData>
            {
                new MyData("one", 10), 
                new MyData("two", 100500), 
                new MyData("three", 777)
            };
        MyDataGrid.ItemsSource = data;
    }
}

Hope this helps, if something will not working - ask me.

dzaraev
  • 125
  • 7
0

XAML:

<DataGrid x:Name="MyDataGrid">
  <DataGridTextColumn Binding="{Binding MyContentIWantToCopy}">
    <DataGrid.CellStyle>
      <Style TargetType="{x:Type DataGridCell}">
        <Setter Property="ContextMenu">
          <Setter.Value>
            <ContextMenu>
              <MenuItem Header="Copy" Click="MenuItem_OnClick"/>
            </ContextMenu>
          </Setter.Value>
        </Setter>
      </Style>
    </DataGrid.CelStyle>
  </DataGridTextColumn>
</DataGrid>

Code-behind:

void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
  ApplicationCommands.Copy.Execute(null, MyDataGrid);
}

I think you can also use the attribute CommandTarget in your XAML, instead of using code-behind.

Maxence
  • 12,868
  • 5
  • 57
  • 69