2015-04-14 27 views
6

Muszę zaimplementować niestandardową kopię + wyciąć + wkleić dane (nie tekst lub CSV), które mają być kopiowane między siatkami w aplikacji WPF. Używanie standardowych poleceń ApplicationCommands i definiowanie CommandBinding działa naprawdę dobrze, ale tylko wtedy, gdy DataGrid zawiera co najmniej 1 wiersz danych i kiedy jest wybrany. Gdy nie ma żadnych wierszy lub fokus nie znajduje się na żadnym z nich, wszystkie polecenia są wyłączone.Zaimplementuj niestandardowe kopiuj i wklej w WPF DataGrid, które działa, gdy nie ma w nim wierszy.

Aby rozwiązać problem, próbowałem wywołać CommandManager.InvalidateRequerySuggested() i ustawić Focusable = true i/lub FocusManager.IsFocusScope = true na DataGrid, ale wydaje się, że wewnętrznie DataGrid jako całość nie jest "zainteresowany" obsługą Kopiuj/Wklej operacje, tylko jego wiersze ponawiają zapytania do poleceń CanExecute i wywołują odpowiednio Execute. Ignoruje także KeyBindings.

Jak sprawić, aby uchwyt DataGrid wymagał obsługi poleceń aplikacji?

Proszę znaleźć przykład, na którym testowałem poniższego problemu:

<Window x:Class="WpfApplication1.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"> 
    <DataGrid x:Name="TheGrid"> 
     <DataGrid.Columns> 
      <DataGridTextColumn Header="Number" Binding="{Binding}"/> 
     </DataGrid.Columns> 
     <DataGrid.InputBindings> 
      <KeyBinding Key="A" Command="{x:Static ApplicationCommands.New}"/> 
     </DataGrid.InputBindings> 
     <DataGrid.CommandBindings> 
      <CommandBinding Command="{x:Static ApplicationCommands.Paste}" CanExecute="CanPaste" Executed="Paste"/> 
      <CommandBinding Command="{x:Static ApplicationCommands.Copy}" CanExecute="CanCopy" Executed="Copy"/> 
      <CommandBinding Command="{x:Static ApplicationCommands.New}" CanExecute="CanAddNew" Executed="AddNew"/> 
     </DataGrid.CommandBindings> 
     <DataGrid.ContextMenu> 
      <ContextMenu> 
       <MenuItem Command="{x:Static ApplicationCommands.Copy}" Header="Copy"/> 
       <MenuItem Command="{x:Static ApplicationCommands.Paste}" Header="Paste"/> 
       <MenuItem Command="{x:Static ApplicationCommands.New}" Header="New row"/> 
      </ContextMenu> 
     </DataGrid.ContextMenu> 
    </DataGrid> 
</Window> 

i kodeksem tyle:

using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Windows; 
using System.Windows.Input; 

namespace WpfApplication1 
{ 
    public partial class MainWindow 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      TheGrid.ItemsSource = numbers; 
      // Following line enables commands when row is selected 
      numbers.Add(0); 
     } 

     private void Copy(object sender, ExecutedRoutedEventArgs e) 
     { 
      Clipboard.SetData(DataFormats.Text, string.Join(",", numbers)); 
     } 

     private void CanCopy(object sender, CanExecuteRoutedEventArgs e) 
     { 
      e.CanExecute = numbers.Count > 0; 
     } 

     private void CanPaste(object sender, CanExecuteRoutedEventArgs e) 
     { 
      e.CanExecute = numbers.Count > 0; 
      e.Handled = true; 
     } 

     private void Paste(object sender, ExecutedRoutedEventArgs e) 
     { 
      Close(); 
     } 

     private void CanAddNew(object sender, CanExecuteRoutedEventArgs e) 
     { 
      e.CanExecute = true; 
      e.Handled = true; 
     } 

     private void AddNew(object sender, ExecutedRoutedEventArgs e) 
     { 
      numbers.Add(numbers.Count); 
     } 

     private readonly ICollection<int> numbers = new ObservableCollection<int>(); 
    } 
} 

Edycja

Kod jedynym rozwiązaniem przez ajjappan Subramanian jest najbardziej pasuje do aplikacji, w której będzie używany. Ostatecznie, ponieważ już dziedziczyłem siatkę, ponieważ ma ona niestandardowego policjanta Format pasta y + do pracy, dodałem trochę kodu, który zapewnia, że ​​koncentruje się w obrębie siatki w 3 przypadkach: Menu

  1. Kontekst jest pokazany
  2. użytkownik kliknie na (pusty) obszaru siatki, kiedy to wizualizacje potomnych nie koncentrują się na tym, co się stało:
  3. (Nasz przypadek specyficzny dla aplikacji) Użytkownik klika na siatkę nawigacji TreeView, która następnie ustawia ostrość na siatkę, aby skróty działały od razu.

odpowiedni kod:

public class MyDataGrid: DataGrid 
{ 
     protected override void OnContextMenuOpening(ContextMenuEventArgs e) 
     { 
      base.OnContextMenuOpening(e); 
      Focus(); 
     } 

     protected override void OnMouseUp(MouseButtonEventArgs e) 
     { 
      base.OnMouseUp(e); 
      if(e.ChangedButton == MouseButton.Left && !IsKeyboardFocusWithin) 
      { 
       Focus(); 
      } 
     } 
} 
+0

myślę, że może to być spowodowane linii -> ** e.CanExecute = numbers.Count> 0; ** spróbuj zmienić linię w funkcji CanPaste do ** e.CanExecute = true; ** –

+0

Przepraszam, ale nie włączono żadnej akcji bez skupienia się na wierszu, w tym ApplicationCommands.New i ma on ustawioną wartość e.CanExecute zawsze na true. – too

Odpowiedz

5

Gdy ContextMenu jest openning następnie można ustawić ostrość na siatce, która umożliwi wszystkie pozycje menu. Dobre wyjaśnienie podano tu http://www.wpftutorial.net/RoutedCommandsInContextMenu.html

Również za realizację pasta odnoszą SO zakładać WPF datagrid pasting

WPF:

<DataGrid x:Name="TheGrid" CanUserAddRows="True" 
      ContextMenuOpening="TheGrid_ContextMenuOpening"> 
    <DataGrid.Columns> 
     <DataGridTextColumn Header="Number" Binding="{Binding}"/> 
    </DataGrid.Columns> 
    <DataGrid.InputBindings> 
     <KeyBinding Key="A" Command="{x:Static ApplicationCommands.New}"/> 
    </DataGrid.InputBindings> 
    <DataGrid.CommandBindings>     
     <CommandBinding Command="{x:Static ApplicationCommands.Paste}" 
         CanExecute="CanPaste" Executed="Paste"/> 
     <CommandBinding Command="{x:Static ApplicationCommands.Copy}" 
         CanExecute="CanCopy" Executed="Copy"/> 
     <CommandBinding Command="{x:Static ApplicationCommands.New}" 
         CanExecute="CanAddNew" Executed="AddNew"/> 
    </DataGrid.CommandBindings> 
    <DataGrid.ContextMenu> 
     <ContextMenu>     
      <MenuItem Command="{x:Static ApplicationCommands.Copy}" Header="Copy"/> 
      <MenuItem Command="{x:Static ApplicationCommands.Paste}" Header="Paste"/> 
      <MenuItem Command="{x:Static ApplicationCommands.New}" Header="New row"/> 
     </ContextMenu> 
    </DataGrid.ContextMenu> 
</DataGrid> 

kod C#:

public partial class Window1 : Window 
{ 
    public Window1() 
    { 
     InitializeComponent(); 
     TheGrid.ItemsSource = numbers; 
    } 

    private void Copy(object sender, ExecutedRoutedEventArgs e) 
    { 
     Clipboard.SetData(DataFormats.Text, string.Join(",", numbers)); 
    } 

    private void CanCopy(object sender, CanExecuteRoutedEventArgs e) 
    { 
     e.CanExecute = true; 
    } 

    private void CanPaste(object sender, CanExecuteRoutedEventArgs e) 
    { 
     e.CanExecute = true; 
     e.Handled = true; 
    } 

    private void Paste(object sender, ExecutedRoutedEventArgs e) 
    { 
     Close(); 
    } 

    private void CanAddNew(object sender, CanExecuteRoutedEventArgs e) 
    { 
     e.CanExecute = true; 
     e.Handled = true; 
    } 

    private void AddNew(object sender, ExecutedRoutedEventArgs e) 
    { 
     numbers.Add(numbers.Count); 
    } 

    private readonly ICollection<int> numbers = new ObservableCollection<int>(); 

    private void TheGrid_ContextMenuOpening(object sender, ContextMenuEventArgs e) 
    { 
     TheGrid.Focus(); 
    } 

} 
+0

Sprawdź ten link w celu wklejenia http://stackoverflow.com/questions/4118617/wpf-datagrid-pasting –

+1

Twoje rozwiązanie ma największy sens, biorąc pod uwagę, że menu kontekstowe kradnie fokus z siatki i ponownie je zdobywa za każdym razem, gdy otwierane jest menu kontekstowe rozwiąże problem prawdopodobnie w każdym przypadku i zrobił to za mnie. Dziękuję Ci. – too

0

Oto moja wersja VB.NET. Pobiera wartość, a następnie wypełnia ją do wszystkich wybranych komórek w datagrid.

Private Sub gridpaste(ByVal pasteValue As String) 

    Dim rowInd As Integer = Nothing 
    Dim colind As Integer = Nothing 
    Dim add As Integer = 1 

    For Each c As DataGridCellInfo In LineListDataGrid.SelectedCells 

     colind = c.Column.DisplayIndex 

     rowInd = GetRowIndex(LineListDataGrid, c) 

     Try 
      LLDB.LineList.Rows(rowInd)(colind) = pasteValue 
     Catch err As Exception 
      MessageBox.Show(err.Message) 
     End Try 
    Next 

End Sub 

    Public Shared Function GetRowIndex(dataGrid As DataGrid, dataGridCellInfo As DataGridCellInfo) As Integer 
    Dim dgrow As DataGridRow = DirectCast(dataGrid.ItemContainerGenerator.ContainerFromItem(dataGridCellInfo.Item), DataGridRow) 
    If dgrow IsNot Nothing Then 
     Return dgrow.GetIndex() 
    Else 
     Return -1 
    End If 

End Function