Added a small helper property to deal with controls that fail to update the measurements in response to complex data binding situations
This commit is contained in:
		
							parent
							
								
									1da1a82b09
								
							
						
					
					
						commit
						12fb020a27
					
				
					 1 changed files with 121 additions and 0 deletions
				
			
		
							
								
								
									
										121
									
								
								Source/Properties.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								Source/Properties.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
 | 
			
		||||
  /// <summary>Additional properties that can be attached to Avalonia objects</summary>
 | 
			
		||||
  public static class Properties {
 | 
			
		||||
 | 
			
		||||
    #region class InvalidateMeasureOnChangeObserver
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///   Invalidates the calculated measurement of the control that is reporting
 | 
			
		||||
    ///   a change to the property value
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private class InvalidateMeasureOnChangeObserver :
 | 
			
		||||
      IObserver<AvaloniaPropertyChangedEventArgs<object>> {
 | 
			
		||||
 | 
			
		||||
      /// <summary>The one and only instance you need</summary>
 | 
			
		||||
      public static readonly InvalidateMeasureOnChangeObserver Instance = new();
 | 
			
		||||
 | 
			
		||||
      /// <summary>Called after all observers have been notified successfully</summary>
 | 
			
		||||
      public void OnCompleted() {}
 | 
			
		||||
 | 
			
		||||
      /// <summary>Reports when the observed control encountered an error</summary>
 | 
			
		||||
      /// <param name="error">Error the observed control has encountered</param>
 | 
			
		||||
      public void OnError(Exception error) {}
 | 
			
		||||
 | 
			
		||||
      /// <summary>Reports the updated value of the property to the observer</summary>
 | 
			
		||||
      /// <param name="value">New value the property has assumed</param>
 | 
			
		||||
      public void OnNext(AvaloniaPropertyChangedEventArgs<object> value) {
 | 
			
		||||
        if(value.Sender is Control senderAsControl) {
 | 
			
		||||
          senderAsControl.InvalidateMeasure();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion // class InvalidateMeasureOnChangeObserver
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///   Invalidates the object's measured size of a control and triggers a new layout pass
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <remarks>
 | 
			
		||||
    ///   <para>
 | 
			
		||||
    ///     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.
 | 
			
		||||
    ///   </para>
 | 
			
		||||
    ///   <para>
 | 
			
		||||
    ///     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:
 | 
			
		||||
    ///   </para>
 | 
			
		||||
    ///   <example>
 | 
			
		||||
    ///     <code>
 | 
			
		||||
    ///       <Border
 | 
			
		||||
    ///         CornerRadius="4"
 | 
			
		||||
    ///         Padding="4,0"
 | 
			
		||||
    ///         HorizontalAlignment="Center"
 | 
			
		||||
    ///         nuclex:Properties.InvalidateMeasureOnChange="{Binding Status}"
 | 
			
		||||
    ///       >
 | 
			
		||||
    ///         <TextBlock
 | 
			
		||||
    ///           HorizontalAlignment="Center"
 | 
			
		||||
    ///           Text="{Binding Status}"
 | 
			
		||||
    ///         />
 | 
			
		||||
    ///       </Border>
 | 
			
		||||
    ///     </code>
 | 
			
		||||
    ///   </example>
 | 
			
		||||
    /// </remarks>
 | 
			
		||||
    public static readonly AttachedProperty<object> InvalidateMeasureOnChangeProperty = (
 | 
			
		||||
      AvaloniaProperty.RegisterAttached<Control, object>(
 | 
			
		||||
        "InvalidateMeasureOnChange",
 | 
			
		||||
        typeof(Properties),
 | 
			
		||||
        defaultValue: null!,
 | 
			
		||||
        inherits: false
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    /// <summary>Initializes all static members of the class</summary>
 | 
			
		||||
    static Properties() {
 | 
			
		||||
      InvalidateMeasureOnChangeProperty.Changed.Subscribe(InvalidateMeasureOnChangeObserver.Instance);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Reads the value of the 'InvalidateMeasureOnChange' property</summary>
 | 
			
		||||
    /// <param name="control">Control from which the property will be read</param>
 | 
			
		||||
    /// <returns>The current value of the property attached to the control</returns>
 | 
			
		||||
    public static object GetInvalidateMeasureOnChange(Control control) {
 | 
			
		||||
      return control.GetValue(InvalidateMeasureOnChangeProperty);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Updates the value of the 'InvalidateMeasureOnChange' property</summary>
 | 
			
		||||
    /// <param name="control">Control on which the property will be updated</param>
 | 
			
		||||
    /// <param name="value">New value to assign to the attached property</param>
 | 
			
		||||
    public static void SetInvalidateMeasureOnChange(Control control, object value) {
 | 
			
		||||
      control.SetValue(InvalidateMeasureOnChangeProperty, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
} // namespace Nuclex.Avalonia
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue