none
イベントの削除がうまく出来ない RRS feed

  • 質問

  • 例えば、フォーム上からコントロールが削除された時に、フォームに紐付いているイベントハンドラを
    削除しようと思ったのですが、下記コードではうまく削除されませんでした。
    (Loadイベントにイベントハンドラを設定している関係で、試験的にOnLoad()で強制的に呼び出しています。)

    何が悪いのか見当がつかない為、どうすれば正しく削除されるのかご教示下さい。

    Form

    private void button1_Click(object sender, EventArgs e)
    {
        this.Controls.Remove(ExTextBox1);
        this.OnLoad(null);
    }


    ExTextBox

    private Control _currentParent = null;
    
    protected override void OnParentChanged(EventArgs e)
    {
        base.OnParentChanged(e);
    
        if (DesignMode)
        {
            return;
        }
    
        // 親コントロールロード発生時に、当該Loadイベントを紐付ける
        ControlHelper.AddParentLoadEvent(this, _onLoad, _currentParent);
        _currentParent = Parent;
    }
    
    private void _onLoad(object sender, EventArgs e)
    {
        // コントロール位置にLabelを配置する
        ControlHelper.PlacementOfSameParent(this, _readOnlyLabel);
    }

    ControlHelper

    // 元親からイベントハンドラを削除してから追加
    public static void AddParentLoadEvent(Control target, EventHandler<EventArgs> e, Control prevParent) { if (prevParent != null) { if (prevParent.GetType().IsSubclassOf(typeof(UserControl))) { ((UserControl)prevParent).Load -= new EventHandler(e); } else if (prevParent.GetType().IsSubclassOf(typeof(Form))) { ((Form)prevParent).Load -= new EventHandler(e); } } AddParentLoadEvent(target, e); }

    // イベントハンドラの追加
    public static void AddParentLoadEvent(Control target, EventHandler<EventArgs> e) { Control parent = target; while (parent.Parent != null) { parent = parent.Parent; if (parent.GetType().IsSubclassOf(typeof(UserControl))) { ((UserControl)parent).Load += new EventHandler(e); break; } else if (parent.GetType().IsSubclassOf(typeof(Form))) { ((Form)parent).Load += new EventHandler(e); break; } } }


    2013年3月6日 2:29

回答

  • すみません的外れな指摘をしていました。

    それで気付いたんですが、もしかして、4 か所にある new EventHandler(e) はすべて、単に e とするだけでいいのではないでしょうか。

    ((UserControl)prevParent).Load -= e;
    
    ((UserControl)parent).Load += e;

    new EventHandler(e) だと、_onLoad メソッドそのものではなく、_onLoad を呼び出す EventHandler<EventArgs> 型のインスタンスの Invoke メソッドが、イベントハンドラになっているかもしれません。

    その場合、異なるインスタンスのメソッドのデリゲートになるので、イベントに追加した時とイベントから削除した時で別のイベントハンドラと認識されるはずです(MulticastDelegate.Equals http://msdn.microsoft.com/ja-jp/library/vstudio/1ts3c5tx.aspx を参照)。

    • 編集済み octopus-jelly 2013年3月6日 4:59
    • 回答としてマーク takiru 2013年3月6日 5:57
    • 回答としてマークされていない takiru 2013年3月6日 5:57
    • 回答としてマーク takiru 2013年3月6日 5:58
    2013年3月6日 4:48

すべての返信

  • AddParentLoadEvent(target,e) バージョンの方では再帰的に UserControl 型か Form 型の親コントロールを探していますよね。一方、AddParentLoadEvent(target,e,prevParent) バージョンの方では引数の prevParent しか調べていません。

    実際のコントロールの階層関係がどうなっているのか知りませんが、AddParentLoadEvent(target,e) の中で実際にイベントハンドラを追加した parent と、AddParentLoadEvent(target,e,prevParent) の引数の prevParent が別物なのではないでしょうか。

    AddParentLoadEvent(target,e,prevParent) の方も再帰的に UserControl 型か Form 型の親コントロールを探すようにコードを変更してみては?

    public static void AddParentLoadEvent
        (Control target, EventHandler<EventArgs> e, Control prevParent)
    {
        while (prevParent != null)
        {
            if (prevParent.GetType().IsSubclassOf(typeof(UserControl)))
            {
                ((UserControl)prevParent).Load -= new EventHandler(e);
                break;
            }
            else if (prevParent.GetType().IsSubclassOf(typeof(Form)))
            {
                ((Form)prevParent).Load -= new EventHandler(e);
                break;
            }
            prevParent = prevParent.Parent;
        }
        AddParentLoadEvent(target, e);
    }
    2013年3月6日 3:04
  • ご回答ありがとうございます。

    試しに頂いたコードのように再帰的に探してイベントハンドラを削除するよう
    変更してみましたが、やはりダメでした。

    ExText内で、
    AddParentLoadEvent(target, e, prevParent)
    の呼出し直後に現在のParentを保持した上で、再度OnLoad()によって
    呼び出されている為、問題ないという解釈でした。
    再帰的に探すコードでも、初回にヒットするので、イベントハンドラを削除しようとする
    対象のオブジェクトに誤りはないと思うのですが・・・。

    2013年3月6日 4:31
  • すみません的外れな指摘をしていました。

    それで気付いたんですが、もしかして、4 か所にある new EventHandler(e) はすべて、単に e とするだけでいいのではないでしょうか。

    ((UserControl)prevParent).Load -= e;
    
    ((UserControl)parent).Load += e;

    new EventHandler(e) だと、_onLoad メソッドそのものではなく、_onLoad を呼び出す EventHandler<EventArgs> 型のインスタンスの Invoke メソッドが、イベントハンドラになっているかもしれません。

    その場合、異なるインスタンスのメソッドのデリゲートになるので、イベントに追加した時とイベントから削除した時で別のイベントハンドラと認識されるはずです(MulticastDelegate.Equals http://msdn.microsoft.com/ja-jp/library/vstudio/1ts3c5tx.aspx を参照)。

    • 編集済み octopus-jelly 2013年3月6日 4:59
    • 回答としてマーク takiru 2013年3月6日 5:57
    • 回答としてマークされていない takiru 2013年3月6日 5:57
    • 回答としてマーク takiru 2013年3月6日 5:58
    2013年3月6日 4:48
  • 度々ありがとうございます。

    確かに以前から、ネットの情報から手探りで作成し、なんでそういうコードにしなければ
    ならないのか甚だ疑問でしたが、なるほど、EventHandler<EventArgs>でそもそも
    定義している関係上、使い方を誤っていることにやっと気づきました。

    new EventHandler(e)ではなく、eとすることで解決しました。
    やっとこれで、ちょっとソースを整理すればいいとこまで辿りつけました。
    ありがとうございます。

    public static void AddParentLoadEvent(Control target, EventHandler e, Control prevParent)
    {
        if (prevParent != null)
        {
            if (prevParent.GetType().IsSubclassOf(typeof(UserControl)))
            {
                ((UserControl)prevParent).Load -= e;
            }
            else if (prevParent.GetType().IsSubclassOf(typeof(Form)))
            {
                ((Form)prevParent).Load -= e;
            }
        }
        AddParentLoadEvent(target, e);
    }
    
    public static void AddParentLoadEvent(Control target, EventHandler e)
    {
        Control parent = target;
        while (parent.Parent != null)
        {
            parent = parent.Parent;
            if (parent.GetType().IsSubclassOf(typeof(UserControl)))
            {
                ((UserControl)parent).Load += e;
                break;
            }
            else if (parent.GetType().IsSubclassOf(typeof(Form)))
            {
                ((Form)parent).Load += e;
                break;
            }
        }
    }
    2013年3月6日 5:57