Skip to content
This repository has been archived by the owner on Sep 4, 2024. It is now read-only.

[WPF] Expand and CanResize support for TreeView columns #1023

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions Xwt.WPF/Xwt.WPFBackend/ExGridViewColumn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Data;

namespace Xwt.WPFBackend
{
public class ExGridViewColumn
: GridViewColumn
{
private readonly BindingSource bindingSource;

public ExGridViewColumn (Action onWidthUpdated)
{
bindingSource = new BindingSource (onWidthUpdated);

var widthBinding = new Binding (BindingSource.WidthPropertyName) {
Mode = BindingMode.TwoWay,
Source = bindingSource,
};
BindingOperations.SetBinding (this, WidthProperty, widthBinding);
}

public bool Expands { get; set; }

public bool CanResize {
get { return bindingSource.CanResize; }
set { bindingSource.CanResize = value; }
}

public void SetWidthForced (double width)
{
bindingSource.SetWidthForced (width);
}

class BindingSource
: INotifyPropertyChanged
{
public const string WidthPropertyName = nameof (Width);

private readonly Action onWidthUpdated;
private double width = double.NaN;

public BindingSource (Action onWidthUpdated)
{
this.onWidthUpdated = onWidthUpdated;
}

public event PropertyChangedEventHandler PropertyChanged;

public bool CanResize { get; set; }

public double Width {
get {
return width;
}
set {
if (CanResize) {
width = value;
onWidthUpdated ();
}
OnPropertyChanged (WidthPropertyName);
}
}

public void SetWidthForced (double width)
{
bool savedCanResize = CanResize;
CanResize = true;
Width = width;
CanResize = savedCanResize;
}

private void OnPropertyChanged (string name)
{
PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (name));
}
}
}
}
134 changes: 124 additions & 10 deletions Xwt.WPF/Xwt.WPFBackend/ExTreeView.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//
//
// ExTreeView.cs
//
// Author:
Expand Down Expand Up @@ -35,25 +35,28 @@
using System.Windows.Controls;
using System.Collections.Generic;
using SWC = System.Windows.Controls;
using SWM = System.Windows.Media;
using WKey = System.Windows.Input.Key;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
using System.Windows.Automation.Peers;

using System.Windows.Automation.Peers;

namespace Xwt.WPFBackend
{
using Keyboard = System.Windows.Input.Keyboard;

public class ExTreeView
: SWC.TreeView, IWpfWidget
{
public ExTreeView()
private ScrollViewer scrollViewer;
private bool needColumnWidthsUpdate;

public ExTreeView ()
{
SelectedItems = new ObservableCollection<object> ();
Loaded += new RoutedEventHandler(ExTreeView_Loaded);
}

void ExTreeView_Loaded(object sender, RoutedEventArgs e)
Loaded += OnLoaded;
View.Columns.CollectionChanged += OnColumnsCollectionChanged;
}

private void OnLoaded(object sender, RoutedEventArgs e)
{
if (SelectionMode == SWC.SelectionMode.Single)
{
Expand All @@ -65,6 +68,11 @@ void ExTreeView_Loaded(object sender, RoutedEventArgs e)
}
}

private void OnColumnsCollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
{
UpdateColumnWidths ();
}

public event EventHandler SelectedItemsChanged;

public WidgetBackend Backend
Expand Down Expand Up @@ -133,6 +141,24 @@ public ExTreeViewItem ContainerFromObject (object item)
return treeItem;
}

public void UpdateColumnWidths ()
{
needColumnWidthsUpdate = true;
InvalidateVisual ();
}

protected override void OnRenderSizeChanged (SizeChangedInfo sizeInfo)
{
UpdateColumnWidths ();
base.OnRenderSizeChanged (sizeInfo);
}

protected override void OnRender (SWM.DrawingContext drawingContext)
{
EnsureColumnWidthUpdated ();
base.OnRender (drawingContext);
}

protected override bool IsItemItsOwnContainerOverride (object item)
{
return item is ExTreeViewItem;
Expand Down Expand Up @@ -183,6 +209,94 @@ protected virtual void OnSelectedItemsPropertyChanged (DependencyPropertyChanged
var newNotifying = e.NewValue as INotifyCollectionChanged;
if (newNotifying != null)
newNotifying.CollectionChanged += SelectedItemsCollectionChanged;
}

private void EnsureColumnWidthUpdated ()
{
if (!needColumnWidthsUpdate)
return;

UpdateExpandableColumnWidths ();
needColumnWidthsUpdate = false;
}

private ScrollViewer FindScrollViewer ()
{
DependencyObject currentObj = this;
while (currentObj != null) {
var scrollViewer = FindAmongChildren<ScrollViewer> (currentObj);
if (scrollViewer == null) {
currentObj = SWM.VisualTreeHelper.GetParent (currentObj);
continue;
}

scrollViewer.ScrollChanged += OnScrollViewerScrollChanged;
return scrollViewer;
}
return null;
}

private static T FindAmongChildren<T> (DependencyObject obj)
where T : DependencyObject
{
int childrenCount = SWM.VisualTreeHelper.GetChildrenCount (obj);
for (int i = 0; i < childrenCount; i++) {
var child = SWM.VisualTreeHelper.GetChild (obj, i);
if (child is T)
return (T) child;

var result = FindAmongChildren<T> (child);
if (result != null)
return result;
}
return null;
}

private void OnScrollViewerScrollChanged (object sender, ScrollChangedEventArgs e)
{
UpdateColumnWidths ();
}

private double CalculateTreeContentWidth ()
{
if (scrollViewer == null)
scrollViewer = FindScrollViewer ();

double width = ActualWidth;
if (scrollViewer != null) {
if (scrollViewer?.ComputedVerticalScrollBarVisibility == Visibility.Visible)
width -= SystemParameters.VerticalScrollBarWidth;
}

const double HorizontalMargin = 10; // Prevents horizontal scroll bar to appear before it should
return width - HorizontalMargin;
}

private void UpdateExpandableColumnWidths ()
{
double totalWidth = CalculateTreeContentWidth ();
if (totalWidth <= 0.0)
return;

var columns = View.Columns.Select (c => (ExGridViewColumn) c);
if (!columns.Any ())
return;

var expandableColumns = columns.Where (c => c.Expands).ToList ();
var nonExpandableColumns = columns.Where (c => !c.Expands).ToList ();

if (expandableColumns.Count == 0) {
var lastColumn = columns.Last ();
expandableColumns.Add (lastColumn);
nonExpandableColumns.Remove (lastColumn);
}

double totalFixedWidth = nonExpandableColumns.Sum (c => c.ActualWidth);
double totalWidthForExpanding = totalWidth - totalFixedWidth;
double newExpandableWidth = Math.Max (totalWidthForExpanding / expandableColumns.Count, 0.0);

foreach (var column in expandableColumns)
column.SetWidthForced (newExpandableWidth);
}

private bool TraverseTree (Func<object, ExTreeViewItem, bool> action, ExTreeViewItem parent = null)
Expand Down
22 changes: 1 addition & 21 deletions Xwt.WPF/Xwt.WPFBackend/ExTreeViewItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@ namespace Xwt.WPFBackend
public class ExTreeViewItem
: TreeViewItem
{

public ExTreeViewItem()
public ExTreeViewItem ()
{
Loaded += OnLoaded;
HorizontalContentAlignment = HorizontalAlignment.Stretch;
}

Expand Down Expand Up @@ -208,24 +206,6 @@ private void FindParent()
}
}

private void OnLoaded (object sender, RoutedEventArgs routedEventArgs)
{
ItemsControl parent = ItemsControlFromItemContainer (this);
if (parent == null)
return;

int index = parent.Items.IndexOf (DataContext);
if (index != parent.Items.Count - 1)
return;

foreach (var column in this.view.View.Columns) {
if (Double.IsNaN (column.Width))
column.Width = column.ActualWidth;

column.Width = Double.NaN;
}
}

protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e)
{
//We can't allow TreeViewItem(our base class) to get this message(OnKeyDown) because it will mess with our ExTreeView handling
Expand Down
24 changes: 21 additions & 3 deletions Xwt.WPF/Xwt.WPFBackend/TreeViewBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,13 @@ public void SetSource (ITreeDataSource source, IBackend sourceBackend)

public object AddColumn (ListViewColumn column)
{
var col = new GridViewColumn ();
if (column.Expands && column.CanResize)
column.CanResize = false;

var col = new ExGridViewColumn (Tree.UpdateColumnWidths) {
Expands = column.Expands,
CanResize = column.CanResize,
};

UpdateColumn (column, col, ListViewColumnChange.Title);

Expand All @@ -251,7 +257,7 @@ public object AddColumn (ListViewColumn column)

public void UpdateColumn (ListViewColumn column, object handle, ListViewColumnChange change)
{
var col = ((GridViewColumn) handle);
var col = (ExGridViewColumn) handle;
switch (change) {
case ListViewColumnChange.Title:
col.Header = column.Title;
Expand All @@ -271,12 +277,24 @@ public void UpdateColumn (ListViewColumn column, object handle, ListViewColumnCh
}

MapColumn (column, col);

break;

case ListViewColumnChange.Alignment:
var style = new Style(typeof(GridViewColumnHeader));
style.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, Util.ToWpfHorizontalAlignment(column.Alignment)));
col.HeaderContainerStyle = style;
break;

case ListViewColumnChange.Expanding:
if (column.Expands && column.CanResize)
column.CanResize = false;
col.Expands = column.Expands;
break;

case ListViewColumnChange.CanResize:
if (column.CanResize && column.Expands)
column.Expands = false;
col.CanResize = column.CanResize;
break;
}
}
Expand Down