2014-10-29 70 views
2

我有一個WPF DataGrid中,像這樣格式WPF的DataGrid的多行條件

Number | Attribute | Old  | New   | 
=============================================| 
1  | Height  | 1.1  | 0.9   | 
--------+------------+---------+-------------| 
1  | Material | Steel1 | Steel2  | 
--------+------------+---------+-------------| 
2  | Color  | Green | Light-Green | 
--------+------------+---------+-------------| 

數據。由於前2條記錄屬於因一起同Number我想2個記錄之間刪除邊框等等它看起來像這樣

Number | Attribute | Old  | New   | 
=============================================| 
1  | Height  | 1.1  | 0.9   | 
1  | Material | Steel1 | Steel2  | 
--------+------------+---------+-------------| 
2  | Color  | Green | Light-Green | 
--------+------------+---------+-------------| 

我要上裝載格式化行的方法

private void myGrid_LoadingRow(object sender, DataGridRowEventArgs e) { 
     ... 
} 

但是,這隻能格式化這個行的數據,我不知道哪一行在之後。所以我不能決定如何格式化這一行的邊框。

如何根據當前行的信息而不是當前行的信息對行進行格式化?

+0

當使用標準的'DataGrid'時沒有像這樣合併行的直接方式。有一些第三方網格可能支持這種事情,但如果您嘗試一起破解解決方案,如果您開始讓用戶應用排序,分組或過濾,將會變成一場噩夢。 – 2014-10-29 20:55:32

+1

@MikeStrobel:但我不想合併行。我只想在前面和後面的記錄上格式化它們 – 2014-10-29 20:57:16

+0

它們是否實際上「合併」並不是真正的重點。你想根據相鄰行是否共享數據來有條件地格式化行,這將比你想象的要複雜得多。行可以基於排序和過濾被添加,移除和重新排列,更不用說爲行虛擬化而回收。 – 2014-10-29 20:59:45

回答

4

我寫了一個簡單的示例應用程序,只有一個XAML文件和代碼隱藏。要重新創建我所做的,只需創建一個新的WPF 4.5應用程序,並將以下代碼粘貼到正確的文件中。

我的解決方案使用視圖模型,它允許您使用數據綁定完成所有任務(並且不要求您在代碼隱藏中連接事件)。

這可能看起來比您預期的要多得多,但請記住這是一個完整的示例,其中很多隻是設置。對於真正重要的代碼,希望你會發現,即使它增加了相當數量的行,它爲您提供了一個非常強大的模板,用於在WPF中創建各種酷炫的用戶界面。我在每個代碼文件後添加了一些評論,希望能夠更容易地弄清楚代碼的作用。

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:wpfApplication1="clr-namespace:WpfApplication1" 
     mc:Ignorable="d" 
     Title="MainWindow" 
     Height="350" 
     Width="525" 
     d:DataContext="{d:DesignInstance Type=wpfApplication1:MainViewModel, IsDesignTimeCreatable=False}"> 
    <DataGrid AutoGenerateColumns="False" 
       ItemsSource="{Binding AttributeUpdateViewModels}" 
       GridLinesVisibility="Vertical"> 
     <DataGrid.RowStyle> 
      <Style TargetType="DataGridRow"> 
       <Setter Property="BorderThickness" 
         Value="{Binding BorderThickness}" /> 
       <Setter Property="BorderBrush" 
         Value="Black" /> 
      </Style> 
     </DataGrid.RowStyle> 
     <DataGrid.Columns> 
      <DataGridTextColumn Header="Number" 
           Binding="{Binding Number}" /> 
      <DataGridTextColumn Header="Attribute" 
           Binding="{Binding Attribute}" /> 
      <DataGridTextColumn Header="Old" 
           Binding="{Binding Old}" /> 
      <DataGridTextColumn Header="New" 
           Binding="{Binding New}" /> 
     </DataGrid.Columns> 
    </DataGrid> 
</Window> 

這基本上只是一個簡單的數據和文本列格。神奇的是自定義的行風格,它根據需要創建水平網格線。 (有關數據綁定的更多詳細信息,請參閱下文。)

MainWindow.xaml.cs(即代碼隱藏):

using System.Collections.Generic; 
using System.Linq; 
using System.Windows; 

namespace WpfApplication1 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      DataContext = new MainViewModel(); 
     } 
    } 

    public class MainViewModel 
    { 
     public List<AttributeUpdateViewModel> AttributeUpdateViewModels { get; set; } 

     public MainViewModel() 
     { 
      var rawAttributeUpdates = new[] 
      { 
      new AttributeUpdate { Number = 1, Attribute = "Height", Old = "1.1", New = "0.9" }, 
      new AttributeUpdate { Number = 1, Attribute = "Material", Old = "Steel1", New = "Steel2" }, 
      new AttributeUpdate { Number = 2, Attribute = "Color", Old = "Green", New = "Light-Green" }, 
      new AttributeUpdate { Number = 3, Attribute = "Attribute4", Old = "Old4", New = "New4" }, 
      new AttributeUpdate { Number = 3, Attribute = "Attribute5", Old = "Old5", New = "New5" }, 
      new AttributeUpdate { Number = 3, Attribute = "Attribute6", Old = "Old6", New = "New6" }, 
      new AttributeUpdate { Number = 4, Attribute = "Attribute7", Old = "Old7", New = "New7" }, 
      new AttributeUpdate { Number = 5, Attribute = "Attribute8", Old = "Old8", New = "New8" }, 
      new AttributeUpdate { Number = 5, Attribute = "Attribute9", Old = "Old9", New = "New9" }, 
      new AttributeUpdate { Number = 1, Attribute = "Attribute10", Old = "Old10", New = "New10" } 
      }; 
      var sortedAttributeUpdates = rawAttributeUpdates.OrderBy(x => x.Number); 
      var groupedAttributeUpdates = sortedAttributeUpdates 
       .GroupBy(x => x.Number); 
      AttributeUpdateViewModels = sortedAttributeUpdates 
       .Select(x => GetAttributeUpdateRow(x, groupedAttributeUpdates)) 
       .ToList(); 
     } 

     private AttributeUpdateViewModel GetAttributeUpdateRow(
      AttributeUpdate attributeUpdate, 
      IEnumerable<IGrouping<int, AttributeUpdate>> groupedAttributeUpdates) 
     { 
      var lastInGroup = groupedAttributeUpdates.Single(x => x.Key == attributeUpdate.Number).Last(); 
      return new AttributeUpdateViewModel 
      { 
       Number = attributeUpdate.Number, 
       Attribute = attributeUpdate.Attribute, 
       New = attributeUpdate.New, 
       Old = attributeUpdate.Old, 
       IsLastInGroup = attributeUpdate == lastInGroup 
      }; 
     } 
    } 

    public class AttributeUpdate 
    { 
     public int Number { get; set; } 
     public string Attribute { get; set; } 
     public string Old { get; set; } 
     public string New { get; set; } 
    } 

    public class AttributeUpdateViewModel 
    { 
     public int Number { get; set; } 
     public string Attribute { get; set; } 
     public string Old { get; set; } 
     public string New { get; set; } 

     public bool IsLastInGroup { get; set; } 

     public Thickness BorderThickness 
     { 
      get { return IsLastInGroup ? new Thickness(0, 0, 0, 1) : new Thickness(); } 
     } 
    } 
} 

基本上,我認爲你是顯示你的表的每一行中的數據是AttributeUpdate。 (我只是編的,你可能有一個更好的名字。)

由於一個AttributeUpdate是純粹的數據,並沒有任何與您的數據應該如何格式化,我創建了一個AttributeUpdateViewModel合併數據格式顯示所需的信息。

因此,AttributeUpdateAttributeUpdateViewModel共享相同的數據,但視圖模型添加了一些處理格式的屬性。

什麼是用於格式化的新屬性?

  • IsLastInGroup - 無論是在有問題的行是最後的組(其中羣組共享相同Number的所有項目)。
  • BorderThickness - 邊框的Thickness。在這種情況下,如果該項目在組中最後一個,則下邊框爲1,其他爲零,否則爲0。

數據綁定在XAML文件中顯示爲{Binding name_of_property},只需輕觸視圖模型中的數據和格式化信息即可。如果底層數據在您的應用程序運行期間可能發生變化,您將希望讓您的視圖模型實現INotifyPropertyChanged interfaceINotifyPropertyChanged基本上將「更改檢測」添加到您的應用程序,允許您的綁定自動重新綁定到新的/更改的數據。最後一點是我用LINQ query來照顧分組邏輯。此特定查詢按Number排序行,然後按Number對它們進行分組。然後,它創建AttributeUpdateViewModel實例,根據當前AttributeUpdate是否與其組中的最後一項匹配來填充IsLastInGroup

注:爲了簡單起見,我在一個文件中放了幾個類。通常的約定是每個文件一個類,所以你可能想要將每個類分成它自己的文件。

結果

A DataGrid with grid lines between groups rather than between every row

編輯

@Mike斯特羅貝爾的評論使點按號碼排序未必是可取的。例如,用戶可能希望按不同的列進行排序,但仍然可以看到按數字分組的行。我不確定這會是一個常見用例,但如果這是一項要求,您可以簡單地用不同的LINQ查詢來替換「當前」值與「下一個」值,然後確定Number是否更改。這裏是我的破解:

var nextAttributeUpdates = rawAttributeUpdates 
    .Skip(1) 
    .Concat(new[] { new AttributeUpdate { Number = -1 } }); 
AttributeUpdateViewModels = rawAttributeUpdates 
    .Zip(
     nextAttributeUpdates, 
     (c, n) => new { Current = c, NextNumber = n.Number }) 
    .Select(
     x => new AttributeUpdateViewModel 
     { 
      Number = x.Current.Number, 
      Attribute = x.Current.Attribute, 
      New = x.Current.New, 
      Old = x.Current.Old, 
      IsLastInGroup = x.Current.Number != x.NextNumber 
     }) 
    .ToList(); 
+0

您正在從* unsorted *'rawAttributeUpdates'序列中填充'AttributeUpdateViewModels',如果原始列表尚未按數字排序,則會給出錯誤的結果。複製集合初始值設定項中的項目塊以查看我的意思。由於此解決方案依賴於按數字對源集合進行排序,只要用戶按其他列進行排序,該解決方案就會中斷。 – 2014-11-06 15:29:58

+0

@MikeStrobel,謝謝,兩個很好的漁獲。我修改了查詢,以便數據先按數字排序,然後根據排序的數據進行分組和選擇。我還在我的答案底部編輯了一個新查詢,以解決用戶通過其他列進行排序的可能性。 – devuxer 2014-11-06 18:48:31

2

如果您只是想隱藏後面跟着具有相同ID的行的底部邊界,那麼爲什麼不直接將當前行模型與下一行模型進行比較?

private void myGrid_LoadingRow(object sender, DataGridRowEventArgs e) 
{ 
    DataGrid grid = (DataGrid)sender; 
    object rowModel = e.Row.Item; 
    int index = grid.Items.IndexOf(e.Row.Item); 

    bool hideBottomBorder = false; 
    if (index + 1 < grid.Items.Count) 
    { 
     var thisItem = rowModel as TheRowModel; 
     var nextItem = grid.Items[index + 1] as TheRowModel; 
     if (thisItem.Number == nextItem.Number) 
     { 
      hideBottomBorder = true; 
     } 
    } 

    if (hideBottomBorder) 
    { 
     // hide bottom border 
    } 
    else 
    { 
     // show bottom border 
    } 
} 

以上應該可以正常工作,如果集合是固定的(即單個項目不添加或刪除),即使它被重新排序(因爲「LoadingRow」將在這種情況下,再次激發每一行)。如果在您的場景中可以修改單個行,那該怎麼辦?

  • 如果「號碼」被修改:最好聽這在視圖模型的PropertyChanged,或者你可以使用DataGrid的CellEditEnding事件。
  • 如果添加或刪除單個行:監聽基礎集合上的CollectionChanged事件,或者使用DataGrid的事件​​/UnloadingRow事件。

您只需要觸發重新計算與修改行相鄰的行的適當邊界。