0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C#+Excel:選択したオートシェイプの文字列編集能力を奪い取る

Last updated at Posted at 2019-12-15

オートシェイプのテキスト絡みで
・Excel2016で、矢印キーでIカーソルが見かけ上動かない問題
・Excel2013で、文字列表示とIカーソル位置が横方向に少しずれる問題
が、くそウザかったので、暫定対策すべくツールをつくってみた。1
(最新版のupdateあてれば治るのかもしれないが・・)

画面キャプチャ

image.png

使い方

  1. Get AutoShapeボタンを押す
  2. Excel上で文字列を含むオートシェイプをクリックして選択する
  3. Stop/Fixボタンを押す
  4. テキストを編集すると、Excel上のオートシェイプのテキストも連動して変化する

注意点

  • Excelのオートシェイプ上の文字列が、部分ごとに書式が違う場合、書式情報が抹消されます。
  • 編集対象のオートシェイプをもつExcelのシート閉じたりオートシェイプを消した状態でテキストを編集すると例外で落ちるはず。
  • Excel上でも編集できてしまい、アプリには反映されない。(タイトル詐欺)

コンパイル方法

Windows10 Excel2016環境
(Excelがインストールされてれば、環境違ってもC:\Windows\assembly\GAC_MSIL\以下をdir /s なんちゃら.dllで探せば出てくるはず。)


csc ^
 /r:C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.Excel\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Excel.dll ^
 /r:C:\Windows\assembly\GAC_MSIL\office\15.0.0.0__71e9bce111e9429c\Office.dll ^
 %*

C#ソースコード


using System;
using System.Drawing;
//using System.Collections.Generic;
//using System.Reflection;
using System.Runtime.CompilerServices; // to use [MethodImpl(MethodImplOptions.NoInlining)]
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows.Forms;

using Excel = Microsoft.Office.Interop.Excel;
//using Microsoft.Office.Core;

class ExcelAutoshapeTest : Form
{
    System.Windows.Forms.Timer timer;
    TextBox txtName;
    TextBox txtContent;
    Button btnStartTryGet;
    dynamic _oShape;
    bool _updatedByProgram;

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void DumpTextOfActiveSheet()
    {
        var oExcelApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application");

        var oSheet = (Excel.Worksheet)oExcelApp.ActiveWorkbook.ActiveSheet;

        var oShapes = (Excel.Shapes)oSheet.Shapes;
        foreach ( Excel.Shape oShape in oShapes ) {
            Console.WriteLine(oShape.Type);
            if (oShape.Type == Microsoft.Office.Core.MsoShapeType.msoAutoShape) {
                Console.WriteLine(oShape.AutoShapeType);
                try {
                    dynamic oTextFrame = oShape.TextFrame;
                    Console.WriteLine(oTextFrame.Characters.Text);
                }
                catch(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) {
                    //Console.WriteLine(e);
                }
                catch(COMException) {
                    //Console.WriteLine(e);
                }
            }
            //oTextFrame.Characters.Text = "bbb";
        }
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    void TryGetActiveAutoShape()
    {
        var oExcelApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
        
        dynamic t = oExcelApp.Selection;

        try {
            _updatedByProgram = true;
            _oShape = null;
            txtContent.Text = Regex.Replace(t.Characters.Text, @"\n", "\r\n", RegexOptions.Multiline);
            txtName.Text = t.Name;
            _oShape = t;
        }
        catch(Exception e) {
            if (e is Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ||
                e is COMException ) {  // Note: C#6以降なら when を使ってもう少しきれいに書ける
                
                _oShape = null;
                txtName.Text = "";
                txtContent.Text = "";
                // もみ消す
                return;
            }
            throw e;
        }
        finally {
            _updatedByProgram = false;
        }
    }

    void UpdateExcelAutoShape()
    {
        if ( _oShape != null ) {
            string s = Regex.Replace(txtContent.Text, @"\r\n", "\n", RegexOptions.Multiline);
            _oShape.Characters.Text = s;
        }
    }


    ExcelAutoshapeTest()
    {
        _oShape = null;

        Controls.Add(txtName = new TextBox(){
            Location = new Point(0,0),
            Width = 150,
            ReadOnly = true
        });


        Controls.Add(btnStartTryGet = new Button(){
            Text = "Get AutoShape",
            Location = new Point(180, 0),
            Width = 100
        });
        btnStartTryGet.Click += (s,e)=>{
            if (timer.Enabled) {
                ((Button)s).Text="Get AutoShape";
                txtContent.ReadOnly = false;
                timer.Stop();
            }
            else {
                ((Button)s).Text="Stop/Fix";
                txtContent.ReadOnly = true;
                timer.Start();
            }
        };
        
        
        Controls.Add(txtContent = new TextBox(){
            Location = new Point(0,30),
            ScrollBars = ScrollBars.Both,
            Multiline = true,
            WordWrap = false
        });
        txtContent.TextChanged += (s,e)=>{if(!_updatedByProgram){UpdateExcelAutoShape();}};


        Load      += (s,e)=>{MyResize();};
        Resize    += (s,e)=>{MyResize();};
        ResizeEnd += (s,e)=>{MyResize();};

        timer = new System.Windows.Forms.Timer();
        timer.Interval = 1000;
        timer.Tick += (s,e)=>{
            TryGetActiveAutoShape();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        };
    }

    void MyResize()
    {
        int w = ClientSize.Width;
        int h = ClientSize.Height - txtContent.Top;
        if(w<50){w=50;}
        if(h<50){h=50;}
        txtContent.Size = new Size(w,h);
    }
    
    [STAThread]
    static void Main(string[] args)
    {
        //DumpTextOfActiveSheet();
        Application.Run(new ExcelAutoshapeTest());
    }
}

参考サイト

  1. そもそもExcelは表計算ツールなのでオートシェイプ使う事自体がおかしなことかもしれない

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?