diff --git a/.gitignore b/.gitignore
index 44fe5e4ba4..84faae1806 100644
--- a/.gitignore
+++ b/.gitignore
@@ -212,3 +212,6 @@ coc-settings.json
*.map
src/Web/Avalonia.Web.Blazor/wwwroot/*.js
src/Web/Avalonia.Web.Blazor/Interop/Typescript/*.js
+node_modules
+src/Web/Avalonia.Web.Blazor/webapp/package-lock.json
+src/Web/Avalonia.Web.Blazor/wwwroot
diff --git a/samples/ControlCatalog/Converter/HexConverter.cs b/samples/ControlCatalog/Converter/HexConverter.cs
index 83a52212f6..31cce5ba67 100644
--- a/samples/ControlCatalog/Converter/HexConverter.cs
+++ b/samples/ControlCatalog/Converter/HexConverter.cs
@@ -7,7 +7,7 @@ namespace ControlCatalog.Converter;
public class HexConverter : IValueConverter
{
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var str = value?.ToString();
if (str == null)
@@ -18,7 +18,7 @@ public class HexConverter : IValueConverter
}
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
try
{
diff --git a/src/Avalonia.Base/Layout/LayoutHelper.cs b/src/Avalonia.Base/Layout/LayoutHelper.cs
index 404d19906a..0851dbaea9 100644
--- a/src/Avalonia.Base/Layout/LayoutHelper.cs
+++ b/src/Avalonia.Base/Layout/LayoutHelper.cs
@@ -251,6 +251,17 @@ namespace Avalonia.Layout
{
double newValue;
+ // Round the value to avoid FP errors. This is needed because if `value` has a floating
+ // point precision error (e.g. 79.333333333333343) then when it's multiplied by
+ // `dpiScale` and rounded up, it will be rounded up to a value one greater than it
+ // should be.
+#if NET6_0_OR_GREATER
+ value = Math.Round(value, 8, MidpointRounding.ToZero);
+#else
+ // MidpointRounding.ToZero isn't available in netstandard2.0.
+ value = Math.Truncate(value * 1e8) / 1e8;
+#endif
+
// If DPI == 1, don't use DPI-aware rounding.
if (!MathUtilities.IsOne(dpiScale))
{
diff --git a/src/Avalonia.Base/Media/DashStyle.cs b/src/Avalonia.Base/Media/DashStyle.cs
index abee580020..3a30b2d32f 100644
--- a/src/Avalonia.Base/Media/DashStyle.cs
+++ b/src/Avalonia.Base/Media/DashStyle.cs
@@ -35,7 +35,6 @@ namespace Avalonia.Media
/// Initializes a new instance of the class.
///
public DashStyle()
- : this(null, 0)
{
}
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
index 9742a6b3ba..e47c8e7e31 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
@@ -87,7 +87,16 @@ public class CompositingRenderer : IRendererWithCompositor
///
public IEnumerable HitTest(Point p, IVisual root, Func? filter)
{
- var res = CompositionTarget.TryHitTest(p, filter);
+ Func? f = null;
+ if (filter != null)
+ f = v =>
+ {
+ if (v is CompositionDrawListVisual dlv)
+ return filter(dlv.Visual);
+ return true;
+ };
+
+ var res = CompositionTarget.TryHitTest(p, f);
if(res == null)
yield break;
foreach(var v in res)
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs b/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs
index 77b392eee5..b019d1792b 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs
@@ -54,13 +54,11 @@ internal class CompositionDrawListVisual : CompositionContainerVisual
Visual = visual;
}
- internal override bool HitTest(Point pt, Func? filter)
+ internal override bool HitTest(Point pt)
{
var custom = Visual as ICustomHitTest;
if (DrawList == null && custom == null)
return false;
- if (filter != null && !filter(Visual))
- return false;
if (custom != null)
{
// Simulate the old behavior
diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
index 4e53e163ec..eb499604e0 100644
--- a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
+++ b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs
@@ -31,7 +31,7 @@ namespace Avalonia.Rendering.Composition
///
///
///
- public PooledList? TryHitTest(Point point, Func? filter)
+ public PooledList? TryHitTest(Point point, Func? filter)
{
Server.Readback.NextRead();
if (Root == null)
@@ -88,10 +88,14 @@ namespace Avalonia.Rendering.Composition
}
void HitTestCore(CompositionVisual visual, Point globalPoint, PooledList result,
- Func? filter)
+ Func? filter)
{
if (visual.Visible == false)
return;
+
+ if (filter != null && !filter(visual))
+ return;
+
if (!TryTransformTo(visual, globalPoint, out var point))
return;
@@ -111,7 +115,7 @@ namespace Avalonia.Rendering.Composition
}
// Hit-test the current node
- if (visual.HitTest(point, filter))
+ if (visual.HitTest(point))
result.Add(visual);
}
diff --git a/src/Avalonia.Base/Rendering/Composition/Visual.cs b/src/Avalonia.Base/Rendering/Composition/Visual.cs
index 7356b7b9e8..6d6818256a 100644
--- a/src/Avalonia.Base/Rendering/Composition/Visual.cs
+++ b/src/Avalonia.Base/Rendering/Composition/Visual.cs
@@ -53,6 +53,6 @@ namespace Avalonia.Rendering.Composition
internal object? Tag { get; set; }
- internal virtual bool HitTest(Point point, Func? filter) => true;
+ internal virtual bool HitTest(Point point) => true;
}
}
diff --git a/src/Avalonia.Controls.ColorPicker/Converters/DoNothingForNullConverter.cs b/src/Avalonia.Controls.ColorPicker/Converters/DoNothingForNullConverter.cs
new file mode 100644
index 0000000000..421a5acb28
--- /dev/null
+++ b/src/Avalonia.Controls.ColorPicker/Converters/DoNothingForNullConverter.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Globalization;
+using Avalonia.Data;
+using Avalonia.Data.Converters;
+
+namespace Avalonia.Controls.Converters
+{
+ ///
+ /// Converter that will do nothing (not update bound values) when a null value is encountered.
+ /// This converter enables binding nullable with non-nullable properties in some scenarios.
+ ///
+ public class DoNothingForNullConverter : IValueConverter
+ {
+ ///
+ public object? Convert(
+ object? value,
+ Type targetType,
+ object? parameter,
+ CultureInfo culture)
+ {
+ return value ?? BindingOperations.DoNothing;
+ }
+
+ ///
+ public object? ConvertBack(
+ object? value,
+ Type targetType,
+ object? parameter,
+ CultureInfo culture)
+ {
+ return value ?? BindingOperations.DoNothing;
+ }
+ }
+}
+
diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
index 59cc48975f..dab4142001 100644
--- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
+++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
@@ -11,6 +11,7 @@
+
48
30
@@ -205,7 +206,7 @@
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
index d63c738133..69e6766bfd 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
@@ -23,10 +23,10 @@ namespace Avalonia.Controls
[PseudoClasses(":pressed", ":current", ":expanded")]
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 const string DATAGRIDROWGROUPHEADER_expanderButton = "PART_ExpanderButton";
+ private const string DATAGRIDROWGROUPHEADER_indentSpacer = "PART_IndentSpacer";
+ private const string DATAGRIDROWGROUPHEADER_itemCountElement = "PART_ItemCountElement";
+ private const string DATAGRIDROWGROUPHEADER_propertyNameElement = "PART_PropertyNameElement";
private bool _areIsCheckedHandlersSuspended;
private ToggleButton _expanderButton;
diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
index 364a2a52e3..b4bae02b9b 100644
--- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
+++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
@@ -409,9 +409,9 @@
ColumnDefinitions="Auto,Auto,Auto,Auto,*"
RowDefinitions="*,Auto">
-
-
-
-
diff --git a/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml b/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml
index 6a748f399e..b43addff86 100644
--- a/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml
+++ b/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml
@@ -258,10 +258,10 @@
ColumnDefinitions="Auto,Auto,Auto,Auto"
RowDefinitions="Auto,*,Auto">
-
-
-
-
diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs
index bb838a4f3f..0de068a416 100644
--- a/src/Avalonia.Controls/Calendar/Calendar.cs
+++ b/src/Avalonia.Controls/Calendar/Calendar.cs
@@ -2109,8 +2109,8 @@ namespace Avalonia.Controls
RemovedItems = new Collection();
}
- private const string PART_ElementRoot = "Root";
- private const string PART_ElementMonth = "CalendarItem";
+ private const string PART_ElementRoot = "PART_Root";
+ private const string PART_ElementMonth = "PART_CalendarItem";
///
/// Builds the visual tree for the
diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs
index 75e9f52621..e2eabb5f28 100644
--- a/src/Avalonia.Controls/Calendar/CalendarItem.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs
@@ -32,11 +32,11 @@ namespace Avalonia.Controls.Primitives
///
private const int NumberOfDaysPerWeek = 7;
- private const string PART_ElementHeaderButton = "HeaderButton";
- private const string PART_ElementPreviousButton = "PreviousButton";
- private const string PART_ElementNextButton = "NextButton";
- private const string PART_ElementMonthView = "MonthView";
- private const string PART_ElementYearView = "YearView";
+ private const string PART_ElementHeaderButton = "PART_HeaderButton";
+ private const string PART_ElementPreviousButton = "PART_PreviousButton";
+ private const string PART_ElementNextButton = "PART_NextButton";
+ private const string PART_ElementMonthView = "PART_MonthView";
+ private const string PART_ElementYearView = "PART_YearView";
private Button? _headerButton;
private Button? _nextButton;
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
index f2b808fe0d..fb27feb521 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
@@ -14,15 +14,15 @@ namespace Avalonia.Controls
///
/// A control to allow the user to select a date
///
- [TemplatePart("ButtonContentGrid", typeof(Grid))]
- [TemplatePart("DayText", typeof(TextBlock))]
- [TemplatePart("FirstSpacer", typeof(Rectangle))]
- [TemplatePart("FlyoutButton", typeof(Button))]
- [TemplatePart("MonthText", typeof(TextBlock))]
- [TemplatePart("PickerPresenter", typeof(DatePickerPresenter))]
- [TemplatePart("Popup", typeof(Popup))]
- [TemplatePart("SecondSpacer", typeof(Rectangle))]
- [TemplatePart("YearText", typeof(TextBlock))]
+ [TemplatePart("PART_ButtonContentGrid", typeof(Grid))]
+ [TemplatePart("PART_DayTextBlock", typeof(TextBlock))]
+ [TemplatePart("PART_FirstSpacer", typeof(Rectangle))]
+ [TemplatePart("PART_FlyoutButton", typeof(Button))]
+ [TemplatePart("PART_MonthTextBlock", typeof(TextBlock))]
+ [TemplatePart("PART_PickerPresenter", typeof(DatePickerPresenter))]
+ [TemplatePart("PART_Popup", typeof(Popup))]
+ [TemplatePart("PART_SecondSpacer", typeof(Rectangle))]
+ [TemplatePart("PART_YearTextBlock", typeof(TextBlock))]
[PseudoClasses(":hasnodate")]
public class DatePicker : TemplatedControl
{
@@ -280,15 +280,15 @@ namespace Avalonia.Controls
}
base.OnApplyTemplate(e);
- _flyoutButton = e.NameScope.Find
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
@@ -214,50 +214,50 @@
CornerRadius="{TemplateBinding CornerRadius}">
-
+
-
-
-
-
-
-
-
-
-
-
-
-
@@ -272,12 +272,12 @@
Color="{DynamicResource ThemeAccentColor}" />
-
-
-
-
-