using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Data; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Xml.Serialization; using ExtendedGrid.Base; using ExtendedGrid.Classes; using ExtendedGrid.Interface; using Expression = System.Linq.Expressions.Expression; using Path = System.Windows.Shapes.Path; namespace ExtendedGrid.ExtendedGridControl { public sealed class ExtendedDataGrid : CopyDg, INotifyPropertyChanged { #region Members private static readonly Uri DataGridUri = new Uri("/DataGridTransform;component/Styles/DataGrid.Generic.xaml", UriKind.Relative); private readonly ResourceDictionary _dataGridUriStyle = new ResourceDictionary {Source = DataGridUri}; private static readonly Uri DefaultThemeUri = new Uri("/DataGridTransform;component/DataGridThemes/Default.xaml", UriKind.Relative); private const string GenericThemeUri = "/DataGridTransform;component/DataGridThemes/{0}.xaml"; private readonly ResourceDictionary _defaultGridTheme = new ResourceDictionary {Source = DefaultThemeUri}; private DataTable _rowSummariesTable = new DataTable(); private ObservableCollection _groupByCollection = new ObservableCollection(); public const int Sum = 0; public const int Average = 1; public const int Count = 2; public const int Min = 3; public const int Max = 4; public const int Smallest = 5; public const int Largest = 6; private ResourceDictionary _lastTheme; internal List ColumnsInGroupBy = new List(); public enum Themes { Default, Office2007Silver, Office2007Black, Office2007Blue, Windows7, OrangeDarkExplorer, System, ElectronicMedia } private bool _inWidthChange; private bool _updatingColumnInfo; public static readonly DependencyProperty ColumnInfoProperty = DependencyProperty.Register("ColumnInfo", typeof ( ObservableCollection < ColumnInformation >), typeof ( ExtendedDataGrid), new FrameworkPropertyMetadata (null, FrameworkPropertyMetadataOptions . BindsTwoWayByDefault, ColumnInfoChangedCallback) ); private int? _lastFrozenRowCount; private readonly List _computaions = new List { "Sum", "Average", "Count", "Min", "Max", "Smallest", "Largest" }; private readonly List _summableComputaions = new List {"Sum", "Average", "Count", "Min", "Max"}; private readonly List _nonSummableComputaions = new List {"Smallest", "Largest", "Count"}; private DataRow _draggedItem; /// /// Format of count on row summary /// public string RowSummaryCountFormat { get; set; } /// /// Format of sum on row summary /// public string RowSummarySumFormat { get; set; } /// /// Format of average on row summary /// public string RowSummaryAverageFormat { get; set; } /// /// Format of minimum on row summary /// public string RowSummaryMinFormat { get; set; } /// /// Format of maximum on row summary /// public string RowSummaryMaxFormat { get; set; } /// /// Format of smallest on row summary /// public string RowSummarySmallestFormat { get; set; } /// /// Format of largest on row summary /// public string RowSummaryLargestFormat { get; set; } /// /// If set to true will remove 3 state sorting /// And it will revert to normal two state sorting /// If set to true it will perform 3 state sorting /// public bool OnlyTwoWaySorting { get; set; } #endregion #region Constructors public ExtendedDataGrid() { AddResources(); AutoFilterHelper = new AutoFilterHelper(this); (Items.SortDescriptions as INotifyCollectionChanged).CollectionChanged += SortDescriptionsCollectionChanged; Sorting += DataGridStandardSorting; Loaded += ExtendedDataGridLoaded; LoadingRow += ExtendedDataGridLoadingRow; Columns.CollectionChanged += ColumnsCollectionChanged; GroupByCollection.CollectionChanged += GroupByCollectionCollectionChanged; GroupByColumnAdded += ExtendedDataGridGroupByColumnAdded; GroupByColumnRemoved += ExtendedDataGridGroupByColumnRemoved; PreviewKeyDown += ExtendedDataGridPreviewKeyDown; RowHeaderChanged += ExtendedDataGridRowHeaderChanged; FrozenColumnCountChanged += ExtendedDataGridFrozenColumnCountChanged; MouseDown += ExtendedDataGridMouseDown; for (int i = 0; i < 8; i++) { var row = RowSummariesTable.NewRow(); RowSummariesTable.Rows.Add(row); } NotifyPropertyChanged("RowSummariesTable"); //Group Style Start var groupStyle = FindResource("CustomGroupStyle"); GroupStyle.Add((GroupStyle) groupStyle); //Group Style End } #endregion #region Events /// /// Event is fired when the Row header is changed /// public event EventHandler RowHeaderChanged; /// /// Frozen column count changed event /// public event EventHandler FrozenColumnCountChanged; /// /// Event is fired when any any column is added in group by /// public event EventHandler GroupByColumnAdded; /// /// Event is fired when any column is removed from group by /// public event EventHandler GroupByColumnRemoved; public event PropertyChangedEventHandler PropertyChanged; #endregion #region Override protected override void OnColumnReordered(DataGridColumnEventArgs e) { UpdateColumnInfo(); base.OnColumnReordered(e); if (RowSummariesGrid != null) { //If columns are reordred its important to resorder row summaries if it exisi. var rowSummariesColumn = RowSummariesGrid.Columns.FirstOrDefault(column => column.SortMemberPath == e.Column.SortMemberPath); if (rowSummariesColumn != null) { rowSummariesColumn.DisplayIndex = e.Column.DisplayIndex; } } } protected override void OnInitialized(EventArgs e) { EventHandler sortDirectionChangedHandler = (sender, x) => UpdateColumnInfo(); EventHandler widthPropertyChangedHandler = (sender, x) => _inWidthChange = true; var sortDirectionPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.SortDirectionProperty, typeof (DataGridColumn)); var widthPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.WidthProperty, typeof (DataGridColumn)); Loaded += (sender, x) => { foreach (var column in Columns) { sortDirectionPropertyDescriptor.AddValueChanged(column, sortDirectionChangedHandler); widthPropertyDescriptor.AddValueChanged(column, widthPropertyChangedHandler); } }; Unloaded += (sender, x) => { foreach (var column in Columns) { sortDirectionPropertyDescriptor.RemoveValueChanged(column, sortDirectionChangedHandler); widthPropertyDescriptor.RemoveValueChanged(column, widthPropertyChangedHandler); } }; base.OnInitialized(e); } protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) { base.OnItemsSourceChanged(oldValue, newValue); CommitEdit(); CommitEdit(); foreach (var column in Columns) { var extendedColumn = column as IExtendedColumn; if (extendedColumn != null) { GetSummaryValue(extendedColumn); } } var cvs = CollectionViewSource.GetDefaultView(ItemsSource); if (cvs != null) cvs.GroupDescriptions.CollectionChanged += GroupDescriptionsCollectionChanged; var scrollViewer = FindControls.FindChild(this, "DG_ScrollViewer"); if (scrollViewer != null) { scrollViewer.ScrollToLeftEnd(); } InvalidateArrange(); } protected override void OnExecutedCommitEdit(ExecutedRoutedEventArgs e) { base.OnExecutedCommitEdit(e); if (e.OriginalSource == null) return; var extendedColumn = ((DataGridCell) e.OriginalSource).Column as IExtendedColumn; if (extendedColumn != null) GetSummaryValue(extendedColumn); } protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { base.OnPropertyChanged(e); switch (e.Property.Name) { case "RowHeaderWidth": if (RowHeaderChanged != null) RowHeaderChanged(this, new EventArgs()); break; case "FrozenColumnCount": if (FrozenColumnCountChanged != null) FrozenColumnCountChanged(this, new EventArgs()); break; } } #endregion #region Methods /// /// THis helps to recompute the row summaries if the data is committed to the data grid /// public void FireNotifyForRowSummaries() { NotifyPropertyChanged("RowSummariesTable"); } private void ExtendedDataGridMouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Right) { if (Columns.Count(c => c.Visibility == Visibility.Visible) == 0) { var cm = _dataGridUriStyle["DataGridHeaderColumnChooser"] as ContextMenu; if (cm != null) { cm.Tag = this; cm.IsOpen = true; } } } } private void ExtendedDataGridFrozenColumnCountChanged(object sender, EventArgs e) { if (RowSummariesGrid != null) { RowSummariesGrid.FrozenColumnCount = FrozenColumnCount; } } private void ExtendedDataGridRowHeaderChanged(object sender, EventArgs e) { ArrangeColumnSplitter(); } private void ArrangeColumnSplitter() { var grid = this; if ((!grid.ShowColumnSplitter || grid.FrozenColumnCount == 0)) return; double tillNowWidth = 0; if (GridSplitterGrid == null) return; var listOfColumn = Columns.ToList().OrderBy(c => c.DisplayIndex); foreach (var column in listOfColumn) { if (column.Visibility == Visibility.Visible && column.DisplayIndex < FrozenColumnCount) { tillNowWidth = tillNowWidth + column.ActualWidth - 0.5; } } GridSplitterGrid.ColumnDefinitions[0].Width = new GridLength(tillNowWidth + GetGridHeaderActualWidth(this)); } private double GetGridHeaderActualWidth(DataGrid grid) { if (grid.HeadersVisibility == DataGridHeadersVisibility.None || grid.HeadersVisibility == DataGridHeadersVisibility.Column) return 0; return grid.RowHeaderWidth; } private void GroupDescriptionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add) { foreach (var gc in e.NewItems) { var columnName = ((PropertyGroupDescription) gc).PropertyName; var count = GroupByCollection.Count(g => g.SortMemberPath == columnName); if (count == 0) { var column = Columns.FirstOrDefault(c => c.SortMemberPath == columnName); if (column != null) { AddGroupByColumn(column, false); if (ColumnsInGroupBy.Count(g => g.SortMemberPath == columnName) == 0) { ColumnsInGroupBy.Add(column); } } } } } else if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (var gc in e.OldItems) { var columnName = ((PropertyGroupDescription) gc).PropertyName; var count = GroupByCollection.Count(g => g.SortMemberPath == columnName); if (count > 0) { var group = GroupByCollection.First(g => g.SortMemberPath == columnName); GroupByCollection.Remove(group); var column = ColumnsInGroupBy.First(c => c.SortMemberPath == columnName); ColumnsInGroupBy.Remove(column); var cvs = CollectionViewSource.GetDefaultView(ItemsSource); if (cvs != null) cvs.Refresh(); } } } NotifyPropertyChanged("GroupByCollection"); } private void ExtendedDataGridPreviewKeyDown(object sender, KeyEventArgs e) { if (!AllowUserToCopy) { if (e.Key == Key.C) { if (Keyboard.IsKeyDown(Key.LeftCtrl) || (Keyboard.IsKeyDown(Key.RightCtrl))) { e.Handled = true; } } } } private void ExtendedDataGridGroupByColumnRemoved(object sender, EventArgs e) { var args = (GroupByEventArgs) e; var columnName = args.Column.SortMemberPath; var count = GroupByCollection.Count(gc => gc.SortMemberPath == columnName); if (count <= 0) return; var itemToBeRemoved = GroupByCollection.First(gc => gc.SortMemberPath == columnName); GroupByCollection.Remove(itemToBeRemoved); var itemToBeRemoved1 = ColumnsInGroupBy.First(gc => gc.SortMemberPath == columnName); var index = ColumnsInGroupBy.IndexOf(itemToBeRemoved1); var lastCount = ColumnsInGroupBy.Count; ColumnsInGroupBy.Remove(itemToBeRemoved1); if (ColumnsInGroupBy.Count == 0) { if (_lastFrozenRowCount != null && _lastFrozenRowCount != 0) FrozenRowCount = _lastFrozenRowCount; } var cvs = CollectionViewSource.GetDefaultView(ItemsSource); var propertyGroupDescription = cvs.GroupDescriptions.Where(gd => ((PropertyGroupDescription) (gd)).PropertyName == columnName).Cast ().FirstOrDefault(); if (propertyGroupDescription == null) return; cvs.GroupDescriptions.Remove(propertyGroupDescription); cvs.Refresh(); if (index != lastCount - 1) ArrangeGroupBy(); } internal void ArrangeGroupBy() { var listOfDataGridColumn = (from item in GroupByCollection let count = Columns.Count(c => c.SortMemberPath == item.SortMemberPath) where count > 0 select Columns[GetColumnIndex(Columns, item.SortMemberPath)]).ToList(); GroupByCollection.Clear(); foreach (var column in listOfDataGridColumn) { AddGroupByColumn(column, false); NotifyPropertyChanged("GroupByCollection"); } } private void AddGroupByColumn(DataGridColumn column, bool shouldAdd) { // If there is no drop target for a dragged column, don't allow grouping if (GroupByControlVisibility == Visibility.Collapsed) { return; } IsGroupByOn = true; var columnName = column.Header; var count = GroupByCollection.Count(gc => Equals(gc.SortMemberPath, columnName)); if (count == 0) { var newItem = new GroupByData { ColumnName = (string) columnName, Index = GroupByCollection.Count, SortDirection = Convert.ToString(column.SortDirection), SortMemberPath = column.SortMemberPath }; var border = FindControls.FindChild(this, ""); var headerPanel = FindControls.FindChild(border, "headerPanel"); var groupByContentControl = FindControls.FindChild(headerPanel, "groupByContentControl"); var group = FindControls.FindChild(groupByContentControl, "group"); if (group != null) group.UpdateLayout(); if (GroupByCollection.Count > 0 && group != null) { newItem.GridWidth = ((ContentPresenter) (@group.ItemContainerGenerator.ContainerFromIndex(GroupByCollection.Count - 1))).ActualWidth + 10; newItem.GridSecondColumnWidth = (newItem.GridWidth - GroupByCollection[GroupByCollection.Count - 1].GridWidth)/2; } if (GroupByCollection.Count == 0) { if (FrozenRowCount != null && FrozenRowCount != 0) { _lastFrozenRowCount = FrozenRowCount; FrozenRowCount = 0; } } GroupByCollection.Add(newItem); if (!shouldAdd) return; try { var cvs = CollectionViewSource.GetDefaultView(ItemsSource); cvs.GroupDescriptions.Add(new PropertyGroupDescription(column.SortMemberPath)); cvs.Refresh(); } catch (Exception) { GroupByCollection.Remove(newItem); MessageBox.Show("Cannot group by when row is being edited.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } } public void RaiseGroupByColumnRemoved(DataGridColumn column) { GroupByColumnRemoved(this, new GroupByEventArgs {Column = column}); } private void AddTheme(Themes value) { if (!UriParser.IsKnownScheme("pack")) UriParser.Register(new GenericUriParser(GenericUriParserOptions.GenericAuthority), "pack", -1); //Remove last loaded themes if (Resources.MergedDictionaries.Contains(_lastTheme)) Resources.MergedDictionaries.Remove(_lastTheme); //Add new Theme var newTheme = new ResourceDictionary { Source = new Uri(String.Format(GenericThemeUri, value.ToString()), UriKind.Relative) }; if (!Resources.MergedDictionaries.Contains(newTheme)) { Resources.MergedDictionaries.Add(newTheme); _lastTheme = newTheme; } } private void GroupByCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { var border = FindControls.FindChild(this, ""); var headerPanel = FindControls.FindChild(border, "headerPanel"); var groupByContentControl = FindControls.FindChild(headerPanel, "groupByContentControl"); var txtDragName = FindControls.FindChild(groupByContentControl, "txtDragName"); var clearButton = FindControls.FindChild