2008-12-10 37 views
11

Tôi muốn sử dụng chế độ GridView của một ListView để hiển thị một tập hợp dữ liệu mà chương trình của tôi sẽ nhận được từ một nguồn bên ngoài. Dữ liệu sẽ bao gồm hai mảng, một trong các tên cột và một trong các giá trị chuỗi để điền điều khiển.WPF GridView với định nghĩa động

Tôi không thấy cách tạo lớp phù hợp mà tôi có thể sử dụng làm Mục trong một ListView. Cách duy nhất tôi biết để điền các mục là đặt nó vào một lớp với các thuộc tính đại diện cho các cột, nhưng tôi không có kiến ​​thức về các cột trước thời gian chạy.

Tôi có thể tạo một ItemTemplate động như được mô tả trong: Create WPF ItemTemplate DYNAMICALLY at runtime nhưng nó vẫn khiến tôi mất mát về cách mô tả dữ liệu thực tế.

Bất kỳ trợ giúp nào được biết ơn.

Trả lời

11

Bạn có thể thêm GridViewColumn đến GridView động cho mảng đầu tiên sử dụng một phương pháp như thế này:

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)) 
     }); 
    } 
} 

tôi giả sử mảng thứ hai chứa các giá trị sẽ được ROWS * chiều dài COLUMNS. Trong trường hợp đó, các mục của bạn có thể là mảng chuỗi có độ dài COLUMNS. Bạn có thể sử dụng Array.Copy hoặc LINQ để chia mảng. Nguyên tắc được thể hiện ở đây:

<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> 
5

Cảm ơn, điều đó rất hữu ích.

Tôi đã sử dụng nó để tạo phiên bản động như sau. Tôi tạo ra các tiêu đề cột như bạn đề nghị:

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; 
} 

Thiết lập ListView rất đơn giản trong XAML:

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

Tạo một lớp wrapper cho ObservableCollection để giữ dữ liệu của tôi:

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

Và ràng buộc ListView của tôi với nó:

results = new MyCollection(); 

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

Sau đó để cư nó, nó chỉ là một trường hợp thanh toán bù trừ ra bất kỳ dữ liệu cũ và bổ sung mới:

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

Có lẽ cách gọn gàng để làm việc đó, nhưng điều này là rất hữu ích cho ứng dụng của tôi. Cảm ơn một lần nữa.

3

Không chắc chắn nếu nó vẫn còn phù hợp nhưng tôi tìm thấy một cách để tạo kiểu cho các ô riêng lẻ bằng bộ chọn celltemplate. Đó là một chút hacky bởi vì bạn phải nhai xung quanh với các nội dung của ContentPresenter để có được DataContext thích hợp cho tế bào (do đó bạn có thể liên kết với các mục di động thực tế trong các mẫu tế bào):

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; 
     } 
    } 

Lưu ý: Tôi đã thay đổi DataMatrix từ bài viết CodeProject để các hàng là Từ điển (ColumnName -> Cell Object).

Tôi không thể đảm bảo rằng giải pháp này sẽ không phá vỡ điều gì đó hoặc sẽ không phá vỡ trong tương lai.Phát hành ròng. Nó dựa trên thực tế là ContentPresenter đặt DataContext sau khi nó đã chọn mẫu cho nội dung của nó. (Reflector sẽ giúp rất nhiều trong những trường hợp này :))

Khi tạo GridColumns, tôi làm điều gì đó như thế:

  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); 

Lưu ý: Tôi đã mở rộng ListView để nó có một tài sản CellTemplateSelector bạn có thể liên kết với trong XAML

@Edit 15/03/2011: tôi đã viết một bài viết nhỏ trong đó có một dự án nhỏ giới thiệu kèm theo: http://codesilence.wordpress.com/2011/03/15/listview-with-dynamic-columns/

1

phiên bản Hoàn toàn 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++; 
     } 
1

tôi sẽ đi về làm điều này bằng cách thêm một AttachedProperty vào GridView nơi ứng dụng MVVM của tôi có thể chỉ định các cột (và có lẽ một số siêu dữ liệu bổ sung.) Các mã hành vi sau đó có thể tự động làm việc trực tiếp với đối tượng GridView để tạo ra các cột. Bằng cách này, bạn tuân thủ MVVM và ViewModel có thể chỉ định các cột khi đang di chuyển.

+0

Đây là cách tôi muốn làm nhưng không rõ ràng như thế nào. Đạo cụ kèm theo và DP vẫn là một bí ẩn đối với tôi. Bất kỳ ví dụ nào cũng sẽ được đánh giá cao. Đã xem xét điều này (https://rachel53461.wordpress.com/2011/09/17/wpf-grids-rowcolumn-count-properties/). Là nó như thế nào? – VivekDev

Các vấn đề liên quan