From 12fb020a277583d84cff3e366ac4e10e89a65610 Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Tue, 29 Jul 2025 13:08:58 +0200 Subject: [PATCH] Added a small helper property to deal with controls that fail to update the measurements in response to complex data binding situations --- Source/Properties.cs | 121 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 Source/Properties.cs diff --git a/Source/Properties.cs b/Source/Properties.cs new file mode 100644 index 0000000..eefd40c --- /dev/null +++ b/Source/Properties.cs @@ -0,0 +1,121 @@ +#region Apache License 2.0 +/* +Nuclex Foundation libraries for .NET +Copyright (C) 2002-2025 Markus Ewald / Nuclex Development Labs + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#endregion // Apache License 2.0 + +using System; + +using Avalonia; +using Avalonia.Controls; + +namespace Nuclex.Avalonia { + + /// Additional properties that can be attached to Avalonia objects + public static class Properties { + + #region class InvalidateMeasureOnChangeObserver + + /// + /// Invalidates the calculated measurement of the control that is reporting + /// a change to the property value + /// + private class InvalidateMeasureOnChangeObserver : + IObserver> { + + /// The one and only instance you need + public static readonly InvalidateMeasureOnChangeObserver Instance = new(); + + /// Called after all observers have been notified successfully + public void OnCompleted() {} + + /// Reports when the observed control encountered an error + /// Error the observed control has encountered + public void OnError(Exception error) {} + + /// Reports the updated value of the property to the observer + /// New value the property has assumed + public void OnNext(AvaloniaPropertyChangedEventArgs value) { + if(value.Sender is Control senderAsControl) { + senderAsControl.InvalidateMeasure(); + } + } + + } + + #endregion // class InvalidateMeasureOnChangeObserver + + /// + /// Invalidates the object's measured size of a control and triggers a new layout pass + /// + /// + /// + /// If, for some reason, an Avalonia control whose size depends on a data-bound value + /// (for example, a <Border /> that holds a <TextBlock /gt; that uses data + /// binding) does not update its own size, that is an Avalonia bug. + /// + /// + /// By attaching this property to the misbehaving control and data-binding its value + /// to the trigger that should cause a reevaluation of its dimensions, you can work + /// around the issue: + /// + /// + /// + /// <Border + /// CornerRadius="4" + /// Padding="4,0" + /// HorizontalAlignment="Center" + /// nuclex:Properties.InvalidateMeasureOnChange="{Binding Status}" + /// > + /// <TextBlock + /// HorizontalAlignment="Center" + /// Text="{Binding Status}" + /// /> + /// </Border> + /// + /// + /// + public static readonly AttachedProperty InvalidateMeasureOnChangeProperty = ( + AvaloniaProperty.RegisterAttached( + "InvalidateMeasureOnChange", + typeof(Properties), + defaultValue: null!, + inherits: false + ) + ); + + /// Initializes all static members of the class + static Properties() { + InvalidateMeasureOnChangeProperty.Changed.Subscribe(InvalidateMeasureOnChangeObserver.Instance); + } + + /// Reads the value of the 'InvalidateMeasureOnChange' property + /// Control from which the property will be read + /// The current value of the property attached to the control + public static object GetInvalidateMeasureOnChange(Control control) { + return control.GetValue(InvalidateMeasureOnChangeProperty); + } + + /// Updates the value of the 'InvalidateMeasureOnChange' property + /// Control on which the property will be updated + /// New value to assign to the attached property + public static void SetInvalidateMeasureOnChange(Control control, object value) { + control.SetValue(InvalidateMeasureOnChangeProperty, value); + } + + } + +} // namespace Nuclex.Avalonia