From 8526d98d28d90912bd4ea8e5712d733d0ebc0448 Mon Sep 17 00:00:00 2001 From: 0x90d <46010672+0x90d@users.noreply.github.com> Date: Tue, 10 Aug 2021 03:35:43 +0200 Subject: [PATCH 01/18] Fix datagrid right click selection --- src/Avalonia.Controls.DataGrid/DataGrid.cs | 35 +++++++++++++++++ .../DataGridCell.cs | 39 ++++++++++++------- .../DataGridRowGroupHeader.cs | 15 ++++++- .../DataGridRowHeader.cs | 17 +++++++- 4 files changed, 90 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 2f170c617d..00ae1b0e7d 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -3002,6 +3002,12 @@ namespace Avalonia.Controls } } + //TODO: Ensure right button is checked for + internal bool UpdateStateOnMouseRightButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit) + { + KeyboardHelper.GetMetaKeyState(pointerPressedEventArgs.KeyModifiers, out bool ctrl, out bool shift); + return UpdateStateOnMouseRightButtonDown(pointerPressedEventArgs, columnIndex, slot, allowEdit, shift, ctrl); + } //TODO: Ensure left button is checked for internal bool UpdateStateOnMouseLeftButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit) { @@ -5674,6 +5680,35 @@ namespace Avalonia.Controls VerticalScroll?.Invoke(sender, e); } + //TODO: Ensure right button is checked for + private bool UpdateStateOnMouseRightButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit, bool shift, bool ctrl) + { + Debug.Assert(slot >= 0); + + if (shift || ctrl) + { + return true; + } + if (IsSlotOutOfBounds(slot)) + { + return true; + } + if (GetRowSelection(slot)) + { + return true; + } + // Unselect everything except the row that was clicked on + try + { + UpdateSelectionAndCurrency(columnIndex, slot, DataGridSelectionAction.SelectCurrent, scrollIntoView: false); + } + finally + { + NoSelectionChangeCount--; + } + return true; + } + //TODO: Ensure left button is checked for private bool UpdateStateOnMouseLeftButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit, bool shift, bool ctrl) { diff --git a/src/Avalonia.Controls.DataGrid/DataGridCell.cs b/src/Avalonia.Controls.DataGrid/DataGridCell.cs index 0de4612958..1029d8ce25 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridCell.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridCell.cs @@ -161,21 +161,34 @@ namespace Avalonia.Controls private void DataGridCell_PointerPressed(PointerPressedEventArgs e) { // OwningGrid is null for TopLeftHeaderCell and TopRightHeaderCell because they have no OwningRow - if (OwningGrid != null) + if (OwningGrid == null) { - OwningGrid.OnCellPointerPressed(new DataGridCellPointerPressedEventArgs(this, OwningRow, OwningColumn, e)); - if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + return; + } + OwningGrid.OnCellPointerPressed(new DataGridCellPointerPressedEventArgs(this, OwningRow, OwningColumn, e)); + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + { + if (!e.Handled) + //if (!e.Handled && OwningGrid.IsTabStop) { - if (!e.Handled) - //if (!e.Handled && OwningGrid.IsTabStop) - { - OwningGrid.Focus(); - } - if (OwningRow != null) - { - e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled); - OwningGrid.UpdatedStateOnMouseLeftButtonDown = true; - } + OwningGrid.Focus(); + } + if (OwningRow != null) + { + e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled); + OwningGrid.UpdatedStateOnMouseLeftButtonDown = true; + } + } + else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed) + { + if (!e.Handled) + //if (!e.Handled && OwningGrid.IsTabStop) + { + OwningGrid.Focus(); + } + if (OwningRow != null) + { + e.Handled = OwningGrid.UpdateStateOnMouseRightButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled); } } } diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs index 1e03b134b1..49ca23d34c 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs @@ -283,7 +283,11 @@ namespace Avalonia.Controls //TODO TabStop private void DataGridRowGroupHeader_PointerPressed(PointerPressedEventArgs e) { - if (OwningGrid != null && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + if (OwningGrid == null) + { + return; + } + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) { if (OwningGrid.IsDoubleClickRecordsClickOnCall(this) && !e.Handled) { @@ -300,6 +304,15 @@ namespace Avalonia.Controls e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, OwningGrid.CurrentColumnIndex, RowGroupInfo.Slot, allowEdit: false); } } + else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed) + { + if (!e.Handled) + { + OwningGrid.Focus(); + } + e.Handled = OwningGrid.UpdateStateOnMouseRightButtonDown(e, OwningGrid.CurrentColumnIndex, RowGroupInfo.Slot, allowEdit: false); + } + } private void EnsureChildClip(Visual child, double frozenLeftEdge) diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs index 0cd3589a57..510072174f 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs @@ -179,12 +179,12 @@ namespace Avalonia.Controls.Primitives //TODO TabStop private void DataGridRowHeader_PointerPressed(object sender, PointerPressedEventArgs e) { - if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + if (OwningGrid == null) { return; } - if (OwningGrid != null) + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) { if (!e.Handled) //if (!e.Handled && OwningGrid.IsTabStop) @@ -199,6 +199,19 @@ namespace Avalonia.Controls.Primitives OwningGrid.UpdatedStateOnMouseLeftButtonDown = true; } } + else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed) + { + if (!e.Handled) + { + OwningGrid.Focus(); + } + if (OwningRow != null) + { + Debug.Assert(sender is DataGridRowHeader); + Debug.Assert(sender == this); + e.Handled = OwningGrid.UpdateStateOnMouseRightButtonDown(e, -1, Slot, false); + } + } } } From 7a027792cbfc4426bea431ea4b24268ef68c68a3 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Mon, 30 Aug 2021 18:39:21 +0300 Subject: [PATCH 02/18] add failing test --- .../Avalonia.Base.UnitTests.csproj | 1 + .../Logging/LoggingTests.cs | 214 ++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj index 32f90d5cbf..c4c1f49346 100644 --- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj +++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj @@ -14,6 +14,7 @@ + diff --git a/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs b/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs new file mode 100644 index 0000000000..193d5e3a45 --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using Avalonia.Logging; +using Avalonia.Markup.Xaml; +using Avalonia.UnitTests; +using Avalonia.Utilities; +using Xunit; + +namespace Avalonia.Base.UnitTests.Logging +{ + public class LoggingTests + { + [Fact] + public void Control_Should_Not_Log_Binding_Errors_When_Detached_From_Visual_Tree() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + +"; + + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + using var logSink = new StubLogSink(LogEventLevel.Warning); + var panel = window.FindControl("panel"); + var rect = window.FindControl("rect"); + window.ApplyTemplate(); + window.Presenter.ApplyTemplate(); + panel.Children.Remove(rect); + Assert.Equal(0, logSink.Results.Count); + } + } + } + + class StubLogSink : ILogSink, IDisposable + { + LogEventLevel _level; + public StubLogSink(LogEventLevel level) + { + _level = level; + Logger.Sink = this; + } + public void Dispose() + { + Logger.Sink = null; + } + public List Results { get; set; } = new List(); + + public bool IsEnabled(LogEventLevel level, string area) + { + return true; + } + + public void Log(LogEventLevel level, string area, object source, string messageTemplate) + { + if (level >= _level) + { + Results.Add(Format(area, messageTemplate, source)); + } + } + + public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0) + { + if (level >= _level) + { + Results.Add(Format(area, messageTemplate, source, propertyValue0)); + } + } + + public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + if (level >= _level) + { + Results.Add(Format(area, messageTemplate, source, propertyValue0, propertyValue1)); + } + } + + public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + if (level >= _level) + { + Results.Add(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); + } + } + + public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues) + { + if (level >= _level) + { + Results.Add(Format(area, messageTemplate, source, propertyValues)); + } + } + #region Copy-Pasta + private static string Format( + string area, + string template, + object source, + T0 v0 = default, + T1 v1 = default, + T2 v2 = default) + { + var result = new StringBuilder(template.Length); + var r = new CharacterReader(template.AsSpan()); + var i = 0; + + result.Append('['); + result.Append(area); + result.Append("] "); + + while (!r.End) + { + var c = r.Take(); + + if (c != '{') + { + result.Append(c); + } + else + { + if (r.Peek != '{') + { + result.Append('\''); + result.Append(i++ switch + { + 0 => v0, + 1 => v1, + 2 => v2, + _ => null + }); + result.Append('\''); + r.TakeUntil('}'); + r.Take(); + } + else + { + result.Append('{'); + r.Take(); + } + } + } + + if (source is object) + { + result.Append(" ("); + result.Append(source.GetType().Name); + result.Append(" #"); + result.Append(source.GetHashCode()); + result.Append(')'); + } + + return result.ToString(); + } + + private static string Format( + string area, + string template, + object source, + object[] v) + { + var result = new StringBuilder(template.Length); + var r = new CharacterReader(template.AsSpan()); + var i = 0; + + result.Append('['); + result.Append(area); + result.Append(']'); + + while (!r.End) + { + var c = r.Take(); + + if (c != '{') + { + result.Append(c); + } + else + { + if (r.Peek != '{') + { + result.Append('\''); + result.Append(i < v.Length ? v[i++] : null); + result.Append('\''); + r.TakeUntil('}'); + r.Take(); + } + else + { + result.Append('{'); + r.Take(); + } + } + } + + if (source is object) + { + result.Append('('); + result.Append(source.GetType().Name); + result.Append(" #"); + result.Append(source.GetHashCode()); + result.Append(')'); + } + + return result.ToString(); + } + #endregion + } +} From 22432a002dc9ba86429d582c08103e17f3e50433 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Tue, 31 Aug 2021 13:14:59 +0300 Subject: [PATCH 03/18] test --- src/Avalonia.Styling/LogicalTree/ControlLocator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs index 6d0302ace4..e89bf55404 100644 --- a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs +++ b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs @@ -57,7 +57,7 @@ namespace Avalonia.LogicalTree private void Detached(object sender, LogicalTreeAttachmentEventArgs e) { _value = null; - PublishNext(null); + // PublishNext(null); } private void Update() From 68370e37a5d5340613369e49f8aa9f9e5b7f31d1 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 31 Aug 2021 16:14:15 +0200 Subject: [PATCH 04/18] fixes(DataGrid): Issue #6508 does not re-enter the edit mode after pressing ESC --- src/Avalonia.Controls.DataGrid/DataGrid.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index ab1aff9220..cb1ce0c731 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -4489,8 +4489,19 @@ namespace Avalonia.Controls element = dataGridColumn.GenerateEditingElementInternal(dataGridCell, dataGridRow.DataContext); if (element != null) { - // Subscribe to the new element's events - element.Initialized += EditingElement_Initialized; + + if (element.IsInitialized) + { + Threading.Dispatcher.UIThread.Post(() => + { + PreparingCellForEditPrivate(element as Control); + }); + } + else + { + // Subscribe to the new element's events + element.Initialized += EditingElement_Initialized; + } } } else From 063e5e7be3b94d4a3f929acbd2ac552534877e41 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Wed, 1 Sep 2021 11:42:16 +0300 Subject: [PATCH 05/18] test --- src/Avalonia.Styling/LogicalTree/ControlLocator.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs index e89bf55404..501a26c848 100644 --- a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs +++ b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs @@ -57,7 +57,10 @@ namespace Avalonia.LogicalTree private void Detached(object sender, LogicalTreeAttachmentEventArgs e) { _value = null; - // PublishNext(null); + if (_relativeTo.IsAttachedToLogicalTree) + { + PublishNext(null); + } } private void Update() From 01518b37e78b44dff6f7839672b6fe5575771a08 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Wed, 1 Sep 2021 12:30:23 +0300 Subject: [PATCH 06/18] test --- src/Avalonia.Styling/LogicalTree/ControlLocator.cs | 6 ++---- src/Avalonia.Visuals/Visual.cs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs index 501a26c848..3f5b1ee0b5 100644 --- a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs +++ b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs @@ -57,10 +57,8 @@ namespace Avalonia.LogicalTree private void Detached(object sender, LogicalTreeAttachmentEventArgs e) { _value = null; - if (_relativeTo.IsAttachedToLogicalTree) - { - PublishNext(null); - } + PublishNext(null); + } private void Update() diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 2c96d87bb6..163c3db5fd 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -493,7 +493,7 @@ namespace Avalonia // In theory this should only need to check for logical tree attachment, but in practise // due to ContentControlMixin only taking effect when the template has finished being // applied, some controls are attached to the visual tree before the logical tree. - if (((ILogical)this).IsAttachedToLogicalTree || ((IVisual)this).IsAttachedToVisualTree) + if (((ILogical)this).IsAttachedToLogicalTree) { if (e is BindingChainException b && string.IsNullOrEmpty(b.ExpressionErrorPoint) && From 5c1fd27d1f0f31cf2c5c5a60aa634c7d3fc58914 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Wed, 1 Sep 2021 15:00:08 +0300 Subject: [PATCH 07/18] wip --- src/Avalonia.Visuals/Visual.cs | 5 +- .../Logging/LoggingTests.cs | 191 +----------------- 2 files changed, 12 insertions(+), 184 deletions(-) diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 163c3db5fd..322b630e83 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -489,10 +489,7 @@ namespace Avalonia protected internal sealed override void LogBindingError(AvaloniaProperty property, Exception e) { - // Don't log a binding error unless the control is attached to a logical or visual tree. - // In theory this should only need to check for logical tree attachment, but in practise - // due to ContentControlMixin only taking effect when the template has finished being - // applied, some controls are attached to the visual tree before the logical tree. + // Don't log a binding error unless the control is attached to a logical tree. if (((ILogical)this).IsAttachedToLogicalTree) { if (e is BindingChainException b && diff --git a/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs b/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs index 193d5e3a45..9e1885463d 100644 --- a/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs +++ b/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs @@ -1,12 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Controls.Shapes; -using Avalonia.Logging; using Avalonia.Markup.Xaml; using Avalonia.UnitTests; -using Avalonia.Utilities; using Xunit; namespace Avalonia.Base.UnitTests.Logging @@ -28,187 +23,23 @@ namespace Avalonia.Base.UnitTests.Logging "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); - using var logSink = new StubLogSink(LogEventLevel.Warning); + var calledTimes = 0; + using var logSink = TestLogSink.Start((l, a, s, m, d) => + { + if (l >= Avalonia.Logging.LogEventLevel.Warning) + { + calledTimes++; + } + }); var panel = window.FindControl("panel"); var rect = window.FindControl("rect"); window.ApplyTemplate(); window.Presenter.ApplyTemplate(); panel.Children.Remove(rect); - Assert.Equal(0, logSink.Results.Count); - } - } - } - - class StubLogSink : ILogSink, IDisposable - { - LogEventLevel _level; - public StubLogSink(LogEventLevel level) - { - _level = level; - Logger.Sink = this; - } - public void Dispose() - { - Logger.Sink = null; - } - public List Results { get; set; } = new List(); - - public bool IsEnabled(LogEventLevel level, string area) - { - return true; - } - - public void Log(LogEventLevel level, string area, object source, string messageTemplate) - { - if (level >= _level) - { - Results.Add(Format(area, messageTemplate, source)); - } - } - - public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0) - { - if (level >= _level) - { - Results.Add(Format(area, messageTemplate, source, propertyValue0)); - } - } - - public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - if (level >= _level) - { - Results.Add(Format(area, messageTemplate, source, propertyValue0, propertyValue1)); - } - } - - public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - if (level >= _level) - { - Results.Add(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); - } - } - - public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues) - { - if (level >= _level) - { - Results.Add(Format(area, messageTemplate, source, propertyValues)); - } - } - #region Copy-Pasta - private static string Format( - string area, - string template, - object source, - T0 v0 = default, - T1 v1 = default, - T2 v2 = default) - { - var result = new StringBuilder(template.Length); - var r = new CharacterReader(template.AsSpan()); - var i = 0; - - result.Append('['); - result.Append(area); - result.Append("] "); - - while (!r.End) - { - var c = r.Take(); - - if (c != '{') - { - result.Append(c); - } - else - { - if (r.Peek != '{') - { - result.Append('\''); - result.Append(i++ switch - { - 0 => v0, - 1 => v1, - 2 => v2, - _ => null - }); - result.Append('\''); - r.TakeUntil('}'); - r.Take(); - } - else - { - result.Append('{'); - r.Take(); - } - } + Assert.Equal(0, calledTimes); } - - if (source is object) - { - result.Append(" ("); - result.Append(source.GetType().Name); - result.Append(" #"); - result.Append(source.GetHashCode()); - result.Append(')'); - } - - return result.ToString(); } + } - private static string Format( - string area, - string template, - object source, - object[] v) - { - var result = new StringBuilder(template.Length); - var r = new CharacterReader(template.AsSpan()); - var i = 0; - - result.Append('['); - result.Append(area); - result.Append(']'); - - while (!r.End) - { - var c = r.Take(); - - if (c != '{') - { - result.Append(c); - } - else - { - if (r.Peek != '{') - { - result.Append('\''); - result.Append(i < v.Length ? v[i++] : null); - result.Append('\''); - r.TakeUntil('}'); - r.Take(); - } - else - { - result.Append('{'); - r.Take(); - } - } - } - - if (source is object) - { - result.Append('('); - result.Append(source.GetType().Name); - result.Append(" #"); - result.Append(source.GetHashCode()); - result.Append(')'); - } - return result.ToString(); - } - #endregion - } } From 77f4a6e808a37664c5a4aa23e171129f02dd9971 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Wed, 1 Sep 2021 15:17:16 +0300 Subject: [PATCH 08/18] more tests --- .../Logging/LoggingTests.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs b/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs index 9e1885463d..d160713023 100644 --- a/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs +++ b/tests/Avalonia.Base.UnitTests/Logging/LoggingTests.cs @@ -39,6 +39,34 @@ namespace Avalonia.Base.UnitTests.Logging Assert.Equal(0, calledTimes); } } + + [Fact] + public void Control_Should_Log_Binding_Errors_When_No_Ancestor_With_Such_Name() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + +"; + var calledTimes = 0; + using var logSink = TestLogSink.Start((l, a, s, m, d) => + { + if (l >= Avalonia.Logging.LogEventLevel.Warning && s is Rectangle) + { + calledTimes++; + } + }); + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + window.ApplyTemplate(); + window.Presenter.ApplyTemplate(); + Assert.Equal(1, calledTimes); + } + } } From df5a9c7afc1726beb5a1338c7175ee56243afea2 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Wed, 1 Sep 2021 15:18:17 +0300 Subject: [PATCH 09/18] wip --- src/Avalonia.Styling/LogicalTree/ControlLocator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs index 3f5b1ee0b5..6d0302ace4 100644 --- a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs +++ b/src/Avalonia.Styling/LogicalTree/ControlLocator.cs @@ -58,7 +58,6 @@ namespace Avalonia.LogicalTree { _value = null; PublishNext(null); - } private void Update() From 907b55cb7e6c3ea93e78a1ed097b3b0441d196ab Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 9 Sep 2021 16:02:58 +0100 Subject: [PATCH 10/18] CompiledBinding correctly locates IDataContextProvider as anchor. (implementation was in ReflectionBinding but missing from CompiledBinding) --- .../MarkupExtensions/CompiledBindingExtension.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs index 17d2ea7ae9..5c4d9315d5 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs @@ -44,6 +44,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions // the context. object anchor = provider.GetFirstParent(); + if (anchor is null) + { + // Try to find IDataContextProvider, this was added to allow us to find + // a datacontext for Application class when using NativeMenuItems. + anchor = provider.GetFirstParent(); + } + // If a control was not found, then try to find the highest-level style as the XAML // file could be a XAML file containing only styles. return anchor ?? From aa5ae2360748056304b643d806e46fc061c607c2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 9 Sep 2021 18:17:32 +0100 Subject: [PATCH 11/18] move duplicated code to an extension method. --- .../Avalonia.Markup.Xaml.csproj | 1 + .../CompiledBindingExtension.cs | 25 +--------------- .../IServiceProviderExtensions.cs | 30 +++++++++++++++++++ .../ReflectionBindingExtension.cs | 23 +------------- 4 files changed, 33 insertions(+), 46 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/IServiceProviderExtensions.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 217da2d50d..8f118c7b2f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -26,6 +26,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs index 5c4d9315d5..41de2355aa 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs @@ -1,7 +1,5 @@ using System; using Avalonia.Data; -using Avalonia.Controls; -using Avalonia.Styling; using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings; using Avalonia.Data.Core; using Avalonia.Markup.Parsers; @@ -33,31 +31,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions Priority = Priority, StringFormat = StringFormat, Source = Source, - DefaultAnchor = new WeakReference(GetDefaultAnchor(provider)) + DefaultAnchor = new WeakReference(provider.GetDefaultAnchor()) }; } - private static object GetDefaultAnchor(IServiceProvider provider) - { - // If the target is not a control, so we need to find an anchor that will let us look - // up named controls and style resources. First look for the closest IControl in - // the context. - object anchor = provider.GetFirstParent(); - - if (anchor is null) - { - // Try to find IDataContextProvider, this was added to allow us to find - // a datacontext for Application class when using NativeMenuItems. - anchor = provider.GetFirstParent(); - } - - // If a control was not found, then try to find the highest-level style as the XAML - // file could be a XAML file containing only styles. - return anchor ?? - provider.GetService()?.RootObject as IStyle ?? - provider.GetLastParent(); - } - protected override ExpressionObserver CreateExpressionObserver(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor, bool enableDataValidation) { if (Source != null) diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/IServiceProviderExtensions.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/IServiceProviderExtensions.cs new file mode 100644 index 0000000000..a2bc19bbce --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/IServiceProviderExtensions.cs @@ -0,0 +1,30 @@ +using System; +using Avalonia.Controls; +using Avalonia.Styling; + +namespace Avalonia.Markup.Xaml.MarkupExtensions +{ + internal static class IServiceProviderExtensions + { + public static object GetDefaultAnchor(this IServiceProvider provider) + { + // If the target is not a control, so we need to find an anchor that will let us look + // up named controls and style resources. First look for the closest IControl in + // the context. + object anchor = provider.GetFirstParent(); + + if (anchor is null) + { + // Try to find IDataContextProvider, this was added to allow us to find + // a datacontext for Application class when using NativeMenuItems. + anchor = provider.GetFirstParent(); + } + + // If a control was not found, then try to find the highest-level style as the XAML + // file could be a XAML file containing only styles. + return anchor ?? + provider.GetService()?.RootObject as IStyle ?? + provider.GetLastParent(); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs index 10770365a3..d373ed852a 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs @@ -37,33 +37,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions Source = Source, StringFormat = StringFormat, RelativeSource = RelativeSource, - DefaultAnchor = new WeakReference(GetDefaultAnchor(descriptorContext)), + DefaultAnchor = new WeakReference(descriptorContext.GetDefaultAnchor()), TargetNullValue = TargetNullValue, NameScope = new WeakReference(serviceProvider.GetService()) }; } - private static object GetDefaultAnchor(IServiceProvider context) - { - // If the target is not a control, so we need to find an anchor that will let us look - // up named controls and style resources. First look for the closest IControl in - // the context. - object anchor = context.GetFirstParent(); - - if(anchor is null) - { - // Try to find IDataContextProvider, this was added to allow us to find - // a datacontext for Application class when using NativeMenuItems. - anchor = context.GetFirstParent(); - } - - // If a control was not found, then try to find the highest-level style as the XAML - // file could be a XAML file containing only styles. - return anchor ?? - context.GetService()?.RootObject as IStyle ?? - context.GetLastParent(); - } - public IValueConverter Converter { get; set; } public object ConverterParameter { get; set; } From 42763792f44ec9317b621bf3633deeacf770dfb9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 9 Sep 2021 18:37:44 +0100 Subject: [PATCH 12/18] use existing extensions class. --- src/Markup/Avalonia.Markup.Xaml/Extensions.cs | 23 ++++++++++++++ .../IServiceProviderExtensions.cs | 30 ------------------- 2 files changed, 23 insertions(+), 30 deletions(-) delete mode 100644 src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/IServiceProviderExtensions.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/Extensions.cs b/src/Markup/Avalonia.Markup.Xaml/Extensions.cs index fe3fd44c1c..263750c316 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Extensions.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Extensions.cs @@ -2,7 +2,9 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using Avalonia.Controls; using Avalonia.Markup.Xaml.XamlIl.Runtime; +using Avalonia.Styling; namespace Avalonia.Markup.Xaml { @@ -32,5 +34,26 @@ namespace Avalonia.Markup.Xaml string name = string.IsNullOrEmpty(namespacePrefix) ? type : $"{namespacePrefix}:{type}"; return tr?.Resolve(name); } + + public static object GetDefaultAnchor(this IServiceProvider provider) + { + // If the target is not a control, so we need to find an anchor that will let us look + // up named controls and style resources. First look for the closest IControl in + // the context. + object anchor = provider.GetFirstParent(); + + if (anchor is null) + { + // Try to find IDataContextProvider, this was added to allow us to find + // a datacontext for Application class when using NativeMenuItems. + anchor = provider.GetFirstParent(); + } + + // If a control was not found, then try to find the highest-level style as the XAML + // file could be a XAML file containing only styles. + return anchor ?? + provider.GetService()?.RootObject as IStyle ?? + provider.GetLastParent(); + } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/IServiceProviderExtensions.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/IServiceProviderExtensions.cs deleted file mode 100644 index a2bc19bbce..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/IServiceProviderExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using Avalonia.Controls; -using Avalonia.Styling; - -namespace Avalonia.Markup.Xaml.MarkupExtensions -{ - internal static class IServiceProviderExtensions - { - public static object GetDefaultAnchor(this IServiceProvider provider) - { - // If the target is not a control, so we need to find an anchor that will let us look - // up named controls and style resources. First look for the closest IControl in - // the context. - object anchor = provider.GetFirstParent(); - - if (anchor is null) - { - // Try to find IDataContextProvider, this was added to allow us to find - // a datacontext for Application class when using NativeMenuItems. - anchor = provider.GetFirstParent(); - } - - // If a control was not found, then try to find the highest-level style as the XAML - // file could be a XAML file containing only styles. - return anchor ?? - provider.GetService()?.RootObject as IStyle ?? - provider.GetLastParent(); - } - } -} From 5b28a2d94cf52e573f63a0164237010d50058113 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 9 Sep 2021 19:42:14 +0100 Subject: [PATCH 13/18] fix build. --- src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 8f118c7b2f..217da2d50d 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -26,7 +26,6 @@ - From c919109be60d96900961cb1917314903eada4bfb Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 10 Sep 2021 11:41:07 +0200 Subject: [PATCH 14/18] fixes(DataGrid): Issue #6567 New line in Editable DataGrid, Not editable --- .../DataGridDataConnection.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs index a94acdec57..fade597ca1 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs @@ -233,7 +233,7 @@ namespace Avalonia.Controls else { editableCollectionView.EditItem(dataItem); - return editableCollectionView.IsEditingItem; + return editableCollectionView.IsEditingItem || editableCollectionView.IsAddingNew; } } @@ -314,7 +314,14 @@ namespace Avalonia.Controls CommittingEdit = true; try { - editableCollectionView.CommitEdit(); + if (editableCollectionView.IsAddingNew) + { + editableCollectionView.CommitNew(); + } + else + { + editableCollectionView.CommitEdit(); + } } finally { From 7a8650ae06867b16c716740b117bf61aef87653a Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 10 Sep 2021 18:05:37 +0200 Subject: [PATCH 15/18] fixes(Datagrid): Avoid using Threading.Dispatcher.UIThread.Post( --- src/Avalonia.Controls.DataGrid/DataGrid.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index cb1ce0c731..9d231ff8b2 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -4489,13 +4489,11 @@ namespace Avalonia.Controls element = dataGridColumn.GenerateEditingElementInternal(dataGridCell, dataGridRow.DataContext); if (element != null) { - + + dataGridCell.Content = element; if (element.IsInitialized) { - Threading.Dispatcher.UIThread.Post(() => - { - PreparingCellForEditPrivate(element as Control); - }); + PreparingCellForEditPrivate(element as Control); } else { @@ -4508,9 +4506,10 @@ namespace Avalonia.Controls { // Generate Element and apply column style if available element = dataGridColumn.GenerateElementInternal(dataGridCell, dataGridRow.DataContext); + dataGridCell.Content = element; } - dataGridCell.Content = element; + } private void PreparingCellForEditPrivate(Control editingElement) From 4eff3852ee640953fd48dcd770b2a2cc17434336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 10 Sep 2021 20:43:47 +0200 Subject: [PATCH 16/18] Set MenuItem icon presenter size from style --- src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml index 3903096933..72c25cea37 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml @@ -75,8 +75,6 @@ @@ -199,6 +197,8 @@