Skip to content

Commit

Permalink
Add some commands to InternalStandardSetView (#277)
Browse files Browse the repository at this point in the history
* Add DataGridPasteCommand

* Add AutoFillCommand to InternalStandardSetViewModel

* Add copy, paste and autofill command to InternalStandardSetView.
  • Loading branch information
YukiMatsuzawa authored Dec 4, 2023
1 parent 00b0280 commit c6b4eea
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 90 deletions.
92 changes: 4 additions & 88 deletions src/Common/ChartDrawing/Behavior/DataGridPasteBehavior.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace CompMs.Graphics.Behavior
{
public class DataGridPasteBehavior
public static class DataGridPasteBehavior
{
public static bool GetEnable(DependencyObject obj)
{
Expand Down Expand Up @@ -38,91 +35,10 @@ private static void EnablePropertyChanged(DependencyObject d, DependencyProperty

private static void Datagrid_KeyDown(object sender, KeyEventArgs e)
{
if (!(sender is DataGrid datagrid)) return;

if (datagrid.SelectedCells.Count == 0) {
return;
}
var leftTopCell = datagrid.SelectedCells[0];
if (datagrid.SelectionUnit == DataGridSelectionUnit.FullRow) {
leftTopCell = datagrid.CurrentCell;
}
if (e.Key == Key.V && Keyboard.Modifiers == ModifierKeys.Control)
{
var clipText = Clipboard.GetText().Replace("\r\n", "\n").TrimEnd().Split('\n').Select(row => row.Split('\t')).ToArray();

var items = datagrid.Items.Cast<object>().ToList();
var startRow = items.IndexOf(leftTopCell.Item);
var startCol = leftTopCell.Column.DisplayIndex;
var errorRows = new PastingFailedRows();

for (int i = 0; i < clipText.Length; i++)
{
if (i + startRow >= items.Count) break;
var datas = clipText[i];
var item = items[i + startRow];

datagrid.BeginEdit();
for (int j = 0; j < datas.Length; j++)
{
if (j + startCol >= datagrid.Columns.Count) break;
var data = datas[j];
datagrid.Columns[j + startCol].OnPastingCellClipboardContent(item, data);
}
var row = datagrid.ItemContainerGenerator.ContainerFromItem(item);
if (Validation.GetHasError(row)) {
datagrid.CancelEdit(DataGridEditingUnit.Row);
errorRows.Add(i + 1, string.Join("\t", datas));
}
else {
datagrid.CommitEdit();
}
}

datagrid.CurrentCell = new DataGridCellInfo(datagrid.Items[startRow], datagrid.Columns[startCol]);
if (errorRows.HasErrors) {
MessageBox.Show(System.Windows.Window.GetWindow(datagrid), errorRows.ToString(), "Pasting error", MessageBoxButton.OK, MessageBoxImage.Error);
}
if (e.Key == Key.V && Keyboard.Modifiers == ModifierKeys.Control) {
CommonMVVM.DataGridPasteCommand.Instance.Execute(sender);
e.Handled = true;
}
}

sealed class PastingFailedRows {
private readonly List<PastingFailedRow> _rows;

public PastingFailedRows() {
_rows = new List<PastingFailedRow>();
Rows = _rows.AsReadOnly();
}

public ReadOnlyCollection<PastingFailedRow> Rows { get; }

public bool HasErrors => Rows.Any();

public void Add(int lineNumber, string content) {
_rows.Add(new PastingFailedRow(lineNumber, content));
}

public override string ToString() {
var errors = new[]
{
$"Falied to paste lines {string.Join(",", _rows.Select(row => row.LineNumber))}.",
"",
string.Join(System.Environment.NewLine, _rows.Select(row => $"Line {row.LineNumber}: {row.Content}")),
};
return string.Join(System.Environment.NewLine, errors);
}
}

sealed class PastingFailedRow {
public PastingFailedRow(int lineNumber, string content) {
LineNumber = lineNumber;
Content = content;
}

public int LineNumber { get; }
public string Content { get; }
}

}
}
107 changes: 107 additions & 0 deletions src/Common/CommonMVVM/Common/DataGridPasteCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace CompMs.CommonMVVM
{
public sealed class DataGridPasteCommand : ICommand
{
public static DataGridPasteCommand Instance { get; } = new DataGridPasteCommand();

public event EventHandler CanExecuteChanged;

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

public void Execute(object parameter) {
if (!(parameter is DataGrid datagrid)) {
return;
}

if (datagrid.SelectedCells.Count == 0) {
return;
}
var leftTopCell = datagrid.SelectedCells[0];
if (datagrid.SelectionUnit == DataGridSelectionUnit.FullRow) {
leftTopCell = datagrid.CurrentCell;
}
var clipText = Clipboard.GetText().Replace("\r\n", "\n").TrimEnd().Split('\n').Select(row => row.Split('\t')).ToArray();

var items = datagrid.Items.Cast<object>().ToList();
var startRow = items.IndexOf(leftTopCell.Item);
var startCol = leftTopCell.Column.DisplayIndex;
var errorRows = new PastingFailedRows();

for (int i = 0; i < clipText.Length; i++)
{
if (i + startRow >= items.Count) break;
var datas = clipText[i];
var item = items[i + startRow];

datagrid.BeginEdit();
for (int j = 0; j < datas.Length; j++)
{
if (j + startCol >= datagrid.Columns.Count) break;
var data = datas[j];
datagrid.Columns[j + startCol].OnPastingCellClipboardContent(item, data);
}
var row = datagrid.ItemContainerGenerator.ContainerFromItem(item);
if (Validation.GetHasError(row)) {
datagrid.CancelEdit(DataGridEditingUnit.Row);
errorRows.Add(i + 1, string.Join("\t", datas));
}
else {
datagrid.CommitEdit();
}
}

datagrid.CurrentCell = new DataGridCellInfo(datagrid.Items[startRow], datagrid.Columns[startCol]);
if (errorRows.HasErrors) {
MessageBox.Show(Window.GetWindow(datagrid), errorRows.ToString(), "Pasting error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}

sealed class PastingFailedRows {
private readonly List<PastingFailedRow> _rows;

public PastingFailedRows() {
_rows = new List<PastingFailedRow>();
Rows = _rows.AsReadOnly();
}

public ReadOnlyCollection<PastingFailedRow> Rows { get; }

public bool HasErrors => Rows.Any();

public void Add(int lineNumber, string content) {
_rows.Add(new PastingFailedRow(lineNumber, content));
}

public override string ToString() {
var errors = new[]
{
$"Falied to paste lines {string.Join(",", _rows.Select(row => row.LineNumber))}.",
"",
string.Join(Environment.NewLine, _rows.Select(row => $"Line {row.LineNumber}: {row.Content}")),
};
return string.Join(Environment.NewLine, errors);
}
}

sealed class PastingFailedRow {
public PastingFailedRow(int lineNumber, string content) {
LineNumber = lineNumber;
Content = content;
}

public int LineNumber { get; }
public string Content { get; }
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
xmlns:behavior="clr-namespace:CompMs.Graphics.Behavior;assembly=ChartDrawing"
xmlns:common="clr-namespace:CompMs.CommonMVVM;assembly=CommonMVVM"
xmlns:vm="clr-namespace:CompMs.App.Msdial.ViewModel.Statistics"
xmlns:local="clr-namespace:CompMs.App.Msdial.View.Statistics"
mc:Ignorable="d"
d:Background="White"
d:DataContext="{d:DesignInstance Type={x:Type vm:InternalStandardSetViewModel}}"
Expand All @@ -23,7 +24,6 @@
<Setter Property="AutoGenerateColumns" Value="False"/>
<Setter Property="SelectionUnit" Value="FullRow"/>
<Setter Property="behavior:DataGridCommitBehavior.Enable" Value="True"/>
<Setter Property="behavior:DataGridPasteBehavior.Enable" Value="True"/>
</Style>
<Style TargetType="DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
Expand All @@ -33,7 +33,25 @@
<DataGrid ItemsSource="{Binding Spots}" Grid.Row="0">
<DataGrid.Resources>
<common:BindingProxy x:Key="TargetMsMethod" Data="{Binding TargetMsMethod}"/>
<local:SelectedAndBellowRowsConverter x:Key="SelectedAndBellowRows"/>
</DataGrid.Resources>
<DataGrid.InputBindings>
<KeyBinding Key="V" Modifiers="Ctrl"
Command="{x:Static common:DataGridPasteCommand.Instance}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</DataGrid.InputBindings>
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Auto fill"
Command="{Binding Path=PlacementTarget.(DataGrid.DataContext).(vm:InternalStandardSetViewModel.AutoFillCommand), RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource SelectedAndBellowRows}}"/>
<MenuItem Header="Copy" Command="Copy"/>
<MenuItem Header="Paste"
Command="{x:Static common:DataGridPasteCommand.Instance}"
CommandParameter="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
InputGestureText="Ctrl+V"/>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Columns>
<DataGridTextColumn Header="Alignment ID"
Binding="{Binding Id}"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using System.Windows;
using CompMs.App.Msdial.ViewModel.Statistics;
using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace CompMs.App.Msdial.View.Statistics
{
Expand Down Expand Up @@ -28,4 +33,22 @@ private void CancelClose(object sender, RoutedEventArgs e) {
window.Close();
}
}

internal sealed class SelectedAndBellowRowsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
var datagrid = (DataGrid)value;
if (datagrid is null) {
return default;
}
var current = (NormalizationSpotPropertyViewModel)datagrid.CurrentItem;
var currentIndex = datagrid.Items.IndexOf(current);
var targets = datagrid.Items.Cast<NormalizationSpotPropertyViewModel>().Skip(currentIndex);
return (current, targets);
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return Binding.DoNothing;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Reactive.Bindings.Extensions;
using Reactive.Bindings.Notifiers;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive;
using System.Reactive.Linq;
Expand All @@ -30,6 +31,13 @@ public InternalStandardSetViewModel(InternalStandardSetModel model) {

Spots.ObserveElementPropertyChanged().Subscribe(_ => isEditting.TurnOn()).AddTo(Disposables);
commit.Subscribe(_ => isEditting.TurnOff()).AddTo(Disposables);

AutoFillCommand = new ReactiveCommand<(NormalizationSpotPropertyViewModel, IEnumerable<NormalizationSpotPropertyViewModel>)>().AddTo(Disposables);
AutoFillCommand.Subscribe(args => {
foreach (var target in args.targets) {
target.InternalStandardId = args.selected.InternalStandardId;
}
}).AddTo(Disposables);
}

public ReadOnlyObservableCollection<NormalizationSpotPropertyViewModel> Spots { get; }
Expand All @@ -38,6 +46,8 @@ public InternalStandardSetViewModel(InternalStandardSetModel model) {

public BooleanNotifier IsEditting { get; }

public ReactiveCommand<(NormalizationSpotPropertyViewModel selected, IEnumerable<NormalizationSpotPropertyViewModel> targets)> AutoFillCommand { get; }

public ReactiveCommand ApplyChangeCommand => _applyChangeCommand;
public override ICommand FinishCommand => _applyChangeCommand;
public override ICommand CancelCommand => _cancelCommand;
Expand Down

0 comments on commit c6b4eea

Please sign in to comment.