/// <summary> /// 在结点被收缩起来时,应该它的子结点的所有数据在_objectItems中对应的项都移除。 /// </summary> /// <param name="treeNode"></param> private void OnNodeCollapsed(TreeGridRow treeNode) { //处理 LazyLoading //结点被折叠后事件。 //如果懒加载开启后,则在折叠时清空所有子结点。 //不处理根结点。 if (treeNode != this.RootNode) { //更新布局 var itemKey = GetId(treeNode.DataContext); _renderExpansion.Remove(itemKey); if (this.IsLazyLoading && this.ClearCollapsedNodes && treeNode.Items.Count > 0) { //deregisters listeners for all ancestors if (this.ObserveChildItems) { _monitor.RemoveNodes(treeNode.Items); } treeNode.ItemsSource = null; TreeGridHelper.CreateDummyItem(treeNode); //TODO do we have collapsed event if all childs are being filtered? //if yes, we need to set the dummy nodes visibility depending //on the filtered childs (visible if at least 1 visible child) } } }
private static void TreeGridRow_Collapsed(object sender, RoutedEventArgs e) { var row = e.OriginalSource as TreeGridRow; if (!TreeGridHelper.IsDummyItem(row)) { (sender as TreeGrid).OnNodeCollapsed(row); } }
private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var sender = (TreeGridHeaderRowPresenter)d; if (e.Property == ColumnHeaderTemplateProperty || e.Property == ColumnHeaderTemplateSelectorProperty) { TreeGridHelper.CheckTemplateAndTemplateSelector("TreeGridHeaderRowPresenter", ColumnHeaderTemplateProperty, ColumnHeaderTemplateSelectorProperty, sender); } sender.UpdateAllHeaders(e.Property); }
protected override void OnPreviewKeyDown(KeyEventArgs e) { base.OnPreviewKeyDown(e); //在编辑控件按 Tab 切换到下一列 if (e.Key == Key.Tab) { bool moveNext = !TreeGridHelper.IsShiftPressed(); this.Column.EditNextColumnOnTabKey(this, moveNext, e, this.Column); } }
/// <summary> /// 在列的项变化时,同步变化对应的单元格列表 UIChildren。 /// </summary> /// <param name="e"></param> private void OnColumnCollectionChanged(TreeGridColumnCollectionChangedEventArgs e) { TreeGridHelper.UpdateDesiredWidthListOnColumnChanged(ref this._desiredWidthList, e); if (this.TryConnect()) { this._cellsPresenter.UpdateItemsSourceOnColumnsChanged(e); base.InvalidateMeasure(); } }
/// <summary> /// 折叠起那些不是当前选择行或其父节点的所有行。 /// </summary> private void ApplyAutoCollapse() { if (!this.AutoCollapse) { return; } object selected = this.SelectedItem; var itemsControl = this.RootItemsControl; if (selected == null) { //if we don't have a selected item, just collapse the //root items foreach (TreeGridRow node in TreeGridHelper.TraverseRows(itemsControl)) { node.IsExpanded = false; } } else { var parents = this.GetParentItemList(selected); foreach (object parent in parents) { var parentNode = this.FindChildRow(parent, itemsControl); if (parentNode == null) { string msg = "Cannot collapse item '{0}' - the item does not exist in the hierarchy of the tree's bound items."; msg = String.Format(msg, GetId(parent)); throw new InvalidOperationException(msg); } foreach (TreeGridRow item in TreeGridHelper.TraverseRows(itemsControl)) { //collapse all items that are no ancestors if (item == parentNode) { continue; } item.IsExpanded = false; } //go a level deeper itemsControl = parentNode; } //finally collapse the item and its siblings foreach (TreeGridRow item in TreeGridHelper.TraverseRows(itemsControl)) { item.IsExpanded = false; } } }
/// <summary> /// 选择核心逻辑。 /// /// 处理多选及单选状态下的 SelectionModel 中数据状态。 /// </summary> /// <param name="oldItem"></param> /// <param name="newItem"></param> private void SelectionCore(object oldItem, object newItem) { var ctrlPressed = TreeGridHelper.IsCtrlPressed(); var shiftPressed = TreeGridHelper.IsShiftPressed(); //当前是否正处于多选模式下。 var inMultiSelectionMode = this._forceMultiSelect || ctrlPressed || shiftPressed; this.ShiftSelectStart(shiftPressed, oldItem, newItem); //如果不是多选状态,则先清空选中行状态。 var sm = this.SelectionModel; if (!inMultiSelectionMode) { sm.ClearSelectedItems(); } if (newItem != null) { if (ctrlPressed || this._forceMultiSelect) { //反选该行 var selected = sm.InnerItems.Contains(newItem); sm.MarkItemSelected(newItem, !selected); } else if (shiftPressed) { sm.ClearSelectedItems(); if (this._shiftStartItem != null) { this.ShiftSelectEnd(newItem); } } else { sm.MarkItemSelected(newItem, true); } } else { if (!this._forceMultiSelect) { sm.ClearSelectedItems(); } } }
/// <summary> /// 让当前所有行的单元格重新测量。 /// </summary> private void InvalidateCells() { var grid = this.TreeGrid; if (grid != null && grid.IsColumnsVirtualizingEnabled) { var rows = TreeGridHelper.TraverseRows(grid); foreach (var item in rows) { if (item.CellsPanel != null) { item.CellsPanel.InvalidateMeasure(); } } } }
/// <summary> /// Updates the tree with newly added items. /// </summary> /// <param name="observed">The observed collection.</param> /// <param name="e">Collection event args.</param> public void HandleNewChildItems(ICollection observed, NotifyCollectionChangedEventArgs e) { IList childs = e.NewItems; if (childs.Count == 0) { return; } //get the node of the parent item that contains the evented childs var parentNode = GetParentNode(childs[0], observed); if (parentNode == null) { return; } //if the node is expanded or does not load lazily, or //already contains valid items, create nodes if (parentNode.IsExpanded || !tree.IsLazyLoading || !TreeGridHelper.ContainsDummyNode(parentNode)) { var rawLayout = tree._renderExpansion; try { tree._renderExpansion = null; parentNode.ItemsSource = childs; } finally { tree._renderExpansion = rawLayout; } //refresh the node in order to apply sorting (or any other //features that will be incorporated) parentNode.Items.Refresh(); } else if (parentNode.Items.Count == 0) { //if the tree is in lazy loading mode and the item did //not contain any childs before, we have to add a dummy //node to render a expander parentNode.Items.Add(TreeGridHelper.CreateDummyItem()); } }
private void OnNodeExpanded(TreeGridRow treeNode) { //处理 LazyLoading //结点被展开后事件 //完成懒加载时的结点创建工作,并设置各结点的 Filter 属性。 object item = treeNode.DataContext; if (item != null) { //布局 var itemKey = GetId(item); _renderExpansion.Add(itemKey); var nodeItems = treeNode.Items; //懒加载状态下,创建结点。 if (this.IsLazyLoading) { TreeGridHelper.ClearDummyChildNode(treeNode); if (nodeItems.Count == 0) { var childItems = this.GetChildItems(item); treeNode.ItemsSource = childItems; //刷新以排序 if (nodeItems.NeedsRefresh) { nodeItems.Refresh(); } } } if (nodeItems.Count == 0) { //如果结点没有数据,则不需要展开它。 treeNode.IsExpanded = false; _renderExpansion.Remove(itemKey); } } }
/// <summary> /// Updates the tree if items were removed from an observed /// child collection. This might cause rendered tree nodes /// to be removed. In case lazy loading is enabled, the update /// of the UI may be as subtle as to remove an expander from /// a collapsed node if the represented item's childs were /// removed (or the childs that pass an active filter).<br/> /// </summary> /// <param name="observed">The observed collection.</param> /// <param name="e">The event arguments that provide the /// removed items.</param> private void HandleRemovedChildItems(ICollection observed, NotifyCollectionChangedEventArgs e) { IList childs = e.OldItems; if (childs.Count == 0) { return; } //get the node of the parent item that contains the evented childs var parentNode = GetParentNode((object)childs[0], observed); if (parentNode == null) { return; } foreach (object childItem in childs) { //check if we have a corresponding open node //-> not necessarily the case if we're doing lazy loading var childRow = tree.FindChildRow(childItem, parentNode); if (childRow != null) { //unregister listeners UnregisterListeners(childRow); //remove node from UI parentNode.Items.Remove(childRow); } } //in case of lazy loading, the tree might contain a dummy node //(has not been expanded). However, it might be that it's now //completely empty... if (observed.Count == 0) { TreeGridHelper.ClearDummyChildNode(parentNode); parentNode.IsExpanded = false; } }
/// <summary> /// 测量所有已经生成的单元格。 /// </summary> /// <param name="columns"></param> /// <param name="availableSize"></param> /// <returns></returns> private Size MeasureChild(TreeGridColumnCollection columns, Size availableSize) { double maxWidth = availableSize.Width; double maxHeight = availableSize.Height; double rowDesiredHeight = 0.0; //行最终需要的高度 double rowDesiredWidth = 0.0; //行最终需要的宽度 bool firstTime = true; //是否在 Init 状态下还没有初始化过 DesiredWidthList。 var cells = this.InternalChildren; for (int i = this._from, j = 0; i <= this._to; i++, j++) { var column = columns[i]; //当前行还可用的宽度 double rowAvaiableWidth = Math.Max(0, maxWidth - rowDesiredWidth); //第一列额外元素需要的宽度 double headersDesiredWidth = 0; #region 测量第一列额外元素需要的宽度 if (i == 0) { //测量 RowHeader if (this.ShowRowHeader) { headersDesiredWidth += this.RowHeaderWidth; rowAvaiableWidth -= this.RowHeaderWidth; //this._rowHeaderContainer.Measure(new Size(columnWidth, maxHeight)); //x += this._rowHeaderContainer.DesiredSize.Width; //columnWidth -= this._rowHeaderContainer.DesiredSize.Width; } //留下 FirstColumnIndent var firstColumnIndent = Math.Min(this.FirstColumnIndent, rowAvaiableWidth); headersDesiredWidth += firstColumnIndent; rowAvaiableWidth -= firstColumnIndent; //测量 Expander if (this.ShowExpander) { var expander = this.Expander; expander.Measure(new Size(rowAvaiableWidth, maxHeight)); headersDesiredWidth += expander.DesiredSize.Width; rowAvaiableWidth -= expander.DesiredSize.Width; } } #endregion var cell = cells[j]; //测量单元格 var state = column.State; if (state == ColumnMeasureState.Init || state == ColumnMeasureState.Headered) { if (firstTime) { TreeGridHelper.EnsureDesiredWidthList(ref this._desiredWidthList, this._treeGrid.Columns); this.LayoutUpdated += this.OnMeasureLayoutUpdated; firstTime = false; } cell.Measure(new Size(rowAvaiableWidth, maxHeight)); //只有在虚拟化后的当前页面时,才影响动态宽度。 if (this._cellsPresenter.IsOnCurrentPage) { //当前列需要的宽度应该是列的宽度加额外元素的宽度。 column.EnsureDataWidth(cell.DesiredSize.Width + headersDesiredWidth); } this._desiredWidthList[column.StableIndex] = column.DesiredDataWidth; rowDesiredWidth += column.DesiredDataWidth; } else { var actualWidth = column.CalculateActualWidth(); rowAvaiableWidth = Math.Min(rowAvaiableWidth, actualWidth - headersDesiredWidth); cell.Measure(new Size(rowAvaiableWidth, maxHeight)); rowDesiredWidth += actualWidth; } rowDesiredHeight = Math.Max(rowDesiredHeight, cell.DesiredSize.Height); } rowDesiredWidth += TreeGridHeaderRowPresenter.c_EndPadding; this._cellsPresenter.IsOnCurrentPageValid = false; return(new Size(rowDesiredWidth, rowDesiredHeight)); }
private void OnColumnHeaderTemplateSelectorChanged(DependencyPropertyChangedEventArgs e) { TreeGridHelper.CheckTemplateAndTemplateSelector("GridViewColumnHeader", GridView.ColumnHeaderTemplateProperty, GridView.ColumnHeaderTemplateSelectorProperty, this); }
/// <summary> /// 保证 _desiredWidthList 与 columns 长度一致。 /// </summary> internal void EnsureDesiredWidthList() { TreeGridHelper.EnsureDesiredWidthList(ref this._desiredWidthList, this.Columns); }
/// <summary> /// 当前 Columns 集合发生变更时,执行此回调。 /// </summary> /// <param name="e"></param> internal virtual void OnColumnCollectionChanged(TreeGridColumnCollectionChangedEventArgs e) { TreeGridHelper.UpdateDesiredWidthListOnColumnChanged(ref this._desiredWidthList, e); }
private void OnHeaderTemplateSelectorChanged(DependencyPropertyChangedEventArgs e) { TreeGridHelper.CheckTemplateAndTemplateSelector("Header", HeaderTemplateProperty, HeaderTemplateSelectorProperty, this); this.OnPropertyChanged(TreeGridColumn.HeaderTemplateSelectorProperty.Name); }