2009-05-29 9 views
3

グリッドがあり、DataSourceList<IListItem>に設定しています。私が望むのは、リストを基になる型にバインドし、IListItemで定義されたプロパティではなく、それらのプロパティを表示することです。だから、:私のList<IListItem>は、ユーザーが含まれている場合、私は、ユーザー固有のフィールドが表示されるようにC#gridデータソース多型

public interface IListItem 
{ 
    string Id; 
    string Name; 
} 

public class User : IListItem 
{ 
    string Id { get; set; }; 
    string Name { get; set; }; 
    string UserSpecificField { get; set; }; 
} 

public class Location : IListItem 
{ 
    string Id { get; set; }; 
    string Name { get; set; }; 
    string LocationSpecificField { get; set; }; 
} 

はどのようにして、グリッドにバインドしていますか?編集:データグリッドにバインドしたい任意のリストは、単一の基本型で構成されることに注意してください。

+0

データテーブルに変換して戻ってきましたか? –

答えて

5

データバインディングリストには、次の戦略は、次のとおりです。

  1. は、データ・ソースがIListSourceを実装していますか?その場合は、GetList()
  2. の結果を持つgoto 2は、IListを実装していますか?そうでない場合は、エラーを投げる。リストが必要です
  3. データソースはITypedListを実装していますか?データソースに非オブジェクトインデクサーpublic Foo this[int index](一部の場合はFoo)が含まれていますか?
  4. もしそうなら、をメタデータに使用してください。
  5. リストには何かがありますか?そしてそれは、を介してメタデータを取得する - もしそうなら、それはタイプIListItemの型付きインデクサを有しているので

List<IListItem> 利用可能なメタデータは、上記の「4」に該当しないメタデータ

  • ための最初の項目(list[0])を使用しTypeDescriptor.GetProperties(typeof(IListItem))

    はだから今、次の3つのオプションがあります。

    • IListItemのプロパティを返しますTypeDescriptionProviderを書く - あなたはおそらくコンクリートの型がちょうど与えているかを知ることができないので、私はこれが実行可能であるかわかりませんIListItem
    • 正しく入力リスト(List<User>など)を使用する - 単に非オブジェクトのインデクサとIList
    • は(仕事の多くを)ITypedListラッパーを書く得るための簡単な方法として
    • は、ArrayList(つまり、公開非オブジェクトインデクサーなし) - 非常にハッキー!

    私の好みはここにタイプ(サンプルの使用と)知らなくても、あなたののためにこれを行いAutoCast方法だ... List<>の正しいタイプを使用するためです。これが唯一の均質なデータのために働くことを

    注(すなわち、すべてのオブジェクトが同じである)、そしてそれは

    // infers the correct list type from the contents 
    static IList AutoCast(this IList list) { 
        if (list == null) throw new ArgumentNullException("list"); 
        if (list.Count == 0) throw new InvalidOperationException(
          "Cannot AutoCast an empty list"); 
        Type type = list[0].GetType(); 
        IList result = (IList) Activator.CreateInstance(typeof(List<>) 
          .MakeGenericType(type), list.Count); 
        foreach (object obj in list) result.Add(obj); 
        return result; 
    } 
    // usage 
    [STAThread] 
    static void Main() { 
        Application.EnableVisualStyles(); 
        List<IListItem> data = new List<IListItem> { 
         new User { Id = "1", Name = "abc", UserSpecificField = "def"}, 
         new User { Id = "2", Name = "ghi", UserSpecificField = "jkl"}, 
        }; 
        ShowData(data, "Before change - no UserSpecifiedField"); 
        ShowData(data.AutoCast(), "After change - has UserSpecifiedField"); 
    } 
    static void ShowData(object dataSource, string caption) { 
        Application.Run(new Form { 
         Text = caption, 
         Controls = { 
          new DataGridView { 
           Dock = DockStyle.Fill, 
           DataSource = dataSource, 
           AllowUserToAddRows = false, 
           AllowUserToDeleteRows = false 
          } 
         } 
        }); 
    } 
    
  • +1

    上記の私のソリューションでこれを試し、BindingListViewを削除してdataGridView1.DataSource = items.AutoCast();を設定するだけです。完璧に動作します。 –

    +0

    グリッドの並べ替えを許可していないようですが、それはおそらく私が紛失しているものです。 –

    +0

    グリッドでソートするには、IBindingListと適切なソート実装が必要です。これはメインランタイムでは提供されていませんが、BindingList (通常はいくつかのメソッドをオーバーライドします)から派生した、数多くの実装が容易に利用できます。あなたが1つを追跡するのに問題がある場合は教えてください。私はあなたのために1つを提供します... –

    0

    これにはグリッドテンプレート列を使用する必要があります。テンプレートフィールドの中では、オブジェクトのタイプを確認してから正しいプロパティを取得する必要があります。これを処理するコードビハインドでメソッドを作成することをお勧めします。したがって:

    <asp:TemplateField HeaderText="PolymorphicField"> 
        <ItemTemplate> 
         <%#GetUserSpecificProperty(Container.DataItem)%> 
        </ItemTemplate> 
    </asp:TemplateField> 
    

    であなたのコードビハインド:

    protected string GetUserSpecificProperty(IListItem obj) { 
        if (obj is User) { 
         return ((User) obj).UserSpecificField 
        } else if (obj is Location) { 
         return ((Location obj).LocationSpecificField; 
        } else { 
         return ""; 
        } 
    } 
    
    +0

    私は実際にwinformsを使用していますが、それは問題ありません。私の目標は、私の見解を特定のタイプに依存しないようにすることです。 – Robert

    +0

    TemplateFieldはwinformsに存在しますか?ビューを特定のタイプに依存させない場合は、なぜタイプ固有のフィールドを表示したいのですか? これに代わる方法は、インスタンスのタイプに応じて特定のフィールドを出力するメソッドを持つユーザーと場所用の基本クラスを作成することです。 –

    +0

    私は、ユーザーに一連のものを提示し、それらを選択させたいと思います。私は、私の見解を変えずに、これらのことがどのようなものになるかを加えたいと思っています。私が試した最初のことは、IListItemにstring [] GetFieldsForDisplay()を追加することでしたが、文字列[]をグリッドの行にバインドする方法はありますか?そして、私はヘッダーを持っていません... – Robert

    0

    私は予測を試みたが、私は基本的なタイプのリストを取得するにはConvert.ChangeTypeを使用してみましたが、データグリッドが表示されませんでしたフィールド。私は最終的に、ヘッダー、表示フィールドを(文字列のリストとして)返すインスタンスメソッドを静的メソッドを作成し、それらをDataTableにまとめてバインドすることに決めました。合理的にきれいにして、データ型とディスプレイの間に必要な分離を維持します。ここで

    は、私は、テーブルを作成するために使用するコードです:

    DataTable GetConflictTable() 
        { 
         Type type = _conflictEnumerator.Current[0].GetType(); 
         List<string> headers = null; 
         foreach (var mi in type.GetMethods(BindingFlags.Static | BindingFlags.Public)) 
         { 
          if (mi.Name == "GetHeaders") 
          { 
           headers = mi.Invoke(null, null) as List<string>; 
           break; 
          } 
         } 
         var table = new DataTable(); 
         if (headers != null) 
         { 
          foreach (var h in headers) 
          { 
           table.Columns.Add(h); 
          } 
          foreach (var c in _conflictEnumerator.Current) 
          { 
           table.Rows.Add(c.GetFieldsForDisplay()); 
          } 
         } 
         return table; 
        } 
    
    +0

    あなたはこれ以上の回答を望まないということですか? – ichiban

    +0

    いいえ、私はこれを回避策と考えていますが、もともと私がやろうとしていたものに対する実際の解決策ではありません。 – Robert

    1

    限り、あなたはリスト<のメンバーIListItem >はすべてここだ、その後、同じ派生型であることを行っていることを確かに知ってどうすればいいのか、「自分のマシン上の作品」の承認を受けることができます。

    まず、BindingListViewをダウンロードします。これにより、汎用リストをDataGridViewにバインドできます。

    この例では、DataGridViewを使用して単純なフォームを作成し、Form1_Load()でユーザーまたは場所のリストをロードするコードをランダムに呼び出すようにしました。コードの

    using System; 
    using System.Collections.Generic; 
    using System.Drawing; 
    using System.Windows.Forms; 
    using Equin.ApplicationFramework; 
    
    namespace DGVTest 
    { 
        public interface IListItem 
        { 
         string Id { get; } 
         string Name { get; } 
        } 
    
        public class User : IListItem 
        { 
         public string UserSpecificField { get; set; } 
         public string Id { get; set; } 
         public string Name { get; set; } 
        } 
    
        public class Location : IListItem 
        { 
         public string LocationSpecificField { get; set; } 
         public string Id { get; set; } 
         public string Name { get; set; } 
        } 
    
        public partial class Form1 : Form 
        { 
         public Form1() 
         { 
          InitializeComponent(); 
         } 
    
         private void InitColumns(bool useUsers) 
         { 
          if (dataGridView1.ColumnCount > 0) 
          { 
           return; 
          } 
    
          DataGridViewCellStyle gridViewCellStyle = new DataGridViewCellStyle(); 
    
          DataGridViewTextBoxColumn IDColumn = new DataGridViewTextBoxColumn(); 
          DataGridViewTextBoxColumn NameColumn = new DataGridViewTextBoxColumn(); 
          DataGridViewTextBoxColumn DerivedSpecificColumn = new DataGridViewTextBoxColumn(); 
    
          IDColumn.DataPropertyName = "ID"; 
          IDColumn.HeaderText = "ID"; 
          IDColumn.Name = "IDColumn"; 
    
          NameColumn.DataPropertyName = "Name"; 
          NameColumn.HeaderText = "Name"; 
          NameColumn.Name = "NameColumn"; 
    
          DerivedSpecificColumn.DataPropertyName = useUsers ? "UserSpecificField" : "LocationSpecificField"; 
          DerivedSpecificColumn.HeaderText = "Derived Specific"; 
          DerivedSpecificColumn.Name = "DerivedSpecificColumn"; 
    
          dataGridView1.Columns.AddRange(
           new DataGridViewColumn[] 
            { 
             IDColumn, 
             NameColumn, 
             DerivedSpecificColumn 
            }); 
    
          gridViewCellStyle.SelectionBackColor = Color.LightGray; 
          gridViewCellStyle.SelectionForeColor = Color.Black; 
          dataGridView1.RowsDefaultCellStyle = gridViewCellStyle; 
         } 
    
         public static void BindGenericList<T>(DataGridView gridView, List<T> list) 
         { 
          gridView.DataSource = new BindingListView<T>(list); 
         } 
    
         private void Form1_Load(object sender, EventArgs e) 
         { 
          dataGridView1.AutoGenerateColumns = false; 
    
          Random rand = new Random(); 
    
          bool useUsers = rand.Next(0, 2) == 0; 
    
          InitColumns(useUsers); 
    
          if(useUsers) 
          { 
           TestUsers(); 
          } 
          else 
          { 
           TestLocations(); 
          } 
    
         } 
    
         private void TestUsers() 
         { 
          List<IListItem> items = 
           new List<IListItem> 
            { 
             new User {Id = "1", Name = "User1", UserSpecificField = "Test User 1"}, 
             new User {Id = "2", Name = "User2", UserSpecificField = "Test User 2"}, 
             new User {Id = "3", Name = "User3", UserSpecificField = "Test User 3"}, 
             new User {Id = "4", Name = "User4", UserSpecificField = "Test User 4"} 
            }; 
    
    
          BindGenericList(dataGridView1, items.ConvertAll(item => (User)item)); 
         } 
    
         private void TestLocations() 
         { 
          List<IListItem> items = 
           new List<IListItem> 
            { 
             new Location {Id = "1", Name = "Location1", LocationSpecificField = "Test Location 1"}, 
             new Location {Id = "2", Name = "Location2", LocationSpecificField = "Test Location 2"}, 
             new Location {Id = "3", Name = "Location3", LocationSpecificField = "Test Location 3"}, 
             new Location {Id = "4", Name = "Location4", LocationSpecificField = "Test Location 4"} 
            }; 
    
    
          BindGenericList(dataGridView1, items.ConvertAll(item => (Location)item)); 
         } 
        } 
    } 
    

    重要な行は、これらは以下のとおりです。

    DerivedSpecificColumn.DataPropertyName = useUsers ? "UserSpecificField" : "LocationSpecificField"; // obviously need to bind to the derived field 
    
    public static void BindGenericList<T>(DataGridView gridView, List<T> list) 
    { 
        gridView.DataSource = new BindingListView<T>(list); 
    } 
    
    dataGridView1.AutoGenerateColumns = false; // Be specific about which columns to show 
    

    と最も重要なのは、これらは以下のとおりです。

    BindGenericList(dataGridView1, items.ConvertAll(item => (User)item)); 
    BindGenericList(dataGridView1, items.ConvertAll(item => (Location)item)); 
    

    リスト内のすべての項目は、派生一定であることが知られている場合型を変換するには、ConvertAllを呼び出してその型にキャストします。

    +0

    これは本当に私が探しているものに近いですが、私は具体的な型への参照を持つことはできません。 IListItemsを返す私の工場以外は何も変更せずに、さまざまなタイプのサポートを追加できるようにしたい。リフレクションコールはOKです。キャストはできません。 – Robert

    +1

    以前のソリューションのようにDataTableを使用しない限り、可能かどうかわかりません。キャストする型がコンパイル時に知られていなければならないので、実行時にインタフェースを具体的なクラスに動的にキャストする方法はありません。私は、一般的なCastメソッドとActivator.CreateInstanceを使用してランタイムタイプをvarに取得しようとしましたが、タイプを判別できません。上記のあなたの解決策があなたの最善の策かもしれません。 –

    +0

    @Chris Doggettを見てくれてありがとう。 BindingListViewは確かに知って良いです。 – Robert

    0

    autogeneratecolumnsを使用すると、自動的にこれを行いません。

    +0

    IListItemにはIDと名前のみが含まれているため、AutoGenerateColumnsは機能しません。表示されるのは2つだけなので、残念ながら、彼は既知の型にキャストするか、アダプタのいくつかの並べ替えを入力します。 –

    0

    私の提案は、次のようになり...型を推論するために、リスト内の少なくとも1つのオブジェクトが必要です余分なプロパティのためにグリッドに列を動的に作成し、利用可能な列のリストを提供するIListItemの関数を作成するか、オブジェクト検査を使用してその型に使用可能な列を識別します。

    GUIの方がはるかに一般的なので、余分な列をUIで制御することはできませんが、動的になります。

    チェックされていない/コンパイルされた「擬似コード」。

    public interface IListItem 
    { 
        IList<string> ExtraProperties; 
    
    
        ... your old code. 
    } 
    
    public class User : IListItem 
    { 
        .. your old code 
        public IList<string> ExtraProperties { return new List { "UserSpecificField" } } 
    } 
    

    、フォームのロード中

    foreach(string columnName in firstListItem.ExtraProperties) 
    { 
        dataGridView.Columns.Add(new DataGridViewTextBoxColumn { DataPropertyName = columnName, HeaderText = columnName); 
    } 
    
    0

    リストビューベースのソリューションを使用して喜んでいる場合は、データバインド可能なバージョンObjectListViewは、あなたがこれを行うようになります。 DataSourceの公開されたプロパティを読み取り、各プロパティを示す列を作成します。 BindingListViewと組み合わせることができます。

    グリッドよりも綺麗に見えます。