13

ListViewのGridViewモードを使用して、プログラムが外部ソースから受信するデータのセットを表示したいと思います。データは2つの配列で構成され、1つは列名、もう1つはコントロールに入力する文字列値です。

ListViewのアイテムとして使用できる適切なクラスを作成する方法がわかりません。アイテムを設定する唯一の方法は、列を表すプロパティを持つクラスに設定することですが、実行前には列についての知識がありません。

「実行時に動的にWPFItemTemplateを作成する」で説明されているように、ItemTemplateを動的に作成できますが、実際のデータをどのように記述するかについては、それでも途方に暮れます。

ありがたいことにどんな助けも受けました。

4

6 に答える 6

13

次のようなメソッドを使用して、最初の配列を指定して、GridViewColumns を GridView に動的に追加できます。

private void AddColumns(GridView gv, string[] columnNames)
{
    for (int i = 0; i < columnNames.Length; i++)
    {
        gv.Columns.Add(new GridViewColumn
        {
            Header = columnNames[i],
            DisplayMemberBinding = new Binding(String.Format("[{0}]", i))
        });
    }
}

値を含む 2 番目の配列は、ROWS * COLUMNS の長さになると想定しています。その場合、項目は長さ COLUMNS の文字列配列にすることができます。Array.Copy または LINQ を使用して配列を分割できます。原理は次のとおりです。

<Grid>
    <Grid.Resources>
        <x:Array x:Key="data" Type="{x:Type sys:String[]}">
            <x:Array Type="{x:Type sys:String}">
                <sys:String>a</sys:String>
                <sys:String>b</sys:String>
                <sys:String>c</sys:String>
            </x:Array>
            <x:Array Type="{x:Type sys:String}">
                <sys:String>do</sys:String>
                <sys:String>re</sys:String>
                <sys:String>mi</sys:String>
            </x:Array>
        </x:Array>
    </Grid.Resources>
    <ListView ItemsSource="{StaticResource data}">
        <ListView.View>
            <GridView>
                <GridViewColumn DisplayMemberBinding="{Binding Path=[0]}" Header="column1"/>
                <GridViewColumn DisplayMemberBinding="{Binding Path=[1]}" Header="column2"/>
                <GridViewColumn DisplayMemberBinding="{Binding Path=[2]}" Header="column3"/>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>
于 2008-12-10T19:35:45.150 に答える
6

ありがとう、それはとても役に立ちます。

それを使用して、次のように動的バージョンを作成しました。あなたが提案したように、列見出しを作成しました:

private void AddColumns(List<String> myColumns)
{
    GridView viewLayout = new GridView();
    for (int i = 0; i < myColumns.Count; i++)
    {
        viewLayout.Columns.Add(new GridViewColumn
        {
            Header = myColumns[i],
            DisplayMemberBinding = new Binding(String.Format("[{0}]", i))
        });
    }
    myListview.View = viewLayout;
}

XAML で非常に簡単に ListView を設定します。

<ListView Name="myListview" DockPanel.Dock="Left"/>

データを保持する ObservableCollection のラッパー クラスを作成しました。

public class MyCollection : ObservableCollection<List<String>>
{
    public MyCollection()
        : base()
    {
    }
}

そして、私の ListView をそれにバインドします。

results = new MyCollection();

Binding binding = new Binding();
binding.Source = results;
myListview.SetBinding(ListView.ItemsSourceProperty, binding);

次に、データを入力するために、古いデータを消去して新しいデータを追加するだけでした:

results.Clear();
List<String> details = new List<string>();
for (int ii=0; ii < externalDataCollection.Length; ii++)
{
    details.Add(externalDataCollection[ii]);
}
results.Add(details);

もっときちんとした方法があるかもしれませんが、これは私のアプリケーションにとって非常に便利です。再度、感謝します。

于 2008-12-18T19:56:47.650 に答える
4

CodeProjectに関するこの記事では、動的リストビューを作成する方法を正確に説明しています。データが実行時にのみ認識されている場合です。 http://www.codeproject.com/KB/WPF/WPF_DynamicListView.aspx

于 2009-05-16T23:30:24.937 に答える
3

まだ関連性があるかどうかはわかりませんが、セル テンプレート セレクターを使用して個々のセルのスタイルを設定する方法を見つけました。セルの適切な DataContext を取得するために ContentPresenter のコンテンツをいじる必要があるため (セル テンプレート内の実際のセル項目にバインドできるようにするため)、少しハックです。

    public class DataMatrixCellTemplateSelectorWrapper : DataTemplateSelector
    {
        private readonly DataTemplateSelector _ActualSelector;
        private readonly string _ColumnName;
        private Dictionary<string, object> _OriginalRow;

        public DataMatrixCellTemplateSelectorWrapper(DataTemplateSelector actualSelector, string columnName)
        {
            _ActualSelector = actualSelector;
            _ColumnName = columnName;
        }

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            // The item is basically the Content of the ContentPresenter.
            // In the DataMatrix binding case that is the dictionary containing the cell objects.
            // In order to be able to select a template based on the actual cell object and also
            // be able to bind to that object within the template we need to set the DataContext
            // of the template to the actual cell object. However after the template is selected
            // the ContentPresenter will set the DataContext of the template to the presenters
            // content. 
            // So in order to achieve what we want, we remember the original DataContext and then
            // change the ContentPresenter content to the actual cell object.
            // Therefor we need to remember the orginal DataContext otherwise in subsequent calls
            // we would get the first cell object.

            // remember old data context
            if (item is Dictionary<string, object>)
            {
                _OriginalRow = item as Dictionary<string, object>;
            }

            if (_OriginalRow == null)
                return null;

            // get the actual cell object
            var obj = _OriginalRow[_ColumnName];

            // select the template based on the cell object
            var template = _ActualSelector.SelectTemplate(obj, container);

            // find the presenter and change the content to the cell object so that it will become
            // the data context of the template
            var presenter = WpfUtils.GetFirstParentForChild<ContentPresenter>(container);
            if (presenter != null)
            {
                presenter.Content = obj;
            }

            return template;
        }
    }

注: 行が辞書 (ColumnName -> Cell Object) になるように、CodeProject の記事から DataMatrix を変更しました。

このソリューションが何かを壊さない、または将来の .Net リリースで壊れないことを保証することはできません。これは、ContentPresenter がテンプレートを選択した後に DataContext を独自のコンテンツに設定するという事実に依存しています。(これらの場合、リフレクターは非常に役立ちます:))

GridColumns を作成するときは、次のようにします。

           var column = new GridViewColumn
                          {
                              Header = col.Name,
                              HeaderTemplate = gridView.ColumnHeaderTemplate
                          };
            if (listView.CellTemplateSelector != null)
            {
                column.CellTemplateSelector = new DataMatrixCellTemplateSelectorWrapper(listView.CellTemplateSelector, col.Name);
            }
            else
            {
                column.DisplayMemberBinding = new Binding(string.Format("[{0}]", col.Name));
            }
            gridView.Columns.Add(column);

注: xaml でバインドできる CellTemplateSelector プロパティを持つように ListView を拡張しました。

@Edit 15/03/2011: 私は、小さなデモ プロジェクトが添付された小さな記事を書きました: http://codesilence.wordpress.com/2011/03/15/listview-with-dynamic-columns/

于 2010-06-30T23:37:32.910 に答える
1

完全にprogmaticバージョン:

        var view = grid.View as GridView;
        view.Columns.Clear();
        int count=0;
        foreach (var column in ViewModel.GridData.Columns)
        {
            //Create Column
            var nc = new GridViewColumn();
            nc.Header = column.Field;
            nc.Width = column.Width;
            //Create template
            nc.CellTemplate = new DataTemplate();
            var factory = new FrameworkElementFactory(typeof(System.Windows.Controls.Border));
            var tbf = new FrameworkElementFactory(typeof(System.Windows.Controls.TextBlock));

            factory.AppendChild(tbf);
            factory.SetValue(System.Windows.Controls.Border.BorderThicknessProperty, new Thickness(0,0,1,1));
            factory.SetValue(System.Windows.Controls.Border.MarginProperty, new Thickness(-7,0,-7,0));
            factory.SetValue(System.Windows.Controls.Border.BorderBrushProperty, Brushes.LightGray);
            tbf.SetValue(System.Windows.Controls.TextBlock.MarginProperty, new Thickness(6,2,6,2));
            tbf.SetValue(System.Windows.Controls.TextBlock.HorizontalAlignmentProperty, column.Alignment);

            //Bind field
            tbf.SetBinding(System.Windows.Controls.TextBlock.TextProperty, new Binding(){Converter = new GridCellConverter(), ConverterParameter=column.BindingField});
            nc.CellTemplate.VisualTree = factory;

            view.Columns.Add(nc);
            count++;
        }
于 2011-08-04T11:01:07.953 に答える
1

これを行うには、MVVM アプリケーションが列 (およびおそらくいくつかの追加のメタデータ) を指定できるように、AttachedProperty を GridView に追加します。その後、Behavior コードは GridView オブジェクトと直接動的に連携して列を作成できます。このようにして、MVVM に準拠し、ViewModel はオンザフライで列を指定できます。

于 2013-01-03T19:20:18.160 に答える