您当前的位置: 首页 > 

寒冰屋

暂无认证

  • 2浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

带有页脚聚合的WPF数据网格

寒冰屋 发布时间:2021-07-02 23:20:20 ,浏览量:2

目录

介绍

使用代码

介绍

这将为您提供一个很好的datagrid,它能够添加汇总数值的页脚列。我还在列标题上添加了第三次单击以删除排序。使用datagrid时,我默认autogeneratecolumns为false,因为它正在寻找自定义列类型。如果您想更改它,可以更改一些代码。

使用代码

以下是我使用的三个类以及您要获取的聚合类型的enum。

public class CustomGrid : DataGrid
{
  public CustomGrid()
  {
    this.AutoGenerateColumns = false;
  }
  private Grid FooterGrid;
  private List FootersBlocks;
  private Border FooterBorder;
  private ScrollViewer ScrollViewer;
  private DataGridColumnHeadersPresenter Headers;
  public override void OnApplyTemplate()
  {
    base.OnApplyTemplate();
    this.CanUserReorderColumns = false;
    FootersBlocks = new List();
    var temp = this.Template;
    ScrollViewer = (ScrollViewer) temp.FindName("DG_ScrollViewer", this);
    FooterBorder = (Border) temp.FindName("FooterBorder", this);
    ScrollViewer.ApplyTemplate();
    this.ApplyTemplate();
    Headers = (DataGridColumnHeadersPresenter)
               ScrollViewer.Template.FindName("PART_ColumnHeadersPresenter", ScrollViewer);
    FooterGrid = (Grid)temp.FindName("FooterGrid", this);
    this.LayoutUpdated += CustomGrid_LayoutUpdated;
    this.PreviewMouseUp += CustomGrid_PreviewMouseUp;
  }

  protected override void OnSorting(DataGridSortingEventArgs eventArgs)
  {
    //See if we are sorted in descending order by this column already,
    //if so then remove the sorting.
    if (eventArgs.Column.SortDirection != null &&
    eventArgs.Column.SortDirection == System.ComponentModel.ListSortDirection.Descending)
    {
      var view = CollectionViewSource.GetDefaultView(this.ItemsSource);
      view?.SortDescriptions.Clear();
      eventArgs.Column.SortDirection = null;
      eventArgs.Handled = true;
      return;
    }

    base.OnSorting(eventArgs);
  }

  private void CustomGrid_PreviewMouseUp(object sender,
               System.Windows.Input.MouseButtonEventArgs e)
  {
    DependencyObject dep = (DependencyObject) e.OriginalSource;

    // iteratively traverse the visual tree
    while ((dep != null) && !(dep is DataGridCell) && !(dep is DataGridColumnHeader))
  {
      dep = VisualTreeHelper.GetParent(dep);
    }
    //Since we didn't find a cell then maybe a blank row was clicked, see if we can find it
    if (dep == null)
    {
      dep = (DependencyObject)e.OriginalSource;
      while ((dep != null) &&  !(dep is DataGridRow))
      {
        dep = VisualTreeHelper.GetParent(dep);
      }
      if (dep == null)
        return;
      if (dep is DataGridRow)
      {
        SelectedIndex = this.ItemContainerGenerator.IndexFromContainer((DataGridRow) dep);
      }
    }
  }

  private void CustomGrid_LayoutUpdated(object sender, EventArgs e)
  {
    int index = 0;

    double totalColumnWidth = 0;
    foreach (CustomGridColumn item in this.Columns)
    {
      totalColumnWidth += item.ActualWidth;
      if (FootersBlocks.Count > index)
      {
        FootersBlocks[index].Width = item.ActualWidth;
      }
      index++;
    }
    double test = Headers.ActualHeight;
    double addForScrollBar = 0;
    //See if my horizontal scrollbar is visible.
    //If so, add some height the top margin of the footer
    if (ScrollViewer.ComputedHorizontalScrollBarVisibility == Visibility.Visible)
    {
      addForScrollBar = 17;
    }

    FooterBorder.Margin = new Thickness(0, (this.ActualHeight -
    (22 + Headers.ActualHeight)) + (ScrollViewer.VerticalOffset - 5) -
    addForScrollBar, 0, -10);
  }

  protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
  {
    base.OnItemsSourceChanged(oldValue, newValue);
    //I have my own custom view model base with a property changed event
    //that I am going to subscribe to so I can update the footer if any of the values change
    if (oldValue != null)
    {
      foreach (var item in oldValue)
      {
        if (item is FcViewModelBase)
        {
          var bi = item as FcViewModelBase;
          bi.PropertyChanged -= Con_PropertyChanged;
        }
      }
    }
    if (newValue != null)
    {
      foreach (var item in newValue)
      {
        if (item is FcViewModelBase)
        {
          var bi = item as FcViewModelBase;
          bi.PropertyChanged += Con_PropertyChanged;
        }
      }
    }
  }

  protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
  {
    base.OnItemsChanged(e);
    Update_Footers();
  }

  public void Update_Footers()
  {
    FootersBlocks.Clear();
    FooterGrid.Children.Clear();
    FooterGrid.ColumnDefinitions.Clear();
    int index = 0;
    foreach (CustomGridColumn item in this.Columns)
    {
      decimal footerValue = 0;
      var tb = new TextBlock();

      FooterGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
      if (item.AggregateType != AggregateType.None)
      {
        foreach (var r in this.Items)
        {
          System.Type type = r.GetType();
          if (type.Name != "NamedObject")
          {
            string val = type.GetProperty(item.ColumnFooter).GetValue(r, null).ToString();
            decimal.TryParse(val, out var fv);
            footerValue += fv;
          }
        }

        switch (item.AggregateType)
        {
          case AggregateType.None:
            break;
          case AggregateType.Sum:
            tb.Text = string.Format(item.StringFormat, footerValue);
            break;
          case AggregateType.Count:
            tb.Text = this.Items.Count.ToString(item.StringFormat);
            break;
          case AggregateType.Average:
            decimal avg = Math.Round(footerValue / this.Items.Count, 2);
            tb.Text = string.Format(item.StringFormat, avg);
            break;
          default:
            break;
        }
      }

      tb.VerticalAlignment = VerticalAlignment.Top;
      tb.Margin = new Thickness(3);
      var borderBrush = (SolidColorBrush)(new BrushConverter().ConvertFrom("#FFB1B1B1"));
      var brd = new Border() { BorderThickness = new Thickness(0,0,1,0),
                BorderBrush = borderBrush, Width = item.ActualWidth };
      brd.Child = tb;
      tb.HorizontalAlignment = item.HorizontalAlignment;
      brd.SetValue(Grid.ColumnProperty, index);
      FooterGrid.Children.Add(brd);
      FootersBlocks.Add(brd);
      index++;
    }
  }

  private void Con_PropertyChanged(object sender,
          System.ComponentModel.PropertyChangedEventArgs e)
  {
    Update_Footers();
  }
}

public class CustomGridColumn : DataGridTextColumn
{
  public CustomGridColumn()
  {
    this.AggregateType = AggregateType.None;
    this.StringFormat = "{0}";
  }

  public static readonly DependencyProperty
         ColumnFooterProperty = DependencyProperty.RegisterAttached
         ("ColumnFooter", typeof(string), typeof(CustomGridColumn));

  public string ColumnFooter
  {
    get { return (string)GetValue(ColumnFooterProperty); }
    set { SetValue(ColumnFooterProperty, value); }
  }

  public static readonly DependencyProperty HorizontalAlignmentProperty =
         DependencyProperty.RegisterAttached("HorizontalAlignment",
         typeof(HorizontalAlignment), typeof(CustomGridColumn));
  public HorizontalAlignment HorizontalAlignment
  {
    get { return (HorizontalAlignment)GetValue(HorizontalAlignmentProperty); }
    set { SetValue(HorizontalAlignmentProperty, value); }
  }

  public static readonly DependencyProperty StringFormatProperty =
         DependencyProperty.RegisterAttached("StringFormat",
         typeof(string), typeof(CustomGridColumn));
  public string StringFormat
  {
    get { return (string)GetValue(StringFormatProperty); }
    set { SetValue(StringFormatProperty, value); }
  }

  public static readonly DependencyProperty AggregateTypeProperty =
         DependencyProperty.RegisterAttached("AggregateType",
         typeof(AggregateType), typeof(CustomGridColumn));
  public AggregateType AggregateType
  {
    get { return (AggregateType)GetValue(AggregateTypeProperty); }
    set { SetValue(AggregateTypeProperty, value); }
  }
}

public enum AggregateType
{
  None, Sum, Count, Average
}

public class FcViewModelBase : INotifyPropertyChanged, IDisposable
{
  public event PropertyChangedEventHandler PropertyChanged;

  protected void OnPropertyChanged(string propertyName)
  {
    var propertyChanged = this.PropertyChanged;

    if (propertyChanged != null)
    {
      propertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
  }

  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool disposing)
  {
    if (disposing)
    {
      // free managed resources
    }
  }

  protected bool SetProperty(ref T backingField, T Value,
                 Expression propertyExpression)
  {
    var changed = !EqualityComparer.Default.Equals(backingField, Value);

    if (changed)
    {
      backingField = Value;
      this.OnPropertyChanged(ExtractPropertyName(propertyExpression));
    }

    return changed;
  }

  private static string ExtractPropertyName(Expression propertyExpression)
  {
    var memberExp = propertyExpression.Body as MemberExpression;

    if (memberExp == null)
    {
      throw new ArgumentException("Expression must be a MemberExpression.",
                                  "propertyExpression");
    }

    return memberExp.Member.Name;
  }
}

这是我的主窗口xaml,其中包含我用于自定义数据网格的样式。您必须使用包含的样式,因为它包含页脚边框和网格。


  
    
    
    
    
      
      
    
    
      
      
    
    
      
      
    
    
    
    
      
      
      
    
    
    
      
      
    
    
    
      
      
    
    
    
    
    
      
      
    
    
      
        
          
            
              
                
                  
                  
                  
                
                
                  
                  
                  

                
                
                
                
                
                
                  
                    
                    
                  
                  
                
              
            
          
          
            
            
              
                
                  
                  
                
              
              

              
            
          
        

      
    

    
      
        
          
        
        
          
        
      
    
    
      
      
        
          
            
              
            
          
        
      
      
        
          
          
          
        
      
    
  
  
    
      
      
    
    
      
      
      
    
    
      
        
        
        
      
    
  

在这个例子中,我只是为我的DataContext使用后面的主窗口代码和一个示例类来用一些测试数据填充数据网格。

public MainWindow()
  {
    InitializeComponent();
    ItemsList = new List();
    for (int i = 0; i < 50; i++)
    {
      var vm = new TestClass { Item = "Test", TestItem = "Test2", Total = 123.5m * i };
      ItemsList.Add(vm);
    }
    this.DataContext = this;
  }
  public List ItemsList { get; set; }
}

public class TestClass : FcViewModelBase
{
  public string Item { get; set; }
  public string TestItem { get; set; }

  private decimal _total;
  public decimal Total
  {
    get { return _total; }
    set { SetProperty(ref _total, value, () => Total); }
  }
}

我希望这能让下一个人更容易,当水平或垂直滚动条出现时,尝试让我的网格线看起来漂亮,同时也让我的页脚表现正确。

https://www.codeproject.com/Tips/5299459/WPF-Datagrid-with-Footer-Aggregate

关注
打赏
1665926880
查看更多评论
立即登录/注册

微信扫码登录

0.0537s