読者です 読者をやめる 読者になる 読者になる

C#で文字列をコードとしてコンパイルして, その中のPaintイベントを動的にメインプログラムのウィンドウに追加したり削除したりする

タイトルが長い…
でもそのままです…


普段GUIを一切実装しないのでイベントとかよく分かってないんですが, プログラムを実行中に動的にPaintイベントを追加したり削除したりしなければいけなくなりました
ややこしいのは、追加したり削除したりするイベントは実体として持っているわけではなくて, プログラム内に文字列として保持しているという状況です

やりたいことをまとめると

  • 文字列をC#のコードとしてコンパイルし, 特定のクラスのインスタンスを得る
  • コンパイルしたプログラム内のイベントをメインプログラムのフォームにPaintイベントとして追加したり削除したりする

の2つです


まず文字列をC#のコードとしてコンパイルし, 特定のクラスのインスタンスを得るコードですが, これは非常に簡単で

var param = new CompilerParameters();
param.ReferencedAssemblies.AddRange(new[] {"参照してるDLLの名前"});
var assembly =  new CSharpCodeProvider()
    .CompileAssemblyFromSource(param, "コード文字列")
    .CompiledAssembly;

これでコードをコンパイルして, アセンブリを得ることが出来ます

後はアセンブリから特定のクラスのインスタンスを作成するんですが

var dataType = assembly.GetType("名前空間.クラス名");
dynamic instance = Activator.CreateInstance(dataType, コンストラクタの引数);

こんな感じでインスタンスを得ることが出来ます
コンストラクタの引数は特になければCreateInstanceの引数は型情報だけで大丈夫です


次にイベントの動的な追加と削除ですが, コンストラクタにFormのインスタンスを渡すと過程すると, そのインスタンスに対してPaintイベントを追加と削除を行うコードとなります

詳細に関してはこちらのサイトが非常に参考になりました
devlights.hatenablog.com


今回書くコードは, ウィンドウをクリックするとPaintイベントを追加し, もう一度クリックすると削除するものです
Paintイベント自体はウィンドウの色を青や赤にするものにします


まずコンストラクタですが, グローバルにFormを定義しておいて, そこに代入します
また色を変更するためのカウンタとrbgを定義しておきます
ついでにClickイベントも書いておきます

Form form;
int count;
int r = 255, g = 0, b = 0;
public クラス名(Form _form)
{
    form = _form;
    form.Click += Click;
    count = 0;
}
public void Click(object sender, EventArgs e)
{
    if (count % 2 == 0)
    {
        AddEvent();
    }else{
        DeleteEvent();
    }
    form.Refresh();
    count++;
}

次に追加したり削除したりするPaintイベントを書きます
ここではウィンドウの色を青や赤にするイベントとします

public void ExecuteEvent(object sender, PaintEventArgs e)
{
    if (r == 255)
    {
        r = 0;
        b = 255;
    }else{
        r = 255;
        b = 0;
    }
    e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(r, g, b)), 0, 0, 640, 480);
}

後はExecuteEventをPaintイベントとして追加するAddEventと削除するDeleteEventという関数を定義します

public void AddEvent()
{
    form.GetType()
        .GetEvent("Paint")
        .AddEventHandler(form,
            Delegate.CreateDelegate(typeof(PaintEventHandler), this, GetType().GetMethod("ExecuteEvent")));
    MessageBox.Show("イベントを追加しました");
}
public void AddEvent()
{
    form.GetType()
        .GetEvent("Paint")
        .RemoveEventHandler(form,
            Delegate.CreateDelegate(typeof(PaintEventHandler), this, GetType().GetMethod("ExecuteEvent")));
    MessageBox.Show("イベントを削除しました");
}

こんな感じで完了です


全体のコードはこんな感じになります


GUIは全く分かりませんが, それっぽいものが作れたと思います
何か良い書き方があれば教えて下さい…