Browse Source

Merge pull request #5222 from antrv/fixes/datagrid-dve

DataGrid: Extracting error data from DataValidationException
pull/5283/head
Max Katz 6 years ago
committed by GitHub
parent
commit
e1a8bb2daa
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 107
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  2. 25
      src/Avalonia.Controls.DataGrid/Utils/ValidationUtil.cs

107
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -1,6 +1,6 @@
// This source is subject to the Microsoft Public License (Ms-PL). // This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved. // All other rights reserved.
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
@ -92,7 +92,7 @@ namespace Avalonia.Controls
private ContentControl _topRightCornerHeader; private ContentControl _topRightCornerHeader;
private Control _frozenColumnScrollBarSpacer; private Control _frozenColumnScrollBarSpacer;
// the sum of the widths in pixels of the scrolling columns preceding // the sum of the widths in pixels of the scrolling columns preceding
// the first displayed scrolling column // the first displayed scrolling column
private double _horizontalOffset; private double _horizontalOffset;
@ -143,7 +143,7 @@ namespace Avalonia.Controls
private object _uneditedValue; // Represents the original current cell value at the time it enters editing mode. private object _uneditedValue; // Represents the original current cell value at the time it enters editing mode.
private ICellEditBinding _currentCellEditBinding; private ICellEditBinding _currentCellEditBinding;
// An approximation of the sum of the heights in pixels of the scrolling rows preceding // An approximation of the sum of the heights in pixels of the scrolling rows preceding
// the first displayed scrolling row. Since the scrolled off rows are discarded, the grid // the first displayed scrolling row. Since the scrolled off rows are discarded, the grid
// does not know their actual height. The heights used for the approximation are the ones // does not know their actual height. The heights used for the approximation are the ones
// set as the rows were scrolled off. // set as the rows were scrolled off.
@ -162,7 +162,7 @@ namespace Avalonia.Controls
AvaloniaProperty.Register<DataGrid, bool>(nameof(CanUserReorderColumns)); AvaloniaProperty.Register<DataGrid, bool>(nameof(CanUserReorderColumns));
/// <summary> /// <summary>
/// Gets or sets a value that indicates whether the user can change /// Gets or sets a value that indicates whether the user can change
/// the column display order by dragging column headers with the mouse. /// the column display order by dragging column headers with the mouse.
/// </summary> /// </summary>
public bool CanUserReorderColumns public bool CanUserReorderColumns
@ -247,8 +247,8 @@ namespace Avalonia.Controls
/// Gets or sets the <see cref="T:System.Windows.Media.Brush" /> that is used to paint the background of odd-numbered rows. /// Gets or sets the <see cref="T:System.Windows.Media.Brush" /> that is used to paint the background of odd-numbered rows.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The brush that is used to paint the background of odd-numbered rows. The default is a /// The brush that is used to paint the background of odd-numbered rows. The default is a
/// <see cref="T:System.Windows.Media.SolidColorBrush" /> with a /// <see cref="T:System.Windows.Media.SolidColorBrush" /> with a
/// <see cref="P:System.Windows.Media.SolidColorBrush.Color" /> value of white (ARGB value #00FFFFFF). /// <see cref="P:System.Windows.Media.SolidColorBrush.Color" /> value of white (ARGB value #00FFFFFF).
/// </returns> /// </returns>
public IBrush AlternatingRowBackground public IBrush AlternatingRowBackground
@ -379,8 +379,8 @@ namespace Avalonia.Controls
public bool IsValid public bool IsValid
{ {
get { return _isValid; } get { return _isValid; }
internal set internal set
{ {
SetAndRaise(IsValidProperty, ref _isValid, value); SetAndRaise(IsValidProperty, ref _isValid, value);
PseudoClasses.Set(":invalid", !value); PseudoClasses.Set(":invalid", !value);
} }
@ -398,7 +398,7 @@ namespace Avalonia.Controls
} }
/// <summary> /// <summary>
/// Gets or sets the maximum width of columns in the <see cref="T:Avalonia.Controls.DataGrid" /> . /// Gets or sets the maximum width of columns in the <see cref="T:Avalonia.Controls.DataGrid" /> .
/// </summary> /// </summary>
public double MaxColumnWidth public double MaxColumnWidth
{ {
@ -418,7 +418,7 @@ namespace Avalonia.Controls
} }
/// <summary> /// <summary>
/// Gets or sets the minimum width of columns in the <see cref="T:Avalonia.Controls.DataGrid" />. /// Gets or sets the minimum width of columns in the <see cref="T:Avalonia.Controls.DataGrid" />.
/// </summary> /// </summary>
public double MinColumnWidth public double MinColumnWidth
{ {
@ -496,7 +496,7 @@ namespace Avalonia.Controls
AvaloniaProperty.Register<DataGrid, IBrush>(nameof(VerticalGridLinesBrush)); AvaloniaProperty.Register<DataGrid, IBrush>(nameof(VerticalGridLinesBrush));
/// <summary> /// <summary>
/// Gets or sets the <see cref="T:System.Windows.Media.Brush" /> that is used to paint grid lines separating columns. /// Gets or sets the <see cref="T:System.Windows.Media.Brush" /> that is used to paint grid lines separating columns.
/// </summary> /// </summary>
public IBrush VerticalGridLinesBrush public IBrush VerticalGridLinesBrush
{ {
@ -542,7 +542,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The index of the current selection, or -1 if the selection is empty. /// The index of the current selection, or -1 if the selection is empty.
/// </returns> /// </returns>
public int SelectedIndex public int SelectedIndex
{ {
get { return _selectedIndex; } get { return _selectedIndex; }
@ -582,7 +582,7 @@ namespace Avalonia.Controls
AvaloniaProperty.Register<DataGrid, bool>(nameof(AutoGenerateColumns)); AvaloniaProperty.Register<DataGrid, bool>(nameof(AutoGenerateColumns));
/// <summary> /// <summary>
/// Gets or sets a value that indicates whether columns are created /// Gets or sets a value that indicates whether columns are created
/// automatically when the <see cref="P:Avalonia.Controls.DataGrid.ItemsSource" /> property is set. /// automatically when the <see cref="P:Avalonia.Controls.DataGrid.ItemsSource" /> property is set.
/// </summary> /// </summary>
public bool AutoGenerateColumns public bool AutoGenerateColumns
@ -626,7 +626,7 @@ namespace Avalonia.Controls
AvaloniaProperty.Register<DataGrid, bool>(nameof(AreRowDetailsFrozen)); AvaloniaProperty.Register<DataGrid, bool>(nameof(AreRowDetailsFrozen));
/// <summary> /// <summary>
/// Gets or sets a value that indicates whether the row details sections remain /// Gets or sets a value that indicates whether the row details sections remain
/// fixed at the width of the display area or can scroll horizontally. /// fixed at the width of the display area or can scroll horizontally.
/// </summary> /// </summary>
public bool AreRowDetailsFrozen public bool AreRowDetailsFrozen
@ -881,7 +881,7 @@ namespace Avalonia.Controls
{ {
int index = (int)e.NewValue; int index = (int)e.NewValue;
// GetDataItem returns null if index is >= Count, we do not check newValue // GetDataItem returns null if index is >= Count, we do not check newValue
// against Count here to avoid enumerating through an Enumerable twice // against Count here to avoid enumerating through an Enumerable twice
// Setting SelectedItem coerces the finally value of the SelectedIndex // Setting SelectedItem coerces the finally value of the SelectedIndex
object newSelectedItem = (index < 0) ? null : DataConnection.GetDataItem(index); object newSelectedItem = (index < 0) ? null : DataConnection.GetDataItem(index);
@ -1168,14 +1168,14 @@ namespace Avalonia.Controls
} }
/// <summary> /// <summary>
/// Occurs one time for each public, non-static property in the bound data type when the /// Occurs one time for each public, non-static property in the bound data type when the
/// <see cref="P:Avalonia.Controls.DataGrid.ItemsSource" /> property is changed and the /// <see cref="P:Avalonia.Controls.DataGrid.ItemsSource" /> property is changed and the
/// <see cref="P:Avalonia.Controls.DataGrid.AutoGenerateColumns" /> property is true. /// <see cref="P:Avalonia.Controls.DataGrid.AutoGenerateColumns" /> property is true.
/// </summary> /// </summary>
public event EventHandler<DataGridAutoGeneratingColumnEventArgs> AutoGeneratingColumn; public event EventHandler<DataGridAutoGeneratingColumnEventArgs> AutoGeneratingColumn;
/// <summary> /// <summary>
/// Occurs before a cell or row enters editing mode. /// Occurs before a cell or row enters editing mode.
/// </summary> /// </summary>
public event EventHandler<DataGridBeginningEditEventArgs> BeginningEdit; public event EventHandler<DataGridBeginningEditEventArgs> BeginningEdit;
@ -1195,7 +1195,7 @@ namespace Avalonia.Controls
public event EventHandler<DataGridCellPointerPressedEventArgs> CellPointerPressed; public event EventHandler<DataGridCellPointerPressedEventArgs> CellPointerPressed;
/// <summary> /// <summary>
/// Occurs when the <see cref="P:Avalonia.Controls.DataGridColumn.DisplayIndex" /> /// Occurs when the <see cref="P:Avalonia.Controls.DataGridColumn.DisplayIndex" />
/// property of a column changes. /// property of a column changes.
/// </summary> /// </summary>
public event EventHandler<DataGridColumnEventArgs> ColumnDisplayIndexChanged; public event EventHandler<DataGridColumnEventArgs> ColumnDisplayIndexChanged;
@ -1218,14 +1218,14 @@ namespace Avalonia.Controls
public event EventHandler<EventArgs> CurrentCellChanged; public event EventHandler<EventArgs> CurrentCellChanged;
/// <summary> /// <summary>
/// Occurs after a <see cref="T:Avalonia.Controls.DataGridRow" /> /// Occurs after a <see cref="T:Avalonia.Controls.DataGridRow" />
/// is instantiated, so that you can customize it before it is used. /// is instantiated, so that you can customize it before it is used.
/// </summary> /// </summary>
public event EventHandler<DataGridRowEventArgs> LoadingRow; public event EventHandler<DataGridRowEventArgs> LoadingRow;
/// <summary> /// <summary>
/// Occurs when a cell in a <see cref="T:Avalonia.Controls.DataGridTemplateColumn" /> enters editing mode. /// Occurs when a cell in a <see cref="T:Avalonia.Controls.DataGridTemplateColumn" /> enters editing mode.
/// ///
/// </summary> /// </summary>
public event EventHandler<DataGridPreparingCellForEditEventArgs> PreparingCellForEdit; public event EventHandler<DataGridPreparingCellForEditEventArgs> PreparingCellForEdit;
@ -1243,7 +1243,7 @@ namespace Avalonia.Controls
RoutedEvent.Register<DataGrid, SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble); RoutedEvent.Register<DataGrid, SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble);
/// <summary> /// <summary>
/// Occurs when the <see cref="P:Avalonia.Controls.DataGrid.SelectedItem" /> or /// Occurs when the <see cref="P:Avalonia.Controls.DataGrid.SelectedItem" /> or
/// <see cref="P:Avalonia.Controls.DataGrid.SelectedItems" /> property value changes. /// <see cref="P:Avalonia.Controls.DataGrid.SelectedItems" /> property value changes.
/// </summary> /// </summary>
public event EventHandler<SelectionChangedEventArgs> SelectionChanged public event EventHandler<SelectionChangedEventArgs> SelectionChanged
@ -1258,19 +1258,19 @@ namespace Avalonia.Controls
public event EventHandler<DataGridColumnEventArgs> Sorting; public event EventHandler<DataGridColumnEventArgs> Sorting;
/// <summary> /// <summary>
/// Occurs when a <see cref="T:Avalonia.Controls.DataGridRow" /> /// Occurs when a <see cref="T:Avalonia.Controls.DataGridRow" />
/// object becomes available for reuse. /// object becomes available for reuse.
/// </summary> /// </summary>
public event EventHandler<DataGridRowEventArgs> UnloadingRow; public event EventHandler<DataGridRowEventArgs> UnloadingRow;
/// <summary> /// <summary>
/// Occurs when a new row details template is applied to a row, so that you can customize /// Occurs when a new row details template is applied to a row, so that you can customize
/// the details section before it is used. /// the details section before it is used.
/// </summary> /// </summary>
public event EventHandler<DataGridRowDetailsEventArgs> LoadingRowDetails; public event EventHandler<DataGridRowDetailsEventArgs> LoadingRowDetails;
/// <summary> /// <summary>
/// Occurs when the <see cref="P:Avalonia.Controls.DataGrid.RowDetailsVisibilityMode" /> /// Occurs when the <see cref="P:Avalonia.Controls.DataGrid.RowDetailsVisibilityMode" />
/// property value changes. /// property value changes.
/// </summary> /// </summary>
public event EventHandler<DataGridRowDetailsEventArgs> RowDetailsVisibilityChanged; public event EventHandler<DataGridRowDetailsEventArgs> RowDetailsVisibilityChanged;
@ -1282,7 +1282,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets a collection that contains all the columns in the control. /// Gets a collection that contains all the columns in the control.
/// </summary> /// </summary>
public ObservableCollection<DataGridColumn> Columns public ObservableCollection<DataGridColumn> Columns
{ {
get get
@ -1456,7 +1456,7 @@ namespace Avalonia.Controls
} }
// Height currently available for cells this value is smaller. This height is reduced by the existence of ColumnHeaders // Height currently available for cells this value is smaller. This height is reduced by the existence of ColumnHeaders
// or a horizontal scrollbar. Layout is asynchronous so changes to the ColumnHeaders or the horizontal scrollbar are // or a horizontal scrollbar. Layout is asynchronous so changes to the ColumnHeaders or the horizontal scrollbar are
// not reflected immediately. // not reflected immediately.
internal double CellsHeight internal double CellsHeight
{ {
@ -1555,7 +1555,7 @@ namespace Avalonia.Controls
internal static double HorizontalGridLinesThickness => DATAGRID_horizontalGridLinesThickness; internal static double HorizontalGridLinesThickness => DATAGRID_horizontalGridLinesThickness;
// the sum of the widths in pixels of the scrolling columns preceding // the sum of the widths in pixels of the scrolling columns preceding
// the first displayed scrolling column // the first displayed scrolling column
internal double HorizontalOffset internal double HorizontalOffset
{ {
@ -2083,20 +2083,20 @@ namespace Avalonia.Controls
} }
/// <summary> /// <summary>
/// Measures the children of a <see cref="T:Avalonia.Controls.DataGridRow" /> to prepare for /// Measures the children of a <see cref="T:Avalonia.Controls.DataGridRow" /> to prepare for
/// arranging them during the /// arranging them during the
/// <see cref="M:Avalonia.Controls.DataGridRow.ArrangeOverride(System.Windows.Size)" /> pass. /// <see cref="M:Avalonia.Controls.DataGridRow.ArrangeOverride(System.Windows.Size)" /> pass.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The size that the <see cref="T:Avalonia.Controls.DataGridRow" /> determines it needs during layout, based on its calculations of child object allocated sizes. /// The size that the <see cref="T:Avalonia.Controls.DataGridRow" /> determines it needs during layout, based on its calculations of child object allocated sizes.
/// </returns> /// </returns>
/// <param name="availableSize"> /// <param name="availableSize">
/// The available size that this element can give to child elements. Indicates an upper limit that /// The available size that this element can give to child elements. Indicates an upper limit that
/// child elements should not exceed. /// child elements should not exceed.
/// </param> /// </param>
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
// Delay layout until after the initial measure to avoid invalid calculations when the // Delay layout until after the initial measure to avoid invalid calculations when the
// DataGrid is not part of the visual tree // DataGrid is not part of the visual tree
if (!_measured) if (!_measured)
{ {
@ -3006,7 +3006,7 @@ namespace Avalonia.Controls
/// If the editing element has focus, this method will set focus to the DataGrid itself /// If the editing element has focus, this method will set focus to the DataGrid itself
/// in order to force the element to lose focus. It will then wait for the editing element's /// in order to force the element to lose focus. It will then wait for the editing element's
/// LostFocus event, at which point it will perform the specified action. /// LostFocus event, at which point it will perform the specified action.
/// ///
/// NOTE: It is important to understand that the specified action will be performed when the editing /// NOTE: It is important to understand that the specified action will be performed when the editing
/// element loses focus only if this method returns true. If it returns false, then the action /// element loses focus only if this method returns true. If it returns false, then the action
/// will not be performed later on, and should instead be performed by the caller, if necessary. /// will not be performed later on, and should instead be performed by the caller, if necessary.
@ -3065,7 +3065,7 @@ namespace Avalonia.Controls
{ {
if (!_scrollingByHeight) if (!_scrollingByHeight)
{ {
// Update layout when RowDetails are expanded or collapsed, just updating the vertical scroll bar is not enough // Update layout when RowDetails are expanded or collapsed, just updating the vertical scroll bar is not enough
// since rows could be added or removed // since rows could be added or removed
InvalidateMeasure(); InvalidateMeasure();
} }
@ -3278,7 +3278,7 @@ namespace Avalonia.Controls
{ {
// Current cell was reset because the commit deleted row(s). // Current cell was reset because the commit deleted row(s).
// Since the user wants to change the current cell, we don't // Since the user wants to change the current cell, we don't
// want to end up with no current cell. We pick the last row // want to end up with no current cell. We pick the last row
// in the grid which may be the 'new row'. // in the grid which may be the 'new row'.
int lastSlot = LastVisibleSlot; int lastSlot = LastVisibleSlot;
if (forCurrentCellChange && if (forCurrentCellChange &&
@ -3336,7 +3336,7 @@ namespace Avalonia.Controls
if (_ignoreNextScrollBarsLayout) if (_ignoreNextScrollBarsLayout)
{ {
_ignoreNextScrollBarsLayout = false; _ignoreNextScrollBarsLayout = false;
// //
} }
@ -3393,7 +3393,7 @@ namespace Avalonia.Controls
} }
// Now cellsWidth is the width potentially available for displaying data cells. // Now cellsWidth is the width potentially available for displaying data cells.
// Now cellsHeight is the height potentially available for displaying data cells. // Now cellsHeight is the height potentially available for displaying data cells.
bool needHorizScrollbar = false; bool needHorizScrollbar = false;
bool needVertScrollbar = false; bool needVertScrollbar = false;
@ -3418,7 +3418,7 @@ namespace Avalonia.Controls
Debug.Assert(cellsHeight >= 0); Debug.Assert(cellsHeight >= 0);
needHorizScrollbarWithoutVertScrollbar = needHorizScrollbar = true; needHorizScrollbarWithoutVertScrollbar = needHorizScrollbar = true;
if (vertScrollBarWidth > 0 && if (vertScrollBarWidth > 0 &&
allowVertScrollbar && (MathUtilities.LessThanOrClose(totalVisibleWidth - cellsWidth, vertScrollBarWidth) || allowVertScrollbar && (MathUtilities.LessThanOrClose(totalVisibleWidth - cellsWidth, vertScrollBarWidth) ||
MathUtilities.LessThanOrClose(cellsWidth - totalVisibleFrozenWidth, vertScrollBarWidth))) MathUtilities.LessThanOrClose(cellsWidth - totalVisibleFrozenWidth, vertScrollBarWidth)))
{ {
@ -3458,7 +3458,7 @@ namespace Avalonia.Controls
// we compute the number of visible columns only after we set up the vertical scroll bar. // we compute the number of visible columns only after we set up the vertical scroll bar.
ComputeDisplayedColumns(); ComputeDisplayedColumns();
if ((vertScrollBarWidth > 0 || horizScrollBarHeight > 0) && if ((vertScrollBarWidth > 0 || horizScrollBarHeight > 0) &&
allowHorizScrollbar && allowHorizScrollbar &&
needVertScrollbar && !needHorizScrollbar && needVertScrollbar && !needHorizScrollbar &&
MathUtilities.GreaterThan(totalVisibleWidth, cellsWidth) && MathUtilities.GreaterThan(totalVisibleWidth, cellsWidth) &&
@ -3963,7 +3963,8 @@ namespace Avalonia.Controls
{ {
var errorList = var errorList =
binding.ValidationErrors binding.ValidationErrors
.SelectMany(ex => ValidationUtil.UnpackException(ex)) .SelectMany(ValidationUtil.UnpackException)
.Select(ValidationUtil.UnpackDataValidationException)
.ToList(); .ToList();
DataValidationErrors.SetErrors(editingElement, errorList); DataValidationErrors.SetErrors(editingElement, errorList);
@ -4124,7 +4125,7 @@ namespace Avalonia.Controls
} }
/// <summary> /// <summary>
/// Exits editing mode without trying to commit or revert the editing, and /// Exits editing mode without trying to commit or revert the editing, and
/// without repopulating the edited row's cell. /// without repopulating the edited row's cell.
/// </summary> /// </summary>
//TODO TabStop //TODO TabStop
@ -5103,9 +5104,9 @@ namespace Avalonia.Controls
{ {
if (ctrl || _editingColumnIndex == -1 || IsReadOnly) if (ctrl || _editingColumnIndex == -1 || IsReadOnly)
{ {
//Go to the next/previous control on the page when //Go to the next/previous control on the page when
// - Ctrl key is used // - Ctrl key is used
// - Potential current cell is not edited, or the datagrid is read-only. // - Potential current cell is not edited, or the datagrid is read-only.
return false; return false;
} }
@ -5516,11 +5517,11 @@ namespace Avalonia.Controls
// v---v // v---v
//|<|_____|###|>| //|<|_____|###|>|
// ^ ^ // ^ ^
// min max // min max
// we want to make the relative size of the thumb reflect the relative size of the viewing area // we want to make the relative size of the thumb reflect the relative size of the viewing area
// viewportSize / (max + viewportSize) = cellsWidth / max // viewportSize / (max + viewportSize) = cellsWidth / max
// -> viewportSize = max * cellsWidth / (max - cellsWidth) // -> viewportSize = max * cellsWidth / (max - cellsWidth)
// always zero // always zero
_hScrollBar.Minimum = 0; _hScrollBar.Minimum = 0;
@ -5572,7 +5573,7 @@ namespace Avalonia.Controls
_hScrollBar.Maximum = 0; _hScrollBar.Maximum = 0;
if (_hScrollBar.IsVisible) if (_hScrollBar.IsVisible)
{ {
// This will trigger a call to this method via Cells_SizeChanged for // This will trigger a call to this method via Cells_SizeChanged for
// which no processing is needed. // which no processing is needed.
_hScrollBar.IsVisible = false; _hScrollBar.IsVisible = false;
_ignoreNextScrollBarsLayout = true; _ignoreNextScrollBarsLayout = true;
@ -5591,14 +5592,14 @@ namespace Avalonia.Controls
// v---v // v---v
//|<|_____|###|>| //|<|_____|###|>|
// ^ ^ // ^ ^
// min max // min max
// we want to make the relative size of the thumb reflect the relative size of the viewing area // we want to make the relative size of the thumb reflect the relative size of the viewing area
// viewportSize / (max + viewportSize) = cellsWidth / max // viewportSize / (max + viewportSize) = cellsWidth / max
// -> viewportSize = max * cellsHeight / (totalVisibleHeight - cellsHeight) // -> viewportSize = max * cellsHeight / (totalVisibleHeight - cellsHeight)
// -> = max * cellsHeight / (totalVisibleHeight - cellsHeight) // -> = max * cellsHeight / (totalVisibleHeight - cellsHeight)
// -> = max * cellsHeight / max // -> = max * cellsHeight / max
// -> = cellsHeight // -> = cellsHeight
// always zero // always zero
_vScrollBar.Minimum = 0; _vScrollBar.Minimum = 0;
@ -5621,7 +5622,7 @@ namespace Avalonia.Controls
if (!_vScrollBar.IsVisible) if (!_vScrollBar.IsVisible)
{ {
// This will trigger a call to this method via Cells_SizeChanged for // This will trigger a call to this method via Cells_SizeChanged for
// which no processing is needed. // which no processing is needed.
_vScrollBar.IsVisible = true; _vScrollBar.IsVisible = true;
if (_vScrollBar.DesiredSize.Width == 0) if (_vScrollBar.DesiredSize.Width == 0)
@ -5637,7 +5638,7 @@ namespace Avalonia.Controls
_vScrollBar.Maximum = 0; _vScrollBar.Maximum = 0;
if (_vScrollBar.IsVisible) if (_vScrollBar.IsVisible)
{ {
// This will trigger a call to this method via Cells_SizeChanged for // This will trigger a call to this method via Cells_SizeChanged for
// which no processing is needed. // which no processing is needed.
_vScrollBar.IsVisible = false; _vScrollBar.IsVisible = false;
_ignoreNextScrollBarsLayout = true; _ignoreNextScrollBarsLayout = true;
@ -5660,8 +5661,8 @@ namespace Avalonia.Controls
Debug.Assert(slot >= 0); Debug.Assert(slot >= 0);
// Before changing selection, check if the current cell needs to be committed, and // Before changing selection, check if the current cell needs to be committed, and
// check if the current row needs to be committed. If any of those two operations are required and fail, // check if the current row needs to be committed. If any of those two operations are required and fail,
// do not change selection, and do not change current cell. // do not change selection, and do not change current cell.
bool wasInEdit = EditingColumnIndex != -1; bool wasInEdit = EditingColumnIndex != -1;

25
src/Avalonia.Controls.DataGrid/Utils/ValidationUtil.cs

@ -80,19 +80,24 @@ namespace Avalonia.Controls.Utils
{ {
if (exception != null) if (exception != null)
{ {
var aggregate = exception as AggregateException; var exceptions = exception is AggregateException aggregate ?
var exceptions = aggregate == null ? aggregate.InnerExceptions :
(IEnumerable<Exception>)new[] { exception } : (IEnumerable<Exception>)new[] { exception };
aggregate.InnerExceptions;
var filtered = exceptions.Where(x => !(x is BindingChainException)).ToList();
if (filtered.Count > 0) return exceptions.Where(x => !(x is BindingChainException)).ToList();
{
return filtered;
}
} }
return null; return Array.Empty<Exception>();
}
public static object UnpackDataValidationException(Exception exception)
{
if (exception is DataValidationException dataValidationException)
{
return dataValidationException.ErrorData;
}
return exception;
} }
/// <summary> /// <summary>

Loading…
Cancel
Save