본문 바로가기
Spread for .NET

필터된 SheetView에서 클립보드 복사하기

by 하_늘_바_람 2022. 6. 2.

스프레드에서 지원하는 방법

FarPoint.Win.Spread.InputMap im = 
    sprEmpCalculte.GetInputMap(FarPoint.Win.Spread.InputMapMode.WhenFocused);
im.Put(new FarPoint.Win.Spread.Keystroke(Keys.C, Keys.Control), 
    FarPoint.Win.Spread.SpreadActions.ClipboardCopyAsStringSkipHidden); // 핵심은 이 부분


수동으로 처리하는 방법

KeyDown 이벤트 처리

private void sprPurchaseDetail_KeyDown(object sender, KeyEventArgs e) {
    // Ctrl + C
    if (e.Control && e.KeyCode == Keys.C) {
    	e.Handled = true;
        // 수기로 클립보드에 복사처리를 하겠다.
        (sender as FpSpread).AutoClipboard = false;
        // 현재 선택된 영역을 클립보드에 넣기
        (sender as FpSpread).ActiveSheet.ClipboardCopy(ClipboardCopyOptions.All);
        SheetUtil.CopyFilteredRow(sender as FpSpread);
        // 다시 원래대로 되돌리기
        (sender as FpSpread).AutoClipboard = true;
    }
}

상단의 CopyFilterRow의 풀 소스 코드

public static void CopyFilteredRow(FpSpread sender) {
    try {
        // 클립보드에 들어있는 텍스트 값만 가져오기
        string data = Clipboard.GetData(DataFormats.Text).ToString();
        // 임시로 문자열을 저장할 공간
        StringBuilder copy = new StringBuilder();

        // ----------------------------------------------------------------------------------
        // 컬럼에 \r\n이 들어 있을 경우 행 단위로 잘라내는 것이 어려움.
        // 아래의 로직은 컬럼에 \r\n이 있을 때  [ "컬러\r\n명" ] 형태로 저장되는 패턴을 파악해서
        // 컬럼 헤더의 값만 포함해서 행단위로 분할을 하고 \r\n을 임시로 | 로 변경을 한다.
        // 마지막에 | 값은 다시 \r\n(Environment.NewLine)으로 변경함.
        int next = 0;
        int prev = 0;
        while (data.Length > next) {
            next = data.IndexOf('"', prev);
            if (next == -1) {
                copy.Append(data.Substring(prev));
                break;
            }

            copy.Append(data.Substring(prev, next - prev + 1));

            prev = next + 1;
            next = data.IndexOf('"', prev);

            copy.Append(data.Substring(prev, next - prev + 1).Replace(Environment.NewLine, "|"));

            prev = next + 1;
        }
        // 여기까지 행단위 분할 로직 처리를 위한 사전 처리
        // ------------------------------------------------------------------------------------

        // 행단위로 분할 해서 스트링 문자열에 값을 담아둔다.
        List<string> selRows = new List<string>();
        string[] rows = copy.ToString().Replace("\r", "").Split("\n".ToCharArray());
        copy.Clear();

        // 컬럼 헤더의 행 갯수를 가지고 온다.
        int columnHeaderRow = 1;
        foreach (var item in Clipboard.GetDataObject().GetFormats()) {
            if (item.Contains("CellInfoRange")) {
                if ((Clipboard.GetData(item) as CellInfoRange) != null &&
                    (Clipboard.GetData(item) as CellInfoRange).ColumnHeader != null) {
                    columnHeaderRow = (Clipboard.GetData(item) as CellInfoRange).ColumnHeader.RowCount;
                }
            }
        }

        // 선택한 영역을 가지고 온다.
        CellRange[] range = sender.ActiveSheet.GetSelections();

        // 필터되고 나서 화면에 보여주는 행의 인덱스만 담아 준다.
        // 필터가 없으면 null을 리턴한다.
        int[] selRowIndex = sender.ActiveSheet.RowFilter.GetIntersectedFilteredInRows();
        if (selRowIndex == null) {
            // 강제로 전체 행의 인덱스를 만들어 준다.
            selRowIndex = Enumerable.Range(0, (sender as FpSpread).ActiveSheet.Rows.Count - 1).ToArray();
        }

        if (range != null && range.Length > 0) {
            // 컬럼의 헤더를 클릭해서 선택한 경우
            if ((range[0].Row < 0 && range[0].RowCount < 0 && range[0].Column != -1) ||
                (range[0].Row < 0 && range[0].RowCount < 0 && range[0].Column < 0 &&
                 range[0].ColumnCount < 0)) {
                // 컬럼(열)만 선택한 경우 - 컬럼을 선택했다. 
                // 해당 열의 헤더는 행의 인덱스에서 제외하고 위치를 계산해야 한다.
                for (int i = 0; i < rows.Length; i++) {
                    if (i < columnHeaderRow || selRowIndex.Contains(i - columnHeaderRow)) {
                        selRows.Add(rows[i]);
                    }
                }
            }
            else if (range[0].Column < 0 && range[0].ColumnCount < 0 && range[0].Row != -1) {
                // 행만 선택한 경우 range의 시작점(range[0].Row)를 
                // 선택한 인덱스를 찾기 위한 값에
                // 플러스 시켜준다.
                for (int i = 0; i < rows.Length; i++) {
                    if (selRowIndex.Contains(i + range[0].Row)) {
                        selRows.Add(rows[i]);
                    }
                }
            }
            else {
                // 스프레드 내부의 값만 선택한 경우
                for (int i = 0; i < rows.Length; i++) {
                    if (selRowIndex.Contains(i + range[0].Row)) {
                        selRows.Add(rows[i]);
                    }
                }
            }
        }

        Clipboard.Clear();
        Clipboard.SetText(string.Join(Environment.NewLine, selRows).Replace("|", Environment.NewLine));
    }
    catch (Exception ex) {
        Console.WriteLine(ex);
    }
}