Ajax Control Toolkit コントロール ライブラリには、いくつかの拡張コントロールが含まれています。これらの拡張コントロールを使用すると、Ajax 効果を通常のコントロールに簡単に追加できます。たとえば、AutoCompleteExtender コントロールを使用して、テキスト ボックスに自動補完 Ajax 効果を追加できます。もちろん、この記事で議論したいのはこれではありません。
Ajax Control Toolkit を Visual Studio 2008 ツールボックスに追加し、新しい aspx ファイルを開いて、その中に TextBox をドラッグします。このとき、TextBox の SmartTasks パネルに、実際に「拡張機能を追加...」へのリンクが表示されました。ボタンとパネルを再度ドラッグしてみたところ、例外なく、各コントロールの SmartTasks パネルの下部に「拡張機能の追加...」リンクが表示されました。
最近、ページの保存、削除、閉じるなどの機能をアクションに抽象化することを計画しています。各アクションは、ターゲット コントロール (Button など) にアクション コントロールをアタッチした後、ターゲット コントロールに Function を持たせます。ページの保存、削除、閉じるなど。 Web フォーム デザイナでボタン コントロールにアクションを簡単にアタッチするにはどうすればよいですか?私が欲しいのは「拡張機能を追加...」のようなものです。
カスタム サーバー コントロールを開発したことのある友人は、コントロールに SmartTasks を追加する場合、ControlDesigner の ActionLists プロパティをオーバーライドし、独自の DesignerActionList を実装する必要があることを知っているはずです。明らかに、TextBox は AjaxControlToolkit の存在を知らないため、「拡張機能を追加...」などの DesignerActionMethodItem はそれによって追加されません。では、.net Framework は、DesignerActionItem を他のコントロールに「動的に挿入」できるようにする、ある種のインターフェイスを提供しているのでしょうか?
AjaxControlToolKit.dll の調査を通じて、これらの拡張コントロールの設計者は「拡張機能の追加」アクションを提供する責任はなく、対応する拡張機能に対応する拡張コンテンツを提供する責任があるだけであることがわかりました。そのため、唯一のエントリは、 Visual StudioのWebフォームデザイナー、ぜひ勉強してください。リフレクターを使用して Microsoft Visual Studio 9.0Common7IDEMicrosoft.Web.Design.Client.dll を開き、IWebSmartTasksProvider インターフェイスを見つけます。このインターフェイスには GetDesignerActionLists メソッドがあります。このメソッドの戻り値は、SmartTasks パネルに表示されるコンテンツである必要があります。 。このインターフェイスには、DataFormDesigner、DataFormXslValueOfDesigner、および ElementDesigner の 3 つの実装クラスがあります。これら 3 つのクラスの名前から、ElementDesigner が最も一般的に使用される実装クラスであることが推測できます。 ElementDesigner の GetDesignerActionLists メソッドは次のように実装されます。
1: DesignerActionListCollection IWebSmartTasksProvider.GetDesignerActionLists()
2: {
3: DesignerActionListCollection コンポーネントアクション = null;
4: if (this.Designer != null)
5: {
6: DesignerActionService サービス = (DesignerActionService)base.DesignerHost.GetService(typeof(DesignerActionService));
7: if (サービス != null)
8: {
9: コンポーネントアクション = service.GetComponentActions(this.Designer.Component);
10: }
11: }
12: if (componentActions == null)
13: {
14: コンポーネントアクション = 新しい DesignerActionListCollection();
15: }
16: コンポーネントアクションを返します。
17: }
18:
19:
20:
21:
上記のコードから、最終的な DesignerActionListCollection は、System.Design アセンブリの System.ComponentModel.Design.DesignerActionService クラスの GetComponentActions と、Microsoft.Web.Design.Client の Microsoft.Web.Design.WebFormDesigner によって決定されることがわかります。 dll.+WebDesignerActionService はこのクラスを継承し、その実装は次のとおりです。
1: 保護されたオーバーライド void GetComponentDesignerActions(IComponent コンポーネント, DesignerActionListCollection actionLists)
2: {
3: コントロール control = コントロールとしてのコンポーネント。
4: ElementDesigner の親 = null;
5: if (コントロール != null)
6: {
7: 親 = ElementDesigner.GetElementDesigner(コントロール);
8: }
9: if ((parent == null) || !parent.InTemplateMode)
10: {
11:base.GetComponentDesignerActions(コンポーネント, actionLists);
12: if ((親 != null) && (parent.Designer != null))
13: {
14: ControlDesigner デザイナー =parent.Designer as ControlDesigner;
15: if ((designer != null) && (designer.AutoFormats.Count > 0))
16: {
17: actionLists.Insert(0, 新しい AutoFormatActionList(parent));
18: }
19: }
20: if ((親 != null) && (parent.Element != null))
21: {
22: IWebDataFormElementCallback dataFormElementCallback =parent.Element.GetDataFormElementCallback();
23: if (dataFormElementCallback != null)
24: {
25: DataFormElementActionList リスト = new DataFormElementActionList(parent,parent.Control,dataFormElementCallback);
26: actionLists.Add(リスト);
27: DataFormElementActionList.ModifyActionListsForListControl(actionLists, list);
28: }
29: }
30: if (((parent != null) && (parent.Designer != null)) &&parent.DocumentDesigner.ExtenderControlHelper.ProvidesActionLists)
31: {
32:parent.DocumentDesigner.ExtenderControlHelper.AddActionLists(親, actionLists);
33: }
34: }
35: if ((親 != null) && (parent.TemplateEditingUI != null))
36: {
37: actionLists.Add(new TemplateEditingActionList(parent.TemplateEditingUI,parent.Element));
38: }
39: }
40:
41:
42:
43:
このメソッドには次の段落があります。
1: if (((parent != null) && (parent.Designer != null)) &&parent.DocumentDesigner.ExtenderControlHelper.ProvidesActionLists)
2: {
3:parent.DocumentDesigner.ExtenderControlHelper.AddActionLists(親, actionLists);
4: }
ここに「拡張機能を追加」アクションが追加されるようです。引き続き ExtenderControlHelper.AddActionLists の実装を確認します。
1: public void AddActionLists(ElementDesigner 要素、DesignerActionListCollection リスト)
2: {
3: lists.Add(new ControlExtenderActionList(element));
4: ExtenderControl コンポーネント = ExtenderControl としての element.Designer.Component;
5: コントロール control = コントロールとしての element.Designer.Component;
6: if ((コンポーネント == null) && (コントロール != null))
7: {
8: IExtenderInformationService サービス = (IExtenderInformationService) control.Site.GetService(typeof(IExtenderInformationService));
9: if (サービス != null)
10: {
11: foreach (service.GetAppliedExtenders(control) の control3 を制御)
12: {
13: lists.Add(new HoistedExtenderActionList(element.Designer, control3));
14: }
15: }
16: }
17: }
18:
19:
20:
21:
このメソッドの最初の文は lists.Add(new ControlExtenderActionList(element)) です。 ControlExtenderActionList は System.ComponentModel.Design.DesignerActionList を継承し、その GetSortedActionItems メソッドは次のように定義されます。
1: パブリック オーバーライド DesignerActionItemCollection GetSortedActionItems()
2: {
3: コントロール コンポーネント = (コントロール) this._htmlDesigner.Component;
4: DesignerActionItemCollection アイテム = new DesignerActionItemCollection();
5: IExtenderInformationService サービス = (IExtenderInformationService) コンポーネント.Site.GetService(typeof(IExtenderInformationService));
6: 文字列カテゴリ = SR.GetString(SR.Ids.SmartTasksLabelExtenderSection, CultureInfo.CurrentUICulture);
7: if (service.IsControlExtendible(component))
8: {
9: 文字列表示名 = SR.GetString(SR.Ids.SmartTasksAddExtender, CultureInfo.CurrentUICulture);
10: items.Add(new DesignerActionMethodItem(this, "AddExtender", displayName, category, true));
11: }
12: if (service.IsControlExtended(component))
13: {
14: 文字列 str3 = SR.GetString(SR.Ids.SmartTasksRemoveExtender, CultureInfo.CurrentUICulture);
15: items.Add(new DesignerActionMethodItem(this, "RemoveExtender", str3, category, true));
16: }
17: 返品品。
18: }
19:
ここで、「拡張機能の追加」アクションが Visual Studio の Web フォーム デザイナーにハードコーディングされていることが明らかです。.net フレームワークには、同様のアクションを追加するための対応するインターフェイスが提供されていません。ただし、私が望む効果は「アクションの追加」アクションを追加することなので、それを実現するために AjaxControlToolkit メソッドを参照することはできません。他のメソッドを探す必要があります。
戻って Microsoft.Web.Design.WebFormDesigner+WebDesignerActionService クラスの GetComponentActions メソッドを再検査し、次のように基本クラス System.Web.UI.Design.WebFormsDesignerActionService の定義を見つけます (System.Design アセンブリの下にあります)。
1: 保護されたオーバーライド void GetComponentDesignerActions(IComponent コンポーネント, DesignerActionListCollection actionLists)
2: {
3: if (コンポーネント == null)
4: {
5: 新しい ArgumentNullException("コンポーネント") をスローします。
6: }
7: if (actionLists == null)
8: {
9: 新しい ArgumentNullException("actionLists") をスローします。
10: }
11: IServiceContainer サイト = IServiceContainer としてのコンポーネント.サイト;
12: if (サイト != null)
13: {
14: DesignerCommandSet サービス = (DesignerCommandSet) site.GetService(typeof(DesignerCommandSet));
15: if (サービス != null)
16: {
17: DesignerActionListCollection リスト = service.ActionLists;
18: if (リスト != null)
19: {
20: actionLists.AddRange(リスト);
21: }
22: }
23: if ((actionLists.Count == 0) || ((actionLists.Count == 1) && (actionLists[0] は ControlDesigner.ControlDesignerActionList)))
24: {
25: DesignerVerbCollection 動詞 = service.Verbs;
26: if ((verbs != null) && (verbs.Count != 0))
27: {
28: DesignerVerb[] 配列 = 新しい DesignerVerb[verbs.Count];
29: 動詞.CopyTo(配列, 0);
30: actionLists.Add(new DesignerActionVerbList(array));
31: }
32: }
33: }
34: }
35:
36:
37:
38:
上記のコードを調べると、DesignerActionListCollection が DesignerCommandSet サービスの ActionLists 属性によって返され、別の DesignerCommandSet を作成し、DesignerCommandSet がコンポーネントから取り出されていることを確認する限り、このサービスはコンポーネントのサイトから取得されることがわかります。このサイトは私のものです。ようやくエントリーポイントを見つけたので、具体的な方法を紹介します。
まず、次のように DesignerCommandSet を継承するクラスを作成します。
1: パブリック クラス MyDesignerCommandSet : DesignerCommandSet
2: {
3: プライベート ComponentDesigner _componentDesigner;
4:
5: public MyDesignerCommandSet(コンポーネントデザイナーコンポーネントデザイナー)
6: {
7: _componentDesigner = コンポーネントデザイナー;
8: }
9:
10: パブリック オーバーライド ICollection GetCommands(文字列名)
11: {
12: if (name.Equals("ActionLists"))
13: {
14: GetActionLists() を返します。
15: }
16:base.GetCommands(名前)を返します。
17: }
18:
19: プライベート DesignerActionListCollection GetActionLists()
20: {
21: //最初にコントロールの元の DesignerActionList を取得します
22: DesignerActionListCollection リスト = _componentDesigner.ActionLists;
23:
24: //「アクションの追加」DesignerActionList を追加します
25: lists.Add(new ActionList(_componentDesigner));
26: リストを返します。
27: }
28:
29: 内部クラス ActionList : DesignerActionList
30: {
31: プライベート DesignerActionItemCollection _actions;
32:
33: public ActionList(IDesignerデザイナー)
34: : ベース(デザイナー.コンポーネント)
35: {
36: }
37: パブリック オーバーライド DesignerActionItemCollection GetSortedActionItems()
38: {
39: if (_actions == null)
40: {
41: const string actionCategory = "アクション";
42: _actions = 新しい DesignerActionItemCollection();
43: _actions.Add(new DesignerActionMethodItem(this, "AddAction", "アクションの追加...", actionCategory, true));
44: }
45: アクションを返す;
46: }
47:
48: パブリック void AddAction()
49: {
50: //アクションロジックを追加(省略)
51: }
52: }
53: }
次のステップは、コンポーネントのサイト ServiceProvider が独自のサービスを返すようにする方法です。自分でSiteを書き、コンポーネントのSiteを自分が書いたSIteクラスのオブジェクトにする方法です。
私が書いた Site クラスの定義は次のとおりです。
1: パブリック クラス SiteProxy : ISite、IServiceContainer
2: {
3: プライベート ISite_site;
4: プライベート ComponentDesigner _designer;
5:
6: パブリック SiteProxy(ISite サイト、ComponentDesigner デザイナ)
7: {
8: _site = サイト;
9: _designer = デザイナー;
10:
11: }
12:
13: #region ISite メンバー
14:
15: パブリック IComponentComponent
16: {
17: { return _site.Component } を取得します。
18: }
19:
20: パブリック System.ComponentModel.IContainer コンテナ
21: {
22: { return _site.Container } を取得します。
23: }
24:
25: public boolDesignMode
26: {
27: get { return _site.DesignMode }
28: }
29:
30: パブリック文字列名
31: {
32: { return _site.Name } を取得します。
33: { _site.Name = 値 } を設定します。
34: }
35:
36: #エンドリージョン
37:
38: #region IServiceProvider メンバー
39:
40: パブリック オブジェクト GetService(Type serviceType)
41: {
42: オブジェクト サービス = _site.GetService(serviceType);
43:
44: if (serviceType == typeof(DesignerCommandSet) && !(_designer.Component は ExtenderControl です))
45: {
46: if (サービス == null || !(サービスは MyDesignerCommandSet))
47: {
48: if (サービス != null)
49: {
50: RemoveService(typeof(DesignerCommandSet));
51: }
52: //自分で書いたDesignerCommandSetを返す
53: サービス = 新しい MyDesignerCommandSet(_designer);
54: AddService(typeof(DesignerCommandSet), サービス);
55: }
56: }
57: リターンサービス。
58: }
59:
60: #エンドリージョン
61:
62: #region IServiceContainer メンバー
63:
64: public void AddService(Type serviceType, ServiceCreatorCallback コールバック, bool プロモート)
65: {
66: (IServiceContainer としての _site).AddService(serviceType、コールバック、プロモート);
67: }
68:
69: public void AddService(Type serviceType, ServiceCreatorCallback コールバック)
70: {
71: (_site as IServiceContainer).AddService(serviceType, callback);
72: }
73:
74: public void AddService(Type serviceType, object serviceInstance, bool promote)
75: {
76: (_site as IServiceContainer).AddService(serviceType, serviceInstance, promote);
77: }
78:
79: public void AddService(タイプ serviceType, オブジェクト serviceInstance)
80: {
81: (_site として IServiceContainer).AddService(serviceType, serviceInstance);
82: }
83:
84: public void RemoveService(Type serviceType, bool promote)
85: {
86: (_site as IServiceContainer).RemoveService(serviceType, promote);
87: }
88:
89: public void RemoveService(Type serviceType)
90: {
91: (_site として IServiceContainer).RemoveService(serviceType);
92: }
93:
94: #エンドリージョン
95: }
このサイトの GetService メソッドで、取得するサービスの種類を決定し、DesignerCommandSet の場合は、作成した MyDesignerCommandSet を返します。
次のステップは、コンポーネントの Site を自分で作成した SiteProxy に変更する方法です。 1 つの方法は、カスタム コントロールを追加し、コントロールの ControlDesigner の Initialize メソッドでコンテナ内の他のコントロールのサイトを変更することです。他のコントロールのサイトを変更するには、コントロールを Web フォームにドラッグするだけです。この方法は、vs パッケージを作成し、パッケージ内の Web フォーム デザイナの対応するイベントをキャプチャすることです。最初のアプローチを以下に紹介します。
Control から継承した ActionManager という新しいコントロールを追加します。このコントロールには関数を追加する必要はありません。必要なのは、ControlDesigner を作成することだけです。 ControlDesigner クラスのメイン コードは次のとおりです。
1: パブリック クラス ActionManagerDesigner : ControlDesigner
2: {
3: プライベート IDesignerHost _host;
4: プライベート IDictionary<IComponent, ISite> _components;
5:
6: public オーバーライド void Initialize(IComponent コンポーネント)
7: {
8:base.Initialize(コンポーネント);
9:
10: _components = 新しい Dictionary<IComponent, ISite>();
11:
12: _host = GetService(typeof(IDesignerHost)) as IDesignerHost;
13: if (_host != null)
14: {
15: //サイトを既存のコントロールに置き換えます
16: プロセスコンポーネント();
17:
18: IComponentChangeService サービス =
19: _host.GetService(typeof(IComponentChangeService)) IComponentChangeService として;
20: if (サービス != null)
21: {
22:service.ComponentAdded += ComponentAdded;
23:service.ComponentRemoving += ComponentRemoving;
24: }
25: }
26: }
27:
28: #regionプロセスコンポーネント
29:
30: プライベート void ProcessComponent()
31: {
32: ComponentCollection コンポーネント = _host.Container.Components;
33: foreach (コンポーネント内の IComponent コンポーネント)
34: {
35: if (コンポーネントは ActionControl です)
36: 続けます。
37: プロセスコンポーネントサイト(コンポーネント);
38: }
39: }
40:
41: #エンドリージョン
42:
43: #region 置換サイト
44:
45: /// <概要>
46: /// コンポーネントの元のサイトを SiteProxy に置き換えます
47: /// </まとめ>
48: private void ProcessComponentSite(IComponent コンポーネント)
49: {
50: ComponentDesigner デザイナー = _host.GetDesigner(component) as ComponentDesigner;
51: _components[コンポーネント] = コンポーネント.サイト;
52:component.Site = 新しい SiteProxy(component.Site, デザイナー);
53: }
54:
55: /// <概要>
56: /// コンポーネントの元のサイトを復元します
57: /// </まとめ>
58: /// <param name="コンポーネント"></param>
59: private void RestoreComponentSite(IComponent コンポーネント)
60: {
61: if (_components.ContainsKey(components))
62: {
63: ISite サイト = _components[コンポーネント];
64: コンポーネント.サイト = サイト;
65: _components.削除(コンポーネント);
66: }
67: }
68:
69: #エンドリージョン
70:
71: コンポーネントの #region 追加、削除、変更
72:
73: private void ComponentRemoving(オブジェクト送信者, ComponentEventArgs e)
74: {
75: if (e.Component が ActionControl である)
76: {
77: 戻る。
78: }
79: //コンポーネントを削除するときは、その Site プロパティを復元する必要があります。復元しないと、元の Site が DesignerHost に残ります。
80: //この方法で同じ名前のコンポーネントを追加すると、「コンポーネント名が重複しています」エラーが報告されます。
81: RestoreComponentSite(e.Component);
82: }
83:
84:
85: private void ComponentAdded(オブジェクト送信者, ComponentEventArgs e)
86: {
87: if (e.Component が ActionControl である)
88: {
89: 戻る。
90: }
91: ProcessComponentSite(e.Component);
92: }
93:
94: #エンドリージョン
95:
96: #リージョン破棄
97:
98: 保護されたオーバーライド void Dispose(bool 破棄)
99: {
100: if (_host != null)
101: {
102: IComponentChangeService サービス =
103: _host.GetService(typeof(IComponentChangeService)) IComponentChangeService として;
104: if (サービス != null)
105: {
106: サービス.コンポーネント追加 -= コンポーネント追加;
107:service.ComponentRemoving -= ComponentRemoving;
108: }
109: }
110:base.Dispose(処分);
111: }
112:
113: #エンドリージョン
114: }
この時点で、ActionManager コントロールを Web フォーム デザイナーにドラッグすると、他のコントロールのスマート タスク パネルに [アクションの追加...] リンクが表示されます。ただし、この方法では、Web フォーム デザイナーに追加のコントロールを配置する必要があります。このコントロールはデザイン時にのみ役立ち、実行時には役に立たないため、最善のアプローチは 2 番目のアプローチであり、vs. パッケージを開発することです。パッケージの Initialize メソッドで、IDesignerEventService の DesignerCreated イベントを登録し、IDesignerHost と IComponentChangeService を通じてコントロール サイトを変更するという目的を達成します。具体的な実装は上記と同様なので、これ以上は書きません。