A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

433 lines
14 KiB

// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Media;
using System;
using System.Diagnostics;
using System.Reactive.Linq;
namespace Avalonia.Controls
{
public class DataGridRowGroupHeader : TemplatedControl
{
private const string DATAGRIDROWGROUPHEADER_expanderButton = "ExpanderButton";
private const string DATAGRIDROWGROUPHEADER_indentSpacer = "IndentSpacer";
private const string DATAGRIDROWGROUPHEADER_itemCountElement = "ItemCountElement";
private const string DATAGRIDROWGROUPHEADER_propertyNameElement = "PropertyNameElement";
private bool _areIsCheckedHandlersSuspended;
private ToggleButton _expanderButton;
private DataGridRowHeader _headerElement;
private Control _indentSpacer;
private TextBlock _itemCountElement;
private TextBlock _propertyNameElement;
private Panel _rootElement;
private double _totalIndent;
public static readonly StyledProperty<bool> IsItemCountVisibleProperty =
AvaloniaProperty.Register<DataGridRowGroupHeader, bool>(nameof(IsItemCountVisible));
/// <summary>
/// Gets or sets a value that indicates whether the item count is visible.
/// </summary>
public bool IsItemCountVisible
{
get { return GetValue(IsItemCountVisibleProperty); }
set { SetValue(IsItemCountVisibleProperty, value); }
}
public static readonly StyledProperty<string> PropertyNameProperty =
AvaloniaProperty.Register<DataGridRowGroupHeader, string>(nameof(PropertyName));
/// <summary>
/// Gets or sets the name of the property that this <see cref="T:Avalonia.Controls.DataGrid" /> row is bound to.
/// </summary>
public string PropertyName
{
get { return GetValue(PropertyNameProperty); }
set { SetValue(PropertyNameProperty, value); }
}
public static readonly StyledProperty<bool> IsPropertyNameVisibleProperty =
AvaloniaProperty.Register<DataGridRowGroupHeader, bool>(nameof(IsPropertyNameVisible));
/// <summary>
/// Gets or sets a value that indicates whether the property name is visible.
/// </summary>
public bool IsPropertyNameVisible
{
get { return GetValue(IsPropertyNameVisibleProperty); }
set { SetValue(IsPropertyNameVisibleProperty, value); }
}
public static readonly StyledProperty<double> SublevelIndentProperty =
AvaloniaProperty.Register<DataGridRowGroupHeader, double>(
nameof(SublevelIndent),
defaultValue: DataGrid.DATAGRID_defaultRowGroupSublevelIndent,
validate: IsValidSublevelIndent);
private static bool IsValidSublevelIndent(double value)
{
return !double.IsNaN(value) && !double.IsInfinity(value) && value >= 0;
}
/// <summary>
/// Gets or sets a value that indicates the amount that the
/// children of the <see cref="T:Avalonia.Controls.RowGroupHeader" /> are indented.
/// </summary>
public double SublevelIndent
{
get { return GetValue(SublevelIndentProperty); }
set { SetValue(SublevelIndentProperty, value); }
}
private void OnSublevelIndentChanged(AvaloniaPropertyChangedEventArgs e)
{
if (OwningGrid != null)
{
OwningGrid.OnSublevelIndentUpdated(this, (double)e.NewValue);
}
}
static DataGridRowGroupHeader()
{
SublevelIndentProperty.Changed.AddClassHandler<DataGridRowGroupHeader>((x,e) => x.OnSublevelIndentChanged(e));
}
/// <summary>
/// Constructs a DataGridRowGroupHeader
/// </summary>
public DataGridRowGroupHeader()
{
//DefaultStyleKey = typeof(DataGridRowGroupHeader);
AddHandler(InputElement.PointerPressedEvent, (s, e) => DataGridRowGroupHeader_PointerPressed(e), handledEventsToo: true);
}
internal DataGridRowHeader HeaderCell
{
get
{
return _headerElement;
}
}
private bool IsCurrent
{
get
{
Debug.Assert(OwningGrid != null);
return (RowGroupInfo.Slot == OwningGrid.CurrentSlot);
}
}
private bool IsMouseOver
{
get;
set;
}
internal bool IsRecycled
{
get;
set;
}
internal int Level
{
get;
set;
}
internal DataGrid OwningGrid
{
get;
set;
}
internal DataGridRowGroupInfo RowGroupInfo
{
get;
set;
}
internal double TotalIndent
{
set
{
_totalIndent = value;
if (_indentSpacer != null)
{
_indentSpacer.Width = _totalIndent;
}
}
}
private IDisposable _expanderButtonSubscription;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
_rootElement = e.NameScope.Find<Panel>(DataGridRow.DATAGRIDROW_elementRoot);
_expanderButtonSubscription?.Dispose();
_expanderButton = e.NameScope.Find<ToggleButton>(DATAGRIDROWGROUPHEADER_expanderButton);
if(_expanderButton != null)
{
EnsureExpanderButtonIsChecked();
_expanderButtonSubscription =
_expanderButton.GetObservable(ToggleButton.IsCheckedProperty)
.Skip(1)
.Subscribe(v => OnExpanderButtonIsCheckedChanged(v));
}
_headerElement = e.NameScope.Find<DataGridRowHeader>(DataGridRow.DATAGRIDROW_elementRowHeader);
if(_headerElement != null)
{
_headerElement.Owner = this;
EnsureHeaderVisibility();
}
_indentSpacer = e.NameScope.Find<Control>(DATAGRIDROWGROUPHEADER_indentSpacer);
if(_indentSpacer != null)
{
_indentSpacer.Width = _totalIndent;
}
_itemCountElement = e.NameScope.Find<TextBlock>(DATAGRIDROWGROUPHEADER_itemCountElement);
_propertyNameElement = e.NameScope.Find<TextBlock>(DATAGRIDROWGROUPHEADER_propertyNameElement);
UpdateTitleElements();
}
internal void ApplyHeaderStatus()
{
if (_headerElement != null && OwningGrid.AreRowHeadersVisible)
{
_headerElement.ApplyOwnerStatus();
}
}
//TODO Implement
internal void ApplyState(bool useTransitions)
{
}
protected override Size ArrangeOverride(Size finalSize)
{
if (OwningGrid == null)
{
return base.ArrangeOverride(finalSize);
}
Size size = base.ArrangeOverride(finalSize);
if (_rootElement != null)
{
if (OwningGrid.AreRowGroupHeadersFrozen)
{
foreach (Control child in _rootElement.Children)
{
child.Clip = null;
}
}
else
{
double frozenLeftEdge = 0;
foreach (Control child in _rootElement.Children)
{
if (DataGridFrozenGrid.GetIsFrozen(child) && child.IsVisible)
{
TranslateTransform transform = new TranslateTransform();
// Automatic layout rounding doesn't apply to transforms so we need to Round this
transform.X = Math.Round(OwningGrid.HorizontalOffset);
child.RenderTransform = transform;
double childLeftEdge = child.Translate(this, new Point(child.Bounds.Width, 0)).X - transform.X;
frozenLeftEdge = Math.Max(frozenLeftEdge, childLeftEdge + OwningGrid.HorizontalOffset);
}
}
// Clip the non-frozen elements so they don't overlap the frozen ones
foreach (Control child in _rootElement.Children)
{
if (!DataGridFrozenGrid.GetIsFrozen(child))
{
EnsureChildClip(child, frozenLeftEdge);
}
}
}
}
return size;
}
internal void ClearFrozenStates()
{
if (_rootElement != null)
{
foreach (Control child in _rootElement.Children)
{
child.RenderTransform = null;
}
}
}
//TODO TabStop
private void DataGridRowGroupHeader_PointerPressed(PointerPressedEventArgs e)
{
if (OwningGrid != null && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
if (OwningGrid.IsDoubleClickRecordsClickOnCall(this) && !e.Handled)
{
ToggleExpandCollapse(!RowGroupInfo.IsVisible, true);
e.Handled = true;
}
else
{
//if (!e.Handled && OwningGrid.IsTabStop)
if (!e.Handled)
{
OwningGrid.Focus();
}
e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, OwningGrid.CurrentColumnIndex, RowGroupInfo.Slot, allowEdit: false);
}
}
}
private void EnsureChildClip(Visual child, double frozenLeftEdge)
{
double childLeftEdge = child.Translate(this, new Point(0, 0)).X;
if (frozenLeftEdge > childLeftEdge)
{
double xClip = Math.Round(frozenLeftEdge - childLeftEdge);
var rg = new RectangleGeometry();
rg.Rect =
new Rect(xClip, 0,
Math.Max(0, child.Bounds.Width - xClip),
child.Bounds.Height);
child.Clip = rg;
}
else
{
child.Clip = null;
}
}
internal void EnsureExpanderButtonIsChecked()
{
if (_expanderButton != null && RowGroupInfo != null && RowGroupInfo.CollectionViewGroup != null &&
RowGroupInfo.CollectionViewGroup.ItemCount != 0)
{
SetIsCheckedNoCallBack(RowGroupInfo.IsVisible);
}
}
//TODO Styles
//internal void EnsureHeaderStyleAndVisibility(Style previousStyle)
internal void EnsureHeaderVisibility()
{
if (_headerElement != null && OwningGrid != null)
{
_headerElement.IsVisible = OwningGrid.AreColumnHeadersVisible;
}
}
private void OnExpanderButtonIsCheckedChanged(bool? value)
{
if(!_areIsCheckedHandlersSuspended)
{
ToggleExpandCollapse(value ?? false, true);
}
}
internal void LoadVisualsForDisplay()
{
EnsureExpanderButtonIsChecked();
EnsureHeaderVisibility();
ApplyState(useTransitions: false);
ApplyHeaderStatus();
}
protected override void OnPointerEnter(PointerEventArgs e)
{
if (IsEnabled)
{
IsMouseOver = true;
ApplyState(useTransitions: true);
}
base.OnPointerEnter(e);
}
protected override void OnPointerLeave(PointerEventArgs e)
{
if (IsEnabled)
{
IsMouseOver = false;
ApplyState(useTransitions: true);
}
base.OnPointerLeave(e);
}
private void SetIsCheckedNoCallBack(bool value)
{
if (_expanderButton != null && _expanderButton.IsChecked != value)
{
_areIsCheckedHandlersSuspended = true;
try
{
_expanderButton.IsChecked = value;
}
finally
{
_areIsCheckedHandlersSuspended = false;
}
}
}
internal void ToggleExpandCollapse(bool isVisible, bool setCurrent)
{
if (RowGroupInfo.CollectionViewGroup.ItemCount != 0)
{
if (OwningGrid == null)
{
// Do these even if the OwningGrid is null in case it could improve the Designer experience for a standalone DataGridRowGroupHeader
RowGroupInfo.IsVisible = isVisible;
}
else if(RowGroupInfo.IsVisible != isVisible)
{
OwningGrid.OnRowGroupHeaderToggled(this, isVisible, setCurrent);
}
EnsureExpanderButtonIsChecked();
ApplyState(true /*useTransitions*/);
}
}
internal void UpdateTitleElements()
{
if (_propertyNameElement != null)
{
string txt;
if (string.IsNullOrWhiteSpace(PropertyName))
txt = String.Empty;
else
txt = String.Format("{0}:", PropertyName);
_propertyNameElement.Text = txt;
}
if (_itemCountElement != null && RowGroupInfo != null && RowGroupInfo.CollectionViewGroup != null)
{
string formatString;
if(RowGroupInfo.CollectionViewGroup.ItemCount == 1)
formatString = "({0} Item)";
else
formatString = "({0} Items)";
_itemCountElement.Text = String.Format(formatString, RowGroupInfo.CollectionViewGroup.ItemCount);
}
}
}
}