35

動的に入力する必要がある Datagrid があります。

テーブルレイアウトは次のようになります。

id | image | name | Description | Name-1 | Name-N

最初の 4 つの列は静的で、他は動的です。ユーザーは、必要な数のユーザーを追加できる必要があります。

複数のユーザーのデータを表に並べて比較しようとしています。

現在、動的に生成された列の名前と静的列を埋めるメソッドを含むリストボックスがあります。各ユーザーのデータをロードすることもできます。次に、それらを 1 つの大きなテーブルにマージする必要があります。

主な問題は次のとおりです。「ユーザーデータ」と静的コンテンツを1つのデータグリッドに配置する方法。

4

4 に答える 4

59

これを行うには、少なくとも 3 つの方法があります。

  1. コード ビハインドから DataGrid の列を手動で変更する
  2. DataTable を ItemsSource として使用する *
  3. CustomTypeDescriptor を使用する

    *シンプルにすることをお勧めします


1 番目のアプローチ:コード ビハインドを使用して、実行時に DataGrid の列を生成します。これは簡単に実装できますが、特に MVVM を使用している場合は、少しハックに感じるかもしれません。したがって、固定列を持つ DataGrid を使用できます。

<DataGrid x:Name="grid">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding id}" Header="id" />
        <DataGridTextColumn Binding="{Binding image}" Header="image" />
    </DataGrid.Columns>
</DataGrid>

「名前」の準備ができたら、列を追加/削除してグリッドを変更します。次に例を示します。

// add new columns to the data grid
void AddColumns(string[] newColumnNames)
{
    foreach (string name in newColumnNames)
    {
        grid.Columns.Add(new DataGridTextColumn { 
            // bind to a dictionary property
            Binding = new Binding("Custom[" + name + "]"), 
            Header = name 
        });
    }
}

元のクラスを含むラッパー クラスと、カスタム プロパティを含む辞書を作成する必要があります。メインの行クラスが「ユーザー」であるとしましょう。次に、次のようなラッパー クラスが必要です。

public class CustomUser : User
{
    public Dictionary<string, object> Custom { get; set; }

    public CustomUser() : base()
    {
        Custom = new Dictionary<string, object>();
    }
}

ItemsSourceこの新しい「CustomUser」クラスのコレクションを に取り込みます。

void PopulateRows(User[] users, Dictionary<string, object>[] customProps)
{
    var customUsers = users.Select((user, index) => new CustomUser {
        Custom = customProps[index];
    });
    grid.ItemsSource = customUsers;
}

たとえば、次のように結び付けます。

var newColumnNames = new string[] { "Name1", "Name2" };
var users = new User[] { new User { id="First User" } };
var newProps = new Dictionary<string, object>[] {
    new Dictionary<string, object> { 
        "Name1", "First Name of First User",
        "Name2", "Second Name of First User",
    },
};
AddColumns(newColumnNames);
PopulateRows(users, newProps);

2 番目のアプローチ: DataTableを使用します。これは内部でカスタムタイプのインフラストラクチャを利用しますが、より使いやすくなっています。DataGridItemsSourceDataTable.DefaultViewプロパティにバインドするだけです。

<DataGrid ItemsSource="{Binding Data.DefaultView}" AutoGenerateColumns="True" />

次に、好きなように列を定義できます。たとえば、次のようになります。

Data = new DataTable();

// create "fixed" columns
Data.Columns.Add("id");
Data.Columns.Add("image");

// create custom columns
Data.Columns.Add("Name1");
Data.Columns.Add("Name2");

// add one row as an object array
Data.Rows.Add(new object[] { 123, "image.png", "Foo", "Bar" });

3 番目のアプローチ: .Net の型システムの拡張性を利用します。具体的には、 を使用しCustomTypeDescriptorます。これにより、実行時にカスタム タイプを作成できます。これにより、タイプに「Name1」、「Name2」、...「NameN」、またはその他の必要なプロパティがあることを DataGrid に伝えることができます。このアプローチの簡単な例については、こちらを参照してください。

于 2013-08-26T20:21:07.427 に答える
10

2 番目のアプローチ: DataTable を使用します。これは内部でカスタムタイプのインフラストラクチャを利用しますが、より使いやすくなっています。DataGrid の ItemsSource を DataTable.DefaultView プロパティにバインドするだけです。

これはほとんど機能しましたが、DataTable.DefaultView プロパティ プロパティにバインドする代わりに、DataView タイプのプロパティを作成し、それにバインドしました。

<DataGrid ItemsSource="{Binding DataView, Mode=TwoWay}" AutoGenerateColumns="True" />

これにより、バインディングを双方向にすることができます。DataTable.DefaultView へのバインディングを TwoWay バインディングにすることはできません。ビューモデルで

    public DataView DataView
    {
        get { return _dataView; }
        set
        {
            _dataView = value;
            OnPropertyChanged("DataView");
        }
    }

このセットアップでは、ビュー モデルの初期化時に列を動的に定義できるだけでなく、データ テーブルをいつでも動的に更新および変更できます。上記の McGarnagle によって定義されたアプローチを使用すると、DataTable が新しいデータ ソースで更新されたときにビュー スキーマが更新されませんでした。

于 2015-07-24T00:01:58.660 に答える
2

私は現在、別のアプローチを使用しています。このようにするのが正しいかどうかはわかりませんが、うまくいきます。小さなサンプルを作りました。

これが機能するには、Datagrid のすべてのエントリに同じ動的列が必要であり、柔軟性が少し低下することに注意してください。ただし、エントリごとに列の量が異なるエントリがある場合、データグリッドはおそらく間違ったアプローチです。

これらは私のクラスです:

 public class Person
    {
        public ObservableCollection<Activity> Hobbys { get; set; }
        public string Name { get; set; }
    }
 public class Activity
    {
        public string Name { get; set; }
    }

これがコード ビハインドです。

public MainWindow()
        {
            InitializeComponent();
            DataContext = this;

            ObservableCollection<Activity> hobbys = new ObservableCollection<Activity>();
            hobbys.Add(new Activity() { Name = "Soccer" });
            hobbys.Add(new Activity() { Name = "Basketball" });

            Community = new ObservableCollection<Person>();
            Community.Add(new Person() { Name = "James", Hobbys = hobbys });
            Community.Add(new Person() { Name = "Carl", Hobbys = hobbys });
            Community.Add(new Person() { Name = "Homer", Hobbys = hobbys });

            MyGrid.Columns.Add(new DataGridTextColumn() { Header = "Name", Binding = new Binding("Name") });    //Static Column
            int x = 0;
            foreach (Activity act in Community[0].Hobbys)  //Create the dynamic columns
            {
                MyGrid.Columns.Add(new DataGridTextColumn() { Header = "Activity", Binding = new Binding("Hobbys["+x+"].Name") });
                x++;
            }

        }

.XAML では単純に次のようになります。

  <DataGrid Name="MyGrid" ItemsSource="{Binding Community}" AutoGenerateColumns="False"/>
于 2018-11-27T17:07:22.667 に答える
0

それを単一の大きな DataGrid(table) に表示する必要がない場合は、id、image、name、Descriptionを持つDataGridを使用できます。その DataGrid でレコードの 1 つが選択されている場合は、ListBox を表示/更新します。その選択したレコードに関連する画像の名前

于 2013-08-26T20:30:15.297 に答える