Monoと.NET Framework環境で動くメモ帳をC#で作る。

コンピュータ

Windowsのメモ帳を目指しました。

ソース

using System.Diagnostics;
using System;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Collections.Generic;

/*
コンパイル
mcs Form1.cs -r:System.Windows.Forms.dll -r:System.Drawing.dll -out:MyNotepad.exe
コンパイル(デバッグあり)
mcs Form1.cs -d:DEBUG -r:System.Windows.Forms.dll -r:System.Drawing.dll -out:MyNotepad.exe
実行
mono MyNotepad.exe
*/
namespace MyNotepad
{
    class Form1 : Form
    {
        // コンストラクタ
        public Form1()
        {
            // 定数の定義
            const string APPLICATION_NAME = "私のメモ帳";
            const string DEFAULT_TITLE = "タイトルなし";
            const string OPEN_FILE_DIALOG_FILTER = "TextFile(*.txt)|*.txt|AllFile(*.*)|*.*";
            bool monoFlag = (RuntimeInformation.FrameworkDescription).Substring(0, 4).ToUpper() == "MONO";

            // フィールドの初期化
            string _text = "";
            string _fileName = "";

            // タイトルバーの初期化
            this.Text = String.Format("{0} - {1}",  DEFAULT_TITLE, APPLICATION_NAME);
            // ステータスバーの初期化
            var statusbar = new StatusStrip();
            this.Controls.Add(statusbar);
            // テキストボックスの初期化
            var textbox = new TextBox
            {
                Dock = DockStyle.Fill,
                Multiline = true, // 複数行
                AcceptsReturn = true, // エンターキーで改行
                ScrollBars = ScrollBars.Both, // 垂直水平
            };
            this.Controls.Add(textbox);
            // メニューの初期化
            var menubar = new MenuStrip{ Name = "MENU", Height = 48, };

            var menuFile = new ToolStripMenuItem  { Name = "MENU_FILE", Text = "ファイル(&F)", };
            menubar.Items.Add(menuFile);
            var menuEdit = new ToolStripMenuItem  { Name = "MENU_EDIT", Text = " 編集 (&E)", };
            menubar.Items.Add(menuEdit);
            var menuHelp = new ToolStripMenuItem  { Name = "MENU_HELP", Text = "ヘルプ(&H)", };
            menubar.Items.Add(menuHelp);

            this.Controls.Add(menubar);

            var menuNew = new ToolStripMenuItem
            {
                Name = "MENU_NEW", Text = "新規作成",
                ShortcutKeys = Keys.Control | Keys.N,
                ShowShortcutKeys = true,
             };
            menuFile.DropDownItems.Add(menuNew);

            var menuOpen = new ToolStripMenuItem
            {
                Name = "MENU_OPEN", Text = "開く",
                ShortcutKeys = Keys.Control | Keys.O,
                ShowShortcutKeys = true,
            };
            menuFile.DropDownItems.Add(menuOpen);
            
            var menuSave = new ToolStripMenuItem
            {
                Name = "MENU_SAVE", Text = "保存",
                ShortcutKeys = Keys.Control | Keys.S,
                ShowShortcutKeys = true,
            };
            menuFile.DropDownItems.Add(menuSave);
            
            var menuSaveAs = new ToolStripMenuItem
            {
                Name = "MENU_SAVEAS", Text = "名前をつけて保存",
                ShortcutKeys = Keys.Control | Keys.Shift | Keys.S,
                ShowShortcutKeys = true,
            };
            menuFile.DropDownItems.Add(menuSaveAs);
            
            var menuClose = new ToolStripMenuItem
            {
                Name = "MENU_CLOSE", Text = "終了",
                ShortcutKeys = Keys.Control | Keys.Q,
                ShowShortcutKeys = true,
            };
            menuFile.DropDownItems.Add(menuClose);

            var menuSearchFor = new ToolStripMenuItem
            {
                Name = "MENU_SEARCHFOR", Text = "検索する",
                ShortcutKeys = Keys.Control | Keys.F,
                ShowShortcutKeys = true,
            };
            menuEdit.DropDownItems.Add(menuSearchFor);

            // ステータスバー
            var statusCursorPosition = new ToolStripStatusLabel{ Text = "行1,列1", };
            statusbar.Items.Add(statusCursorPosition);

            // ウィンドウ初期サイズ
            this.Size = new Size(800, 600);

            // タイトルバーをセット
            Action setTitle = () =>
            {
                this.Text = String.Format("{0}{1} - {2}",
                    textbox.Modified ? "*" : "",
                    _fileName == "" ? DEFAULT_TITLE : System.IO.Path.GetFileName(_fileName),
                    APPLICATION_NAME);
            };

            // フォームを閉じる
            Action closeForm1 = () =>
            {
                this.Close();
            };
            menuClose.Click += (sender, e) => closeForm1();

            // テキストファイル保存
            Action<string, string> saveText = (string fileName, string text) =>
            {
                using(var sw = new StreamWriter(fileName, false, Encoding.UTF8))
                {
                    sw.Write(text);
                };
            };
            // 名前をつけて保存
            Action saveAsText = () =>
            {
                var dialog = new SaveFileDialog { Filter = OPEN_FILE_DIALOG_FILTER, };
                if (dialog.ShowDialog() != DialogResult.OK) return;
                _fileName = dialog.FileName;

                saveText(_fileName, textbox.Text);
                textbox.Modified = false;
                _text = textbox.Text;
                setTitle();
            };
            menuSaveAs.Click +=(sender, e) => saveAsText();
            // テキストファイル読み込み
            Func<string, string> loadText = (string fileName) =>
            {
                string text = "";
                using(var sr = new StreamReader(fileName, Encoding.UTF8))
                {
                    text = sr.ReadToEnd();
                };
                return text;
            };
            // 保存
            Action overWriteText = () =>
            {
                if (_fileName == "")
                {
                    saveAsText();
                    return;
                }
                saveText(_fileName, textbox.Text);
                textbox.Modified = false;
                _text = textbox.Text;
                setTitle();
            };
            menuSave.Click += (sender, e) => overWriteText();
            // 保存確認
            Func<bool> SaveConfirmation = () =>
            {
                if (textbox.Modified)
                {
                    var result = (int)MessageBox.Show("保存しますか?", "確認", (System.Windows.Forms.MessageBoxButtons)1);
                    if (result == 1)
                    {
                        overWriteText();
                    }
                    else
                    {
                        return false;
                    }
                }
                return true;
            };
            // テキストファイルを開く
            Action openTextWithDialog = async () =>
            {
                SaveConfirmation();
                var dialog = new OpenFileDialog { Filter = OPEN_FILE_DIALOG_FILTER, };
                if (dialog.ShowDialog() != DialogResult.OK) return;
                _fileName = dialog.FileName;
                
                _text = await Task.Run<string>(()=> { return loadText(_fileName); });
                textbox.Text = _text;
                textbox.Modified = false;
            };
            Action<string> openText = async (string fileName) =>
            {
                SaveConfirmation();
                _fileName = fileName;
                
                _text = await Task.Run<string>(()=> { return loadText(_fileName); });
                textbox.Text = _text;
                textbox.Modified = false;
            };
            menuOpen.Click +=(sender, e) => openTextWithDialog();


            // 新規作成
            Action newText = () =>
            {
                SaveConfirmation();
                _text = "";
                _fileName = "";
                setTitle();
                textbox.Clear();
                textbox.Modified = false;
            };
            menuNew.Click += (sender, e) => newText();
            
            // ドラック&ドロップを受け入れる
            this.AllowDrop = true;
            // ドラック&ドロップの効果
            this.DragEnter += (sender, e) =>
            {
                e.Effect = DragDropEffects.All;
            };
            // ドラック&ドロップイベント
            this.DragDrop += (sender, e) =>
            {
                // ドロップされたデータを文字配列に取得
                string[] fileNames =
                    (string[]) e.Data.GetData(DataFormats.FileDrop, false);
                
                if (fileNames.Length > 0)
                {
                    openText(fileNames[0]);
                }
            };
            // フォームのロード
            this.Load += (sender, e) =>
            {
                string[] args = Environment.GetCommandLineArgs();
                if (args.Length > 1)
                {
                    openText(args[1]);
                }
            };
            // フォームのクローズ
            this.Closing += (sender, e) =>
            {
                e.Cancel = !(SaveConfirmation());
            };
            // 変更
            textbox.TextChanged += (sender, e) =>
            {    
                textbox.Modified = _text != textbox.Text;
                setTitle();
            };
            // カーソル位置を表示
            Action showCursorPosition = () =>
            {
                int line = textbox.GetLineFromCharIndex(textbox.SelectionStart);
                int column = textbox.SelectionStart - textbox.GetFirstCharIndexOfCurrentLine();
                int n = monoFlag ? 0 : 1; // Monoと.Net Frameworkで行が異なるので調整。原因は未調査。
                statusCursorPosition.Text = String.Format("行{0}、列{1}", line+n, column+1);
            };
            // 検索
            Func<string, bool> searchText = (string word) =>
            {
                bool result = false;
                string str = textbox.Text;
                int startPos = textbox.SelectionStart;

                int pos = str.IndexOf(word, startPos);
                if (pos >= 0)
                {
                    textbox.SelectionStart = pos;
                    textbox.SelectionLength = word.Length;
                    result = true;
                }
                return result;
            };

            // 検索する
            Action searchFor = () =>
            {
                var dialog = new Form{ Size = new Size(640, 400), Text = "検索する", };
                var searchWord = new TextBox{ Size = new Size(260, 64), Location = new Point(10, 10) };
                var searchButton = new Button{ Size = new Size(120, 64), Location = new Point(10, 80), Text = " 検索 ", };
                dialog.Controls.Add(searchWord);
                dialog.Controls.Add(searchButton);
                searchButton.Click += (o2, e2) =>
                {
                    if (searchText(searchWord.Text) == false)
                    {
                        MessageBox.Show("該当無し");
                    }
                    dialog.Close();
                };
                searchWord.KeyDown += (o3, e3) =>
                {
                    if (e3.KeyCode == Keys.Enter)
                    {
                        if (searchText(searchWord.Text) == false)
                        {
                            MessageBox.Show("該当無し");
                        }
                        dialog.Close();
                    }
                };
                dialog.ShowDialog();
            };
            menuSearchFor.Click += (o, e) => searchFor();

            // カーソル位置
            textbox.Click += (o, e) => showCursorPosition();
            textbox.KeyUp += (o, e) => showCursorPosition();
        }
        // エントリーポイント
        [STAThread]
        static void Main()
        {
            var dl = (DefaultTraceListener)Debug.Listeners["Default"];
            dl.LogFileName = @"E:\csharp\mono\MyNotepad\Debug.log";
            Application.Run(new Form1());
        }
    }//class
}//ns

コンパイル

Mono環境(Ubuntu)でコンパイル

コンパイル
mcs Form1.cs -r:System.Windows.Forms.dll -r:System.Drawing.dll -out:MyNotepad.exe
コンパイル(デバッグあり)
mcs Form1.cs -d:DEBUG -r:System.Windows.Forms.dll -r:System.Drawing.dll -out:MyNotepad.exe

実行

.NET Framework環境(Windows11)で実行

感想

コンストラクタで全ての処理を行おうとしましたが長くなり過ぎました。

コメント