none
自動生成されるイベントハンドラのカスタマイズ RRS feed

  • 質問

  • 自動生成されるイベントハンドラを、独自にカスタマイズされた状態で生成することはできませんでしょうか?

    例えば、ボタンのクリックイベントに関して、デザイナー上でボタンをダブルクリックしたり、ビハインドコード上の入力補助(+=と入力しTab押下)などで、以下のようなコードが自動で生成されます。

    private void Button1_Click(object sender, RoutedEventArgs e)
    {
    }

    これを、例えば以下のようにカスタマイズされた状態でコード生成できないかと思っています。

    #region Button1_Click private void Button1_Click(object sender, RoutedEventArgs e) { try { } catch(Exception ex) { throw; } finally { } } #endregion

    独自のコードスニペットは作っているのですが、この自動生成されるコードのカスタマイズに関してはやり方が分かりませんし、できるのかもわかりません。

    ご存知の方がいらっしゃいましたら、ご教授願えれば幸いです。

    環境 : VisualStudio2012、Windows7、Windows8


    • 編集済み かずし 2013年2月13日 8:12
    2013年2月13日 7:58

回答

  • 機能がないなら作ればいいよね。というわけで、アドインで書いてみた。
    VS2010でしか試してないけどVS2012でも動くはず

    using System;
    using Extensibility;
    using EnvDTE;
    using EnvDTE80;
    namespace HandleSnippet
    {
        public class Connect : IDTExtensibility2
        {
            public Connect() { }
            public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
            {
                _applicationObject = (DTE2)application;
                _addInInstance = (AddIn)addInInst;
    
                _HandleModifier = new CSHandleModifier();
                _HandleModifier.Attach(_applicationObject);
    
            }
            public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
            {
                if (_HandleModifier != null)
                {
                    _HandleModifier.Dettach();
                    _HandleModifier = null;
                }
            }
            public void OnAddInsUpdate(ref Array custom) { }
            public void OnStartupComplete(ref Array custom) { }
            public void OnBeginShutdown(ref Array custom) { }
    
            private DTE2 _applicationObject;
            private AddIn _addInInstance;
            private CSHandleModifier _HandleModifier;
        }
    
        internal class CSHandleModifier
        {
            private CodeModelEvents _CodeModelEvents;
            private DTE2 _applicationObject;
            public void Attach(DTE2 _applicationObject)
            {
                Dettach();
                this._applicationObject = _applicationObject;
                var ev2 = (EnvDTE80.Events2)_applicationObject.DTE.Events;
                _CodeModelEvents = ev2.CodeModelEvents;
                _CodeModelEvents.ElementAdded += CodeModelEvents_ElementAdded;
    
            }
            public void Dettach()
            {
                if (_CodeModelEvents != null)
                {
                    _CodeModelEvents.ElementAdded -= CodeModelEvents_ElementAdded;
                    _CodeModelEvents = null;
                    _applicationObject = null;
                }
            }
    
            void CodeModelEvents_ElementAdded(CodeElement Element)
            {
                if (Element.Kind == vsCMElement.vsCMElementFunction
                && Element.Language == EnvDTE.CodeModelLanguageConstants.vsCMLanguageCSharp)
                {
                    TextDocument doc = Element.StartPoint.Parent;
                    TextSelection sel = null;
    
                    try
                    {
                        sel = doc.Selection;
                    }
                    catch (System.Runtime.InteropServices.COMException)
                    {
                        DelayTool.Retry(this, Element);//コードのウィンドウがないときにSelectionが取れないので表示してから再度挑戦
                        return;
                    }
    
                    VirtualPoint currentPos = sel.ActivePoint;
    
                    try
                    {
                        sel.MoveToPoint(Element.StartPoint, false);
                        sel.MoveToPoint(Element.EndPoint, true);
                        CodeAnalyze(sel);
                    }
                    finally
                    {
                        sel.MoveToPoint(currentPos);
                    }
                }
            }
    
            private class DelayTool
            {
                private DelayTool() { }
                public static void Retry(CSHandleModifier modifier, CodeElement Element)
                {
                    DelayTool x = new DelayTool();
                    x.modifier = modifier;
                    x.Element = Element;
    
                    modifier._applicationObject.Events.WindowEvents.WindowActivated += x.WindowEvents_WindowActivated;
                }
                private CSHandleModifier modifier;
                private CodeElement Element;
                void WindowEvents_WindowActivated(Window GotFocus, Window LostFocus)
                {
                    modifier._applicationObject.Events.WindowEvents.WindowActivated -= WindowEvents_WindowActivated;
                    modifier.CodeModelEvents_ElementAdded(Element);
                }
            }
    
            private const string CSPattern = @"(?<HEAD>\s*void\s+\S*\(object\s+sender\s*,\s*\S*EventArgs\s+e\)(\s|(\r\n))*"
                + @"\{(\s|(\r\n))*)"
                + @"(?<THROW>throw\s+new\s+(System.)?NotImplementedException\s*\(\s*\)\s*;)?" //+=を入力してで生成される時に限定するなら、この行の最後の?を消す
                + @"(?<TAIL>(\s|(\r\n))*\})";
    
            private System.Text.RegularExpressions.Regex regCSHandler
                = new System.Text.RegularExpressions.Regex(CSPattern);
    
    
            void CodeAnalyze(TextSelection sel)
            {
                string originalCode = sel.Text;
                var match = regCSHandler.Match(originalCode);
                if (match.Success)
                {
                    VirtualPoint p = sel.TopPoint;
    
                    string head = match.Groups["HEAD"].Value;
                    string tail = match.Groups["TAIL"].Value;
                    string oldCode = string.Empty;
    
                    var grp = match.Groups["THROW"];
                    if (grp.Success)
                    {
                        oldCode = grp.Value;
                    }
                    string newCode = OnEventHanderCreated(grp.Value);
                    sel.Text =head + newCode + tail;
                    sel.SmartFormat();
                }
            }
    
            protected virtual string OnEventHanderCreated(string originalCode)
            {
                return "try\r\n{\r\n}\r\ncatch(System.Exception ex)\r\n{\r\nthrow;\r\n}\r\nfinally\r\n{\r\n}";                                                                                                                                      //#個人的にはこの程度のスケルトンは十分許容範囲だけどなぁ….~すべきというのもなんだか
            }
        }
    }
    


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答の候補に設定 佐伯玲 2013年2月18日 4:05
    • 回答としてマーク かずし 2013年2月20日 0:57
    2013年2月14日 11:57
  • ご回答ありがとうございます。

    すみません。作成しているのがストアアプリだということを記載していませんでした。

    おっしゃる通りで、WPFやWinFormsであればそうなのですが、ストアアプリだと、UnhandledException が発生しないケースがあったり、UnhandledException のイベント引数からだと発生した例外情報が得られないケースなどがあり、UnhandledException が使い物になりません。

    ■UnhandledException
    http://msdn.microsoft.com/ja-jp/library/windows/apps/windows.ui.xaml.application.unhandledexception.aspx

    よって、面倒なのですが、すべてのイベントハンドラ内で例外処理をフックする実装を加えないといけないと判断しています。
    そこで、その手間を少しでも軽減すべく、今回の質問に至った次第です。

    • 回答としてマーク かずし 2013年2月20日 0:58
    2013年2月15日 2:00

すべての返信

  • 質問そのものについては私はわかりませんので他の方にお任せするとして…

    イベントハンドラをカスタマイズしてまで、同じ処理を多数並べたいというのは、コードのコピペと同じくあまり勧められることではないと思います。

    例えば今回の場合、イベントハンドラに対する例外処理とのことですから、WPFであればApplication.DispatcherUnhandledExceptionイベントやWinFormsであればApplication.ThreadExceptionイベントが使えるかもしれません。

    2013年2月13日 9:15
  • イベントハンドラスタブに対するカスタマイズは、現状提供されていないと思います。
    必要だと考えるのであれば、Connect に提案を投稿してください。(開発チームが採用したくなるようなメリットを説明しないと蹴られる可能性もあります)

    ただ、その前に佐祐理さんが書かれているように、共通の例外処理はもう少し違う方法を考えた方がよいと思っています。

    2013年2月13日 14:33
    モデレータ
  • 無さそうですね。InheritedTemplate.csのようなテンプレートがあればと思ったのですが、私が探した限りでは見つかりませんでした。

    (参考)
    Customizing Visual Studio's Code Generation Templates
    http://www.codeproject.com/Articles/5646/Customizing-Visual-Studio-s-Code-Generation-Templa

    確かにカスタマイズできても良い気がしますので、Azuleanさんが言われるように必要であればConnectに提案されてみると良いと思います。
    ちなみに以下は VS2012で実現されました。ただ、こちらは当然と言えば当然なので、少し事情が異なりますが・・・

    Auto generated event handlers should use implicit conversion of method group to delegate
    https://connect.microsoft.com/VisualStudio/feedback/details/632300/auto-generated-event-handlers-should-use-implicit-conversion-of-method-group-to-delegate


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2013年2月14日 2:10
    モデレータ
  • 機能がないなら作ればいいよね。というわけで、アドインで書いてみた。
    VS2010でしか試してないけどVS2012でも動くはず

    using System;
    using Extensibility;
    using EnvDTE;
    using EnvDTE80;
    namespace HandleSnippet
    {
        public class Connect : IDTExtensibility2
        {
            public Connect() { }
            public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
            {
                _applicationObject = (DTE2)application;
                _addInInstance = (AddIn)addInInst;
    
                _HandleModifier = new CSHandleModifier();
                _HandleModifier.Attach(_applicationObject);
    
            }
            public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
            {
                if (_HandleModifier != null)
                {
                    _HandleModifier.Dettach();
                    _HandleModifier = null;
                }
            }
            public void OnAddInsUpdate(ref Array custom) { }
            public void OnStartupComplete(ref Array custom) { }
            public void OnBeginShutdown(ref Array custom) { }
    
            private DTE2 _applicationObject;
            private AddIn _addInInstance;
            private CSHandleModifier _HandleModifier;
        }
    
        internal class CSHandleModifier
        {
            private CodeModelEvents _CodeModelEvents;
            private DTE2 _applicationObject;
            public void Attach(DTE2 _applicationObject)
            {
                Dettach();
                this._applicationObject = _applicationObject;
                var ev2 = (EnvDTE80.Events2)_applicationObject.DTE.Events;
                _CodeModelEvents = ev2.CodeModelEvents;
                _CodeModelEvents.ElementAdded += CodeModelEvents_ElementAdded;
    
            }
            public void Dettach()
            {
                if (_CodeModelEvents != null)
                {
                    _CodeModelEvents.ElementAdded -= CodeModelEvents_ElementAdded;
                    _CodeModelEvents = null;
                    _applicationObject = null;
                }
            }
    
            void CodeModelEvents_ElementAdded(CodeElement Element)
            {
                if (Element.Kind == vsCMElement.vsCMElementFunction
                && Element.Language == EnvDTE.CodeModelLanguageConstants.vsCMLanguageCSharp)
                {
                    TextDocument doc = Element.StartPoint.Parent;
                    TextSelection sel = null;
    
                    try
                    {
                        sel = doc.Selection;
                    }
                    catch (System.Runtime.InteropServices.COMException)
                    {
                        DelayTool.Retry(this, Element);//コードのウィンドウがないときにSelectionが取れないので表示してから再度挑戦
                        return;
                    }
    
                    VirtualPoint currentPos = sel.ActivePoint;
    
                    try
                    {
                        sel.MoveToPoint(Element.StartPoint, false);
                        sel.MoveToPoint(Element.EndPoint, true);
                        CodeAnalyze(sel);
                    }
                    finally
                    {
                        sel.MoveToPoint(currentPos);
                    }
                }
            }
    
            private class DelayTool
            {
                private DelayTool() { }
                public static void Retry(CSHandleModifier modifier, CodeElement Element)
                {
                    DelayTool x = new DelayTool();
                    x.modifier = modifier;
                    x.Element = Element;
    
                    modifier._applicationObject.Events.WindowEvents.WindowActivated += x.WindowEvents_WindowActivated;
                }
                private CSHandleModifier modifier;
                private CodeElement Element;
                void WindowEvents_WindowActivated(Window GotFocus, Window LostFocus)
                {
                    modifier._applicationObject.Events.WindowEvents.WindowActivated -= WindowEvents_WindowActivated;
                    modifier.CodeModelEvents_ElementAdded(Element);
                }
            }
    
            private const string CSPattern = @"(?<HEAD>\s*void\s+\S*\(object\s+sender\s*,\s*\S*EventArgs\s+e\)(\s|(\r\n))*"
                + @"\{(\s|(\r\n))*)"
                + @"(?<THROW>throw\s+new\s+(System.)?NotImplementedException\s*\(\s*\)\s*;)?" //+=を入力してで生成される時に限定するなら、この行の最後の?を消す
                + @"(?<TAIL>(\s|(\r\n))*\})";
    
            private System.Text.RegularExpressions.Regex regCSHandler
                = new System.Text.RegularExpressions.Regex(CSPattern);
    
    
            void CodeAnalyze(TextSelection sel)
            {
                string originalCode = sel.Text;
                var match = regCSHandler.Match(originalCode);
                if (match.Success)
                {
                    VirtualPoint p = sel.TopPoint;
    
                    string head = match.Groups["HEAD"].Value;
                    string tail = match.Groups["TAIL"].Value;
                    string oldCode = string.Empty;
    
                    var grp = match.Groups["THROW"];
                    if (grp.Success)
                    {
                        oldCode = grp.Value;
                    }
                    string newCode = OnEventHanderCreated(grp.Value);
                    sel.Text =head + newCode + tail;
                    sel.SmartFormat();
                }
            }
    
            protected virtual string OnEventHanderCreated(string originalCode)
            {
                return "try\r\n{\r\n}\r\ncatch(System.Exception ex)\r\n{\r\nthrow;\r\n}\r\nfinally\r\n{\r\n}";                                                                                                                                      //#個人的にはこの程度のスケルトンは十分許容範囲だけどなぁ….~すべきというのもなんだか
            }
        }
    }
    


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答の候補に設定 佐伯玲 2013年2月18日 4:05
    • 回答としてマーク かずし 2013年2月20日 0:57
    2013年2月14日 11:57
  • ご回答ありがとうございます。

    すみません。作成しているのがストアアプリだということを記載していませんでした。

    おっしゃる通りで、WPFやWinFormsであればそうなのですが、ストアアプリだと、UnhandledException が発生しないケースがあったり、UnhandledException のイベント引数からだと発生した例外情報が得られないケースなどがあり、UnhandledException が使い物になりません。

    ■UnhandledException
    http://msdn.microsoft.com/ja-jp/library/windows/apps/windows.ui.xaml.application.unhandledexception.aspx

    よって、面倒なのですが、すべてのイベントハンドラ内で例外処理をフックする実装を加えないといけないと判断しています。
    そこで、その手間を少しでも軽減すべく、今回の質問に至った次第です。

    • 回答としてマーク かずし 2013年2月20日 0:58
    2013年2月15日 2:00
  • ご回答ありがとうございます。

    >共通の例外処理はもう少し違う方法を考えた方がよいと思っています。

    おっしゃる通りなのですが、現状MSさんのサポートにも確認したのですが、ストアアプリの場合、UnhandledExceptionを利用しての例外処理の集約は厳しいと判断しています。

    もちろん、そのイベントハンドラ内で行う処理の過程で発生が想定できる例外に対するフック&後処理は、イベントハンドラ内で書かなければいけないのは言うまでもないです。

    2013年2月15日 2:17
  • ご回答ありがとうございます。

    私もテンプレートは見てみましたが、残念!!って感じでした。。。

    2013年2月15日 2:18
  • ご回答ありがとうございます。

    VS2012で試してみました。できました!感激っす!!!!

    コードをコピペして作っただけなので、正直何が何だか分かっていませんが、これから勉強します!
    ありがとうございました!


    • 編集済み かずし 2013年2月15日 2:28
    2013年2月15日 2:28