C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[11]: ソリューションの中にあるコントロールの一覧を取得したい


(過去ログ 116 を表示中)

[トピック内 14 記事 (1 - 14 表示)]  << 0 >>

■68386 / inTopicNo.1)  ソリューションの中にあるコントロールの一覧を取得したい
  
□投稿者/ morimori (32回)-(2013/10/18(Fri) 09:02:29)

分類:[C#] 

開発環境
Windows7 Home
使用言語
2010 C# Professional

morimoriと申します。
ソリューションの中にあるコントロールの一覧を取得するために、
ソリューション→プロジェクト→フォーム→コントロールの順に
アクセスして一覧を取得しています。

フォームからコントロール一覧を取得する方法はわかったのですが、
上位のアプリケーション→プロジェクト→フォームの取得方法がわかりません。
コントロールは以下のような方法で取得しております。

List<Control> list_Ctrl = new List<Control>();

foreach (Control c in top.Controls)
{
list_Ctrl.Add(c);
list_Ctrl.AddRange(GetAllControls(c));
}
return list_Ctrl;

このようにListにアプリケーション,プロジェクト,フォームの一覧を取得したいと思います。
ご存知の方がいらっしゃれば教えていただければと思います。
それでは失礼します。

引用返信 編集キー/
■68387 / inTopicNo.2)  Re[1]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ 魔界の仮面弁士 (379回)-(2013/10/18(Fri) 09:33:05)
No68386 (morimori さん) に返信
> ソリューションの中にあるコントロールの一覧を取得するために、
フォーム上では無く、ソリューション上ということは、
開発環境(Visual Studio)向けのアドインでしょうか?

> ソリューション→プロジェクト→フォーム→コントロールの順に
> アクセスして一覧を取得しています。
何のために必要なのかよく分かりませんが、たとえば

 Solution1
 ├Project1.DLL …「TextBox を継承した MoriTextBox」と「Label を継承した MoriLabel」を公開
 ├Project2.DLL …「MoriTextBox を継承した MoriMoriTextBox」を公開
 ├Project3.DLL …「UserControl に MoriMoriTextBox1 と MoriLabel1 を載せた MoriMoriBox コントロール」を公開
 └WindowsApp.EXE … Form1 上に Button1 と MoriMoriBox1 を貼り付け

のような場合、「コントロールの一覧」に必要なのはどのコントロールなのでしょうか?
引用返信 編集キー/
■68388 / inTopicNo.3)  Re[2]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ morimori (33回)-(2013/10/18(Fri) 09:52:53)
No68386 (morimori さん) に返信
> ソリューションの中にあるコントロールの一覧を取得するために、
いえ、フォーム上で使っているコントロールを取得したいのです。
なのでソリューション上にあるフォームの一覧を取得するために
このようなことがしたいのです。

>何のために必要なのかよく分かりませんが
たとえば、コントロールの内容が変更されたときに変更イベントを発生させたりするソースを組む際に、
どのような種類のコントロールを使っているのかを調べる必要があります。

調べたコントロールの種類に対して、どのイベントが発生したときに変更イベントを発生させたりするのか
を検討するためにつかおうと考えております。
一つ一つコントロールを監視するのは大変ですから・・・。

また、フォーム上で使っている日本語の翻訳を作る際にも、
どのコントロールでどんな日本語を使っているのか。また、どのコントロールのどんなプロパティのデータを参照すればいいのか
という一覧を作成するときにも大変役立つと思います。

今回は後者の理由ですが、フォームのコントロールのデータを応用していろいろなことに汎用的に使おうと考えております。

引用返信 編集キー/
■68390 / inTopicNo.4)  Re[3]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ 魔界の仮面弁士 (380回)-(2013/10/18(Fri) 12:25:39)
No68388 (morimori さん) に返信
> いえ、フォーム上で使っているコントロールを取得したいのです。

列挙対象とするのは、自ソリューションの Form 上で使われているものだけなのですね?

たとえば、UserControl や 継承コントロールを公開しているプロジェクトがあったとします。
それらのコントロールは、他のソリューションで利用するためのものであって、
自ソリューション内のフォームからは一切使われていないような場合、
そのコントロールは、列挙対象に含めない、と。

逆に、Form 上で利用しているコントロールであれば、それは
自ソリューションで公開されたものであろうと、
他ソリューションで公開されたものであろうと、列挙したい、と。


>> コントロールは以下のような方法で取得しております。

操作に応じてコントロールの数が動的に増減するようなもの(タブブラウザ型のアプリなど)は、
どの時点で列挙したかによっても、結果が変わってきそうですが、その点は問題ないでしょうか。

また、この手の手法では、複合コントロールの内包コントロールも
列挙されることになりますが、その点は問題ないでしょうか。

たとえば、先の UserControl を使っていた場合は、ラベルとテキストボックスが
列挙されることになりますし、標準的なコントロールにおいても、
DomainUpDown コントロールは、
 ├UpDownEdit
 └UpDownButtons
という内部構成になっていますし、DataGridView なら水平/垂直スクロールバーや
EditingControl が取り出されたりします。


> List<Control> list_Ctrl = new List<Control>();
ループ処理を廃した別案実装。内容的には同じですが。


private void button1_Click(object sender, EventArgs e)
{
 dataGridView1.DataSource = GetAllControls(調査対象).ToArray();
}

public IEnumerable<Control> GetAllControls(Control container)
{
 var controls = container.Controls.Cast<Control>();
 var children = controls.Select(c => GetAllControls(c));
 return controls.Concat(children.SelectMany(c => c));
}



> なのでソリューション上にあるフォームの一覧を取得するために
別ソリューションで定義されたフォーム(PrintPreviewDialog など)を利用している場合や、
public では無いフォーム(「private class InternalForm : Form {}」など)の取り扱いを
どうするかといった問題はありますが、方針の一つとして、Reflection を用いて
「Form を継承したクラス」を列挙するという考え方があるかと思います。
引用返信 編集キー/
■68391 / inTopicNo.5)  Re[3]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ PANG2 (13回)-(2013/10/18(Fri) 12:28:52)
2013/10/18(Fri) 13:02:23 編集(投稿者)

ソリューションではなくアセンブリ単位だけど

using System.Windows.Forms;
using System.Reflection;
using System.Diagnostics;


Assembly asm = Assembly.LoadFrom(fileName); //DLLまたはEXEのフルパス

foreach (Type t in asm.GetTypes()) {
if (!t.IsSubclassOf(typeof(Form)))
continue;
Debug.WriteLine(t.FullName);
foreach (FieldInfo f in t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
if (f.FieldType.IsSubclassOf(typeof(Control)))
Debug.WriteLine("\t" + f.Name + "\t" + f.FieldType.Name);
}
}

// ToDo : asm.GetReferencedAssemblies()で再帰
引用返信 編集キー/
■68392 / inTopicNo.6)  Re[4]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ morimori (34回)-(2013/10/18(Fri) 13:25:42)
皆様回答ありがとうございます。

>魔界の仮面弁士さん
>列挙対象とするのは、自ソリューションの Form 上で使われているものだけなのですね?
はいそうです。コントロールでネストされているサブコントロールも調査対象です。
コントロールの取得のソースありがとうございます。
参考にさせていただきます。

>PANG2さん
ソースありがとうございます。助かります。
途中でFormデータを処理しているところがありますが、
あそこでFormのインスタンスを作ることはできるでしょうか。
Formのインスタンスも別に取得しておいた方がいいと思ったのでお尋ねしました。

foreach (Type t in asm.GetTypes()) {
if (!t.IsSubclassOf(typeof(Form)))
continue;
Debug.WriteLine(t.FullName);

おそらくここのtのデータをキャストすればいいと思うんですけど、
どうすればいいでしょう。
引用返信 編集キー/
■68393 / inTopicNo.7)  Re[5]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ PANG2 (14回)-(2013/10/18(Fri) 13:50:24)
> あそこでFormのインスタンスを作ることはできるでしょうか。

Form form = (Form)Activator.CreateInstance(t);

form側のコンストラクタでInitializeComponentメソッドが呼ばれ、コントロールのインスタンスが生成されます。
引用返信 編集キー/
■68395 / inTopicNo.8)  Re[5]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ 魔界の仮面弁士 (381回)-(2013/10/18(Fri) 14:02:55)
No68392 (morimori さん) に返信
>> PANG2さん
> ソースありがとうございます。助かります。
このコードの場合、デザイン時に GenerateMember = False に設定されたコントロールは
列挙対象になりません(結果として、UserControl 内のコントロールは列挙されません)。

その点が、Controls から辿る場合との違いですね。


> あそこでFormのインスタンスを作ることはできるでしょうか。
> Formのインスタンスも別に取得しておいた方がいいと思ったのでお尋ねしました。

Type さえ得られているのであれば作れますよ。

厳密にいえば、複数のコンストラクタを持つ Form などにおいては、
どのコンストラクタで生成するのかを考慮する必要がありますが、
たとえば引数無しコンストラクタ限定ならばこんな感じ。


if(!t.ContainsGenericParameters)
{
 using (dynamic instance = t.GetConstructor(Type.EmptyTypes).Invoke(null))
 {
  // instance.ShowDialog();
 }
}

/*
** using (dynamic instance = Activator.CreateInstance(t)) でも可。
*/


ちなみに ContainsGenericParameters プロパティを使っているのは、
 public class F<T> : Form {}
のような、ジェネリックなフォームを除外するための物です。

もし、ジェネリックフォームのインスタンスも作成したい場合には、
 Type t2 = t.MakeGenericType( 型パラメータ );
で生成した Type から作成してみてください。
引用返信 編集キー/
■68396 / inTopicNo.9)  Re[6]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ morimori (35回)-(2013/10/18(Fri) 14:03:39)
ありがとうございます。
これでアセンブリ取得用のファイルパスさえわかれば
ファイル名 → アセンブリ → フォーム → コントロール
の順にアクセスすることができます。

動作が達成できそうなのでこれで解決済みにしたいと思います。
皆様ありがとうございます。


解決済み
引用返信 編集キー/
■68397 / inTopicNo.10)  Re[7]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ morimori (36回)-(2013/10/18(Fri) 16:12:59)
すみません、このようなソースを作ったのですがフォームを取得できませんでした。
何か間違えているのでしょうか。

public static List<Form> GetForms(string strExePath)
{
List<Form> listForm = new List<Form>();
Assembly asm = Assembly.LoadFrom(strExePath);
GetForms(asm);
return listForm;
}

public static List<Form> GetForms(Assembly asm)
{
List<Form> listForm = new List<Form>();
foreach (Type t in asm.GetTypes())
{
if (!t.IsSubclassOf(typeof(Form)))
continue;

Form form = (Form)Activator.CreateInstance(t);
listForm.Add(form);
}

List<AssemblyName> assemblyName = asm.GetReferencedAssemblies().ToList();
foreach (AssemblyName asmName in assemblyName)
{
listForm.AddRange(GetForms(Assembly.Load(asmName)));
}

return listForm;
}
引用返信 編集キー/
■68398 / inTopicNo.11)  Re[8]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ 魔界の仮面弁士 (382回)-(2013/10/18(Fri) 16:47:38)
No68397 (morimori さん) に返信
> public static List<Form> GetForms(string strExePath)
> {
>  List<Form> listForm = new List<Form>();
>  Assembly asm = Assembly.LoadFrom(strExePath);
>  GetForms(asm);
>  return listForm;
> }

『return GetForms(asm);』ではなく?
引用返信 編集キー/
■68399 / inTopicNo.12)  Re[9]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ morimori (37回)-(2013/10/18(Fri) 17:03:08)
修正しました。でも、まだ取得できません。
どうしてでしょう・・・。

public static List<Form> GetForms(string strExePath)
{
Assembly asm = Assembly.LoadFrom(strExePath);
return GetForms(asm);
}

public static List<Form> GetForms(Assembly asm)
{
List<Form> listForm = new List<Form>();
foreach (Type t in asm.GetTypes())
{
if (!t.IsSubclassOf(typeof(Form)))
continue;

Form form = (Form)Activator.CreateInstance(t);
listForm.Add(form);
}

List<AssemblyName> assemblyName = asm.GetReferencedAssemblies().ToList();
foreach (AssemblyName asmName in assemblyName)
{
listForm.AddRange(GetForms(Assembly.Load(asmName)));
}

return listForm;
}
引用返信 編集キー/
■68400 / inTopicNo.13)  Re[10]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ 魔界の仮面弁士 (383回)-(2013/10/18(Fri) 17:39:30)
2013/10/18(Fri) 18:14:15 編集(投稿者)

No68399 (morimori さん) に返信
> 修正しました。
インスタンスを取得するのは良いですが、
Form (というか Control)は IDispsable なので
使用後は確実に Dispose するようにしましょう。


> でも、まだ取得できません。
取得できない、とは、具体的にどういう状態なのでしょうか?

返されるはずの Form が返されないのか、
予定していない Form が返されるのか、
幾ら待っても応答が返ってこない(無限再帰?)のか、
例外が発生してしまうのか、もう少し細かく教えてください。

また、例外が発生する場合は、何と言うエラーが
発生するのか(StackOverflowExceptionなど)と、
それが form 生成時のエラーなら、その時の Type は
何であるのかなどを調査してみてください。


> どうしてでしょう・・・。
たとえば、その Form が抽象クラスであった場合、
インスタンスを直接生成するわけにはいかないですよね。
また No68395 にも少し書きましたが、すべての Form が
引数なしの public コンストラクタを持っているわけではありません。

自作アプリでは、そうした Form を作る機会は少ないかもしれませんが、
System.Design.DLL をはじめとして、.NET 側の標準ライブラリ群に
おいては、そうした実装が幾つも含まれています。具体例を挙げてみると
 Microsoft.VisualBasic.DLL の Microsoft.VisualBasic.CompilerServices.VBInputBox クラス
 System.Windows.Forms.DLL の System.Windows.Forms.Design.ComponentEditorForm クラス
などがあるようです。

なので GetReferencedAssemblies を使うなら、そこから返される
アセンブリが、自身のソリューションのものなのか確認するとか、
生成可能なフォームなのかインスタンス生成前に型情報を確認するとか、
例外処理を組み込むなどの対処を含めてみては如何でしょうか。


それともう一点。現状のコードだと、たとえば参照設定が

Application.EXE

├Class1.DLL
│└SharedInterface.DLL

├Class2.DLL
│└SharedInterface.DLL


のような形で行われていた場合、EXE を GetForms に渡すと、
SharedInterface.DLL が複数回列挙されることになると
思いますので、その点も考慮してみてください。
引用返信 編集キー/
■68425 / inTopicNo.14)  Re[11]: ソリューションの中にあるコントロールの一覧を取得したい
□投稿者/ morimori (38回)-(2013/10/21(Mon) 09:32:13)
すみません、原因が分かりました。
パブリックでデータを渡さないとエラーになるフォームがあり、
そこで例外を起こしておりました。

フォームのインスタンスを無理に作ろうとすると他にも問題が起こりそうなので、フォームのインスタンスは作成せずに、
フォーム名を取って特定のコントロールへアクセスすることにします。
みなさまありがとうございます。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -