2017-03-06 103 views
0

我有一個自定義數據結構,我需要一個WPF組件,它將代表我的數據結構。組件應該看起來像一張圖片。組件應該是動態的,因此結構中的ColumnSets的數量可以從1到x。並且每個columnSet可以有不同的列數。WPF嵌套數據網格爲自定義數據結構

public class CustomStructure{ 
    public List<ColumnSet> ColumnSets{get; set;} 
} 

public class ColumnSet{ 
    public string Name { get; set; } 
    public List<Column> ColumnSets{get;} 
} 

public class Column{ 
    public string Name { get; set; } 
    public List<int> Data{get;} 
} 

第一行表示來自類ColumnSet的Name屬性。第二行是類Column的Name屬性。其他行是來自Column類的Data。

Wanted WPF component design (image)
我的解決方案

我定義的兩個組成部分。首先表示外部表,它被稱爲FuzzyTableControl。它有X列和只有一行。 第二個表示FuzzyTableControl的第一行中每列的FuzzyInnerTableControl。

FuzzyTableControl.xaml

<UserControl:Class="FuzzyTableControl">  
    <UserControl.DataContext> 
     <viewModel:FuzzyTableViewModel/> 
    </UserControl.DataContext> 

<DataGrid AutoGenerateColumns="True" IsReadOnly="True" 
     CanUserAddRows="False" 
     CanUserDeleteRows="False" 
     ItemsSource="{Binding DataTable}"> 
// here is problem 1 
    <DataGrid.Columns> 
     <DataGridTemplateColumn Header="{Binding}"> 
     <DataGridTemplateColumn.CellTemplate> 
      <DataTemplate> 
       <view:FuzzyInnerTableControl/> 
      </DataTemplate> 
     </DataGridTemplateColumn.CellTemplate> 
    </DataGridTemplateColumn> 
</DataGrid.Columns> 
</DataGrid> 
</UserControl> 

FuzzyTableViewModel.cs

public class FuzzyTableViewModel : BaseViewModel 
    { 
     public FuzzyTable Table { get; set; } 

     public DataTable DataTable { get; set; } 


     public FuzzyTableViewModel() 
     { 
      Table = FuzzyTable.Generate(3, 2); 
      DataTable = new DataTable(); 

      foreach (var attribute in Table.Attributes) 
      { 
       DataTable.Columns.Add(new DataColumn(attribute.Name)); 
      } 
      var row = new List<object>(); 
      foreach (var attribute in Table.Attributes) 
       row.Add(attribute); 

      DataTable.Rows.Add(row.ToArray()); 
     } 
    } 

FuzzyInnerTableControl.xaml 這一個工程好

<UserControl:Class="FuzzyInnerTableControl"> 
    <UserControl.DataContext> 
     <viewModel:FuzzyInnerTableViewModel/> 
    </UserControl.DataContext>  
    <DataGrid ItemsSource="{Binding DataTable}"/> 
</UserControl> 

FuzzyInnerTableViewModel.cs

public class FuzzyInnerTableViewModel : BaseViewModel 
    { 
     public DataTable DataTable { get; } 

     public ColumnSetDouble ColumnSetDouble { get; set; } 

     public FuzzyInnerTableViewModel() 
     { 
      // test 
      var table = FuzzyTable.Generate(3, 2); 
      ColumnSetDouble = table.ClassAttribute; 
      //end test 

      DataTable = new DataTable(); 

      foreach (var attribute in ColumnSetDouble.Columns) 
       DataTable.Columns.Add(new DataColumn(attribute.Name)); 

      for (int rowId = 0; rowId < ColumnSetDouble.Columns[0].Data.Count; rowId++) 
      { 
       var row = new List<object>(); 
       foreach (var column in ColumnSetDouble.Columns) 
       { 
        row.Add(column.Data[rowId]); 
       } 

       DataTable.Rows.Add(row.ToArray()); 
      } 

     } 

    } 

我不知道如何爲FuzzyTableControl的每一列定義單元格模板。此解決方案創建新列,但我需要從viewModel動態加載列。

+0

數據網格通常是通過一個行集合,其中每一行有一些列的屬性來表示。在嘗試顯示之前,您可能希望將數據轉換爲基於行的結構。 – grek40

+0

如果您想知道如何顯示多個表格,您應該已經知道如何顯示一個表格(一個'ColumnSet')並將其包含在您的問題中。否則,你應該先弄清楚如何顯示單個表格,而不用擔心很多表格。問題是要儘可能廣泛。 – grek40

+0

我剛添加更多的細節問題。 – Patrik

回答

0

爲了定義所有自動生成的單元格的模板,您可以嘗試使用DataGrid.CellStyle

<DataGrid AutoGenerateColumns="True" IsReadOnly="True" 
    CanUserAddRows="False" 
    CanUserDeleteRows="False" 
    ItemsSource="{Binding DataTable}"> 
    <DataGrid.CellStyle> 
     <Style TargetType="DataGridCell"> 
      <Setter Property="ContentTemplate"> 
       <Setter.Value> 
        <DataTemplate> 
         <view:FuzzyInnerTableControl/> 
        </DataTemplate> 
       </Setter.Value> 
      </Setter> 
     </Style> 
    </DataGrid.CellStyle> 
</DataGrid> 

如果我有更好的主意,我會編輯。


現在你問爲什麼<view:FuzzyInnerTableControl DataContext="{Binding}"/>不起作用。所以,我想你已經準備好外的DataTable所以它真的包含FuzzyInnerTableViewModel這是不完全瑣碎:

所有的
// when creating a column, ensure to set the DataType, so your content won't be reduced to some text 
DataTable.Columns.Add(new DataColumn(attribute.Name, typeof(FuzzyInnerTableViewModel))); 

// I have no idea what attribute is, but it better be a FuzzyInnerTableViewModel for your use case! 
row.Add(attribute); 

首先,DataGrid會嘗試自動生成相對無用DataGridTextColumn,這需要改變。處理AutoGeneratingColumn事件以更改列類型。 DataGridTemplateColumn實際上不會爲您提供單元格內容,如果您(如我)嘗試通過CellStyle設置DataGridCell.Content,它會好心忽略您並在本地設置內容。因此,我們暫時將單元格值推到DataGridCell.Tag

這裏是FuzzyTableControlDataGrid_AutoGeneratingColumn方法連接到xaml中的DataGrid.AutoGeneratingColumn事件。

public partial class FuzzyTableControl : UserControl 
{ 
    public FuzzyTableControl() 
    { 
     InitializeComponent(); 
    } 

    private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) 
    { 
     e.Column = new DataGridTemplateColumn 
     { 
      CellStyle = new Style(typeof(DataGridCell)) 
      { 
       Setters = 
       { 
        new Setter(DataGridCell.TagProperty, new Binding(e.PropertyName)), 
       } 
      }, 
      Header = e.Column.Header, 
      HeaderStringFormat = e.Column.HeaderStringFormat, 
      HeaderStyle = e.Column.HeaderStyle, 
      HeaderTemplate = e.Column.HeaderTemplate, 
      HeaderTemplateSelector = e.Column.HeaderTemplateSelector, 
      // transfer whatever properties you feel worthy in your scenario 
     }; 
    } 
} 

現在,至少需要的數據都掛在Tag周圍地方,但你猜怎麼着 - 在DataGrid.CellStyle只是通過一種風格誰是主要關注的是動態綁定一些數據所取代。因此,讓移動DataTemplate到資源,而且在其熱添加的結合,一些家長DataGridCell.Tag ...

<UserControl.Resources> 
    <DataTemplate x:Key="FuzzyInnerTableTemplate"> 
     <view:FuzzyInnerTableControl 
      DataContext="{Binding Tag,RelativeSource={RelativeSource AncestorType=DataGridCell}}"/> 
    </DataTemplate> 
</UserControl.Resources> 

及導線上模板,列生成裏面的所有其他的事情了DataGridTemplateColumn

  // ... 
      // transfer whatever properties you feel worthy in your scenario 
      CellTemplate = Resources["FuzzyInnerTableTemplate"] as DataTemplate 

FuzzyInnerTableControl中刪除UserControl.DataContext,因爲否則綁定將失敗並且仍然會獲得默認值。如果您需要結合默認值和外部提供的值,請查找其他方法。


困惑?好了,所以是我,所以這裏是我的測試代碼的總結,包括一些虛構的行和列,因爲我沒有使用你的整個Column.DataTable.Attributes東西:

XAML的FuzzyTable(控制,視圖模型)

<UserControl x:Class="WpfApplication2.FuzzyTableControl" 
      ... 
    > 
    <UserControl.DataContext> 
     <viewModel:FuzzyTableViewModel/> 
    </UserControl.DataContext> 
    <UserControl.Resources> 
     <DataTemplate x:Key="FuzzyInnerTableTemplate"> 
      <view:FuzzyInnerTableControl 
       DataContext="{Binding Tag,RelativeSource={RelativeSource AncestorType=DataGridCell}}"/> 
     </DataTemplate> 
    </UserControl.Resources> 

    <DataGrid AutoGenerateColumns="True" IsReadOnly="True" 
     CanUserAddRows="False" 
     CanUserDeleteRows="False" 
     ItemsSource="{Binding DataTable}" 
     AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"> 
    </DataGrid> 
</UserControl> 

代碼FuzzyTable

public partial class FuzzyTableControl : UserControl 
{ 
    public FuzzyTableControl() 
    { 
     InitializeComponent(); 
    } 

    private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) 
    { 
     e.Column = new DataGridTemplateColumn 
     { 
      CellStyle = new Style(typeof(DataGridCell)) 
      { 
       Setters = 
       { 
        new Setter(DataGridCell.TagProperty, new Binding(e.PropertyName)), 
       } 
      }, 
      Header = e.Column.Header, 
      HeaderStringFormat = e.Column.HeaderStringFormat, 
      HeaderStyle = e.Column.HeaderStyle, 
      HeaderTemplate = e.Column.HeaderTemplate, 
      HeaderTemplateSelector = e.Column.HeaderTemplateSelector, 
      // transfer whatever properties you feel worthy in your scenario 
      CellTemplate = Resources["FuzzyInnerTableTemplate"] as DataTemplate 
     }; 
    } 
} 

public class FuzzyTableViewModel : BaseViewModel 
{ 
    public DataTable DataTable { get; set; } 


    public FuzzyTableViewModel() 
    { 
     DataTable = new DataTable(); 

     DataTable.Columns.Add(new DataColumn("O", typeof(FuzzyInnerTableViewModel))); 
     DataTable.Columns.Add(new DataColumn("P", typeof(FuzzyInnerTableViewModel))); 

     var c1 = new FuzzyInnerTableViewModel(); 
     var c2 = new FuzzyInnerTableViewModel(); 

     c1.DataTable.Rows[1][0] = "Replace"; 

     var row = new List<object>(); 
     row.Add(c1); 
     row.Add(c2); 

     DataTable.Rows.Add(row.ToArray()); 
    } 
} 

XAML爲FuzzyInnerTable

<UserControl x:Class="WpfApplication2.FuzzyInnerTableControl" 
      ... 
      Loaded="UserControl_Loaded"> 
    <UserControl.Resources> 
     <viewModel:FuzzyInnerTableViewModel x:Key="defaultVM"/> 
    </UserControl.Resources> 
    <DataGrid ItemsSource="{Binding DataTable}"/> 
</UserControl> 

代碼FuzzyInnerTable

public partial class FuzzyInnerTableControl : UserControl 
{ 
    public FuzzyInnerTableControl() 
    { 
     InitializeComponent(); 
    } 

    private void UserControl_Loaded(object sender, RoutedEventArgs e) 
    { 
     if (DataContext == null) 
     { 
      DataContext = Resources["defaultVM"]; 
     } 

    } 
} 

public class FuzzyInnerTableViewModel : BaseViewModel 
{ 
    public DataTable DataTable { get; private set; } 

    public FuzzyInnerTableViewModel() 
    { 
     DataTable = new DataTable(); 

     DataTable.Columns.Add(new DataColumn("A")); 
     DataTable.Columns.Add(new DataColumn("B")); 

     for (int rowId = 0; rowId < 3; rowId++) 
     { 
      var row = new List<object>(); 
      row.Add(rowId); 
      row.Add(2 * rowId); 

      DataTable.Rows.Add(row.ToArray()); 
     } 
    } 
} 
+0

謝謝,這是我需要的。還有一個問題。在行中,我創建了新的FuzzyInnerTableViewModel,我想綁定它。但這段代碼不起作用 Patrik

+0

@Patrik「不工作」是一個相當模糊的問題描述。它是否拋出錯誤,它是否將某些內容記錄到調試器輸出中,是不是顯示(如果是這樣,Snoop或類似的WPF運行時調試器會告訴有關可用的datacontext?) – grek40

+0

新數據未更新,它仍在顯示默認數據。在數據上下文選項卡上的snoop中,有默認的ViewModel。 – Patrik