|
|
@@ -29,53 +29,23 @@ namespace Avalonia.Controls
|
|
|
/// <remarks>
|
|
|
/// The control class extends <see cref="InputElement"/> and adds the following features:
|
|
|
///
|
|
|
- /// - An inherited <see cref="DataContext"/>.
|
|
|
/// - A <see cref="Tag"/> property to allow user-defined data to be attached to the control.
|
|
|
- /// - A collection of class strings for custom styling.
|
|
|
- /// - Implements <see cref="IStyleable"/> to allow styling to work on the control.
|
|
|
- /// - Implements <see cref="ILogical"/> to form part of a logical tree.
|
|
|
+
|
|
|
/// </remarks>
|
|
|
- public class Control : InputElement, IControl, INamed, ISetInheritanceParent, ISetLogicalParent, ISupportInitialize, IVisualBrushInitialize
|
|
|
+ public class Control : InputElement, IControl, INamed, ISupportInitialize, IVisualBrushInitialize, IRequiresTemplateInStyle
|
|
|
{
|
|
|
- /// <summary>
|
|
|
- /// Defines the <see cref="DataContext"/> property.
|
|
|
- /// </summary>
|
|
|
- public static readonly StyledProperty<object> DataContextProperty =
|
|
|
- AvaloniaProperty.Register<Control, object>(
|
|
|
- nameof(DataContext),
|
|
|
- inherits: true,
|
|
|
- notifying: DataContextNotifying);
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Defines the <see cref="FocusAdorner"/> property.
|
|
|
/// </summary>
|
|
|
public static readonly StyledProperty<ITemplate<IControl>> FocusAdornerProperty =
|
|
|
AvaloniaProperty.Register<Control, ITemplate<IControl>>(nameof(FocusAdorner));
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Defines the <see cref="Name"/> property.
|
|
|
- /// </summary>
|
|
|
- public static readonly DirectProperty<Control, string> NameProperty =
|
|
|
- AvaloniaProperty.RegisterDirect<Control, string>(nameof(Name), o => o.Name, (o, v) => o.Name = v);
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Defines the <see cref="Parent"/> property.
|
|
|
- /// </summary>
|
|
|
- public static readonly DirectProperty<Control, IControl> ParentProperty =
|
|
|
- AvaloniaProperty.RegisterDirect<Control, IControl>(nameof(Parent), o => o.Parent);
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Defines the <see cref="Tag"/> property.
|
|
|
/// </summary>
|
|
|
public static readonly StyledProperty<object> TagProperty =
|
|
|
AvaloniaProperty.Register<Control, object>(nameof(Tag));
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Defines the <see cref="TemplatedParent"/> property.
|
|
|
- /// </summary>
|
|
|
- public static readonly StyledProperty<ITemplatedControl> TemplatedParentProperty =
|
|
|
- AvaloniaProperty.Register<Control, ITemplatedControl>(nameof(TemplatedParent), inherits: true);
|
|
|
-
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Defines the <see cref="ContextMenu"/> property.
|
|
|
/// </summary>
|
|
|
@@ -88,20 +58,8 @@ namespace Avalonia.Controls
|
|
|
public static readonly RoutedEvent<RequestBringIntoViewEventArgs> RequestBringIntoViewEvent =
|
|
|
RoutedEvent.Register<Control, RequestBringIntoViewEventArgs>("RequestBringIntoView", RoutingStrategies.Bubble);
|
|
|
|
|
|
- private int _initCount;
|
|
|
- private string _name;
|
|
|
- private IControl _parent;
|
|
|
- private readonly Classes _classes = new Classes();
|
|
|
private DataTemplates _dataTemplates;
|
|
|
private IControl _focusAdorner;
|
|
|
- private bool _isAttachedToLogicalTree;
|
|
|
- private IAvaloniaList<ILogical> _logicalChildren;
|
|
|
- private INameScope _nameScope;
|
|
|
- private IResourceDictionary _resources;
|
|
|
- private Styles _styles;
|
|
|
- private bool _styled;
|
|
|
- private Subject<IStyleable> _styleDetach = new Subject<IStyleable>();
|
|
|
- private bool _dataContextUpdating;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Initializes static members of the <see cref="Control"/> class.
|
|
|
@@ -112,127 +70,6 @@ namespace Avalonia.Controls
|
|
|
PseudoClass(IsEnabledCoreProperty, x => !x, ":disabled");
|
|
|
PseudoClass(IsFocusedProperty, ":focus");
|
|
|
PseudoClass(IsPointerOverProperty, ":pointerover");
|
|
|
- DataContextProperty.Changed.AddClassHandler<Control>(x => x.OnDataContextChangedCore);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Initializes a new instance of the <see cref="Control"/> class.
|
|
|
- /// </summary>
|
|
|
- public Control()
|
|
|
- {
|
|
|
- _nameScope = this as INameScope;
|
|
|
- _isAttachedToLogicalTree = this is IStyleRoot;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Raised when the control is attached to a rooted logical tree.
|
|
|
- /// </summary>
|
|
|
- public event EventHandler<LogicalTreeAttachmentEventArgs> AttachedToLogicalTree;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Raised when the control is detached from a rooted logical tree.
|
|
|
- /// </summary>
|
|
|
- public event EventHandler<LogicalTreeAttachmentEventArgs> DetachedFromLogicalTree;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Occurs when the <see cref="DataContext"/> property changes.
|
|
|
- /// </summary>
|
|
|
- /// <remarks>
|
|
|
- /// This event will be raised when the <see cref="DataContext"/> property has changed and
|
|
|
- /// all subscribers to that change have been notified.
|
|
|
- /// </remarks>
|
|
|
- public event EventHandler DataContextChanged;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Occurs when the control has finished initialization.
|
|
|
- /// </summary>
|
|
|
- /// <remarks>
|
|
|
- /// The Initialized event indicates that all property values on the control have been set.
|
|
|
- /// When loading the control from markup, it occurs when
|
|
|
- /// <see cref="ISupportInitialize.EndInit"/> is called *and* the control
|
|
|
- /// is attached to a rooted logical tree. When the control is created by code and
|
|
|
- /// <see cref="ISupportInitialize"/> is not used, it is called when the control is attached
|
|
|
- /// to the visual tree.
|
|
|
- /// </remarks>
|
|
|
- public event EventHandler Initialized;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Occurs when a resource in this control or a parent control has changed.
|
|
|
- /// </summary>
|
|
|
- public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets or sets the name of the control.
|
|
|
- /// </summary>
|
|
|
- /// <remarks>
|
|
|
- /// An element's name is used to uniquely identify a control within the control's name
|
|
|
- /// scope. Once the element is added to a logical tree, its name cannot be changed.
|
|
|
- /// </remarks>
|
|
|
- public string Name
|
|
|
- {
|
|
|
- get
|
|
|
- {
|
|
|
- return _name;
|
|
|
- }
|
|
|
-
|
|
|
- set
|
|
|
- {
|
|
|
- if (String.IsNullOrWhiteSpace(value))
|
|
|
- {
|
|
|
- throw new InvalidOperationException("Cannot set Name to null or empty string.");
|
|
|
- }
|
|
|
-
|
|
|
- if (_styled)
|
|
|
- {
|
|
|
- throw new InvalidOperationException("Cannot set Name : control already styled.");
|
|
|
- }
|
|
|
-
|
|
|
- _name = value;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets or sets the control's classes.
|
|
|
- /// </summary>
|
|
|
- /// <remarks>
|
|
|
- /// <para>
|
|
|
- /// Classes can be used to apply user-defined styling to controls, or to allow controls
|
|
|
- /// that share a common purpose to be easily selected.
|
|
|
- /// </para>
|
|
|
- /// <para>
|
|
|
- /// Even though this property can be set, the setter is only intended for use in object
|
|
|
- /// initializers. Assigning to this property does not change the underlying collection,
|
|
|
- /// it simply clears the existing collection and addds the contents of the assigned
|
|
|
- /// collection.
|
|
|
- /// </para>
|
|
|
- /// </remarks>
|
|
|
- public Classes Classes
|
|
|
- {
|
|
|
- get
|
|
|
- {
|
|
|
- return _classes;
|
|
|
- }
|
|
|
-
|
|
|
- set
|
|
|
- {
|
|
|
- if (_classes != value)
|
|
|
- {
|
|
|
- _classes.Replace(value);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets or sets the control's data context.
|
|
|
- /// </summary>
|
|
|
- /// <remarks>
|
|
|
- /// The data context is an inherited property that specifies the default object that will
|
|
|
- /// be used for data binding.
|
|
|
- /// </remarks>
|
|
|
- public object DataContext
|
|
|
- {
|
|
|
- get { return GetValue(DataContextProperty); }
|
|
|
- set { SetValue(DataContextProperty, value); }
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -253,55 +90,6 @@ namespace Avalonia.Controls
|
|
|
/// </remarks>
|
|
|
public DataTemplates DataTemplates => _dataTemplates ?? (_dataTemplates = new DataTemplates());
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Gets a value that indicates whether the element has finished initialization.
|
|
|
- /// </summary>
|
|
|
- /// <remarks>
|
|
|
- /// For more information about when IsInitialized is set, see the <see cref="Initialized"/>
|
|
|
- /// event.
|
|
|
- /// </remarks>
|
|
|
- public bool IsInitialized { get; private set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets the styles for the control.
|
|
|
- /// </summary>
|
|
|
- /// <remarks>
|
|
|
- /// Styles for the entire application are added to the Application.Styles collection, but
|
|
|
- /// each control may in addition define its own styles which are applied to the control
|
|
|
- /// itself and its children.
|
|
|
- /// </remarks>
|
|
|
- public Styles Styles
|
|
|
- {
|
|
|
- get { return _styles ?? (Styles = new Styles()); }
|
|
|
- set
|
|
|
- {
|
|
|
- Contract.Requires<ArgumentNullException>(value != null);
|
|
|
-
|
|
|
- if (_styles != value)
|
|
|
- {
|
|
|
- if (_styles != null)
|
|
|
- {
|
|
|
- (_styles as ISetStyleParent)?.SetParent(null);
|
|
|
- _styles.ResourcesChanged -= ThisResourcesChanged;
|
|
|
- }
|
|
|
-
|
|
|
- _styles = value;
|
|
|
-
|
|
|
- if (value is ISetStyleParent setParent && setParent.ResourceParent == null)
|
|
|
- {
|
|
|
- setParent.SetParent(this);
|
|
|
- }
|
|
|
-
|
|
|
- _styles.ResourcesChanged += ThisResourcesChanged;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets the control's logical parent.
|
|
|
- /// </summary>
|
|
|
- public IControl Parent => _parent;
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Gets or sets a context menu to the control.
|
|
|
/// </summary>
|
|
|
@@ -311,34 +99,6 @@ namespace Avalonia.Controls
|
|
|
set { SetValue(ContextMenuProperty, value); }
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Gets or sets the control's resource dictionary.
|
|
|
- /// </summary>
|
|
|
- public IResourceDictionary Resources
|
|
|
- {
|
|
|
- get => _resources ?? (Resources = new ResourceDictionary());
|
|
|
- set
|
|
|
- {
|
|
|
- Contract.Requires<ArgumentNullException>(value != null);
|
|
|
-
|
|
|
- var hadResources = false;
|
|
|
-
|
|
|
- if (_resources != null)
|
|
|
- {
|
|
|
- hadResources = _resources.Count > 0;
|
|
|
- _resources.ResourcesChanged -= ThisResourcesChanged;
|
|
|
- }
|
|
|
-
|
|
|
- _resources = value;
|
|
|
- _resources.ResourcesChanged += ThisResourcesChanged;
|
|
|
-
|
|
|
- if (hadResources || _resources.Count > 0)
|
|
|
- {
|
|
|
- ((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Gets or sets a user-defined object attached to the control.
|
|
|
/// </summary>
|
|
|
@@ -348,226 +108,11 @@ namespace Avalonia.Controls
|
|
|
set { SetValue(TagProperty, value); }
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Gets the control whose lookless template this control is part of.
|
|
|
- /// </summary>
|
|
|
- public ITemplatedControl TemplatedParent
|
|
|
- {
|
|
|
- get { return GetValue(TemplatedParentProperty); }
|
|
|
- internal set { SetValue(TemplatedParentProperty, value); }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets the control's logical children.
|
|
|
- /// </summary>
|
|
|
- protected IAvaloniaList<ILogical> LogicalChildren
|
|
|
- {
|
|
|
- get
|
|
|
- {
|
|
|
- if (_logicalChildren == null)
|
|
|
- {
|
|
|
- var list = new AvaloniaList<ILogical>();
|
|
|
- list.ResetBehavior = ResetBehavior.Remove;
|
|
|
- list.Validate = ValidateLogicalChild;
|
|
|
- list.CollectionChanged += LogicalChildrenCollectionChanged;
|
|
|
- _logicalChildren = list;
|
|
|
- }
|
|
|
-
|
|
|
- return _logicalChildren;
|
|
|
- }
|
|
|
- }
|
|
|
+ public new IControl Parent => (IControl)base.Parent;
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Gets the <see cref="Classes"/> collection in a form that allows adding and removing
|
|
|
- /// pseudoclasses.
|
|
|
- /// </summary>
|
|
|
- protected IPseudoClasses PseudoClasses => Classes;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets a value indicating whether the element is attached to a rooted logical tree.
|
|
|
- /// </summary>
|
|
|
- bool ILogical.IsAttachedToLogicalTree => _isAttachedToLogicalTree;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets the control's logical parent.
|
|
|
- /// </summary>
|
|
|
- ILogical ILogical.LogicalParent => Parent;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets the control's logical children.
|
|
|
- /// </summary>
|
|
|
- IAvaloniaReadOnlyList<ILogical> ILogical.LogicalChildren => LogicalChildren;
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources;
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- IResourceNode IResourceNode.ResourceParent => ((IStyleHost)this).StylingParent as IResourceNode;
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- IAvaloniaReadOnlyList<string> IStyleable.Classes => Classes;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets the type by which the control is styled.
|
|
|
- /// </summary>
|
|
|
- /// <remarks>
|
|
|
- /// Usually controls are styled by their own type, but there are instances where you want
|
|
|
- /// a control to be styled by its base type, e.g. creating SpecialButton that
|
|
|
- /// derives from Button and adds extra functionality but is still styled as a regular
|
|
|
- /// Button.
|
|
|
- /// </remarks>
|
|
|
- Type IStyleable.StyleKey => GetType();
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- IObservable<IStyleable> IStyleable.StyleDetach => _styleDetach;
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- bool IStyleHost.IsStylesInitialized => _styles != null;
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- IStyleHost IStyleHost.StylingParent => (IStyleHost)InheritanceParent;
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- public virtual void BeginInit()
|
|
|
- {
|
|
|
- ++_initCount;
|
|
|
- }
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- public virtual void EndInit()
|
|
|
- {
|
|
|
- if (_initCount == 0)
|
|
|
- {
|
|
|
- throw new InvalidOperationException("BeginInit was not called.");
|
|
|
- }
|
|
|
-
|
|
|
- if (--_initCount == 0 && _isAttachedToLogicalTree)
|
|
|
- {
|
|
|
- InitializeStylesIfNeeded();
|
|
|
-
|
|
|
- InitializeIfNeeded();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void InitializeStylesIfNeeded(bool force = false)
|
|
|
- {
|
|
|
- if (_initCount == 0 && (!_styled || force))
|
|
|
- {
|
|
|
- RegisterWithNameScope();
|
|
|
- ApplyStyling();
|
|
|
- _styled = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void InitializeIfNeeded()
|
|
|
- {
|
|
|
- if (_initCount == 0 && !IsInitialized)
|
|
|
- {
|
|
|
- IsInitialized = true;
|
|
|
- Initialized?.Invoke(this, EventArgs.Empty);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- void ILogical.NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
|
|
- {
|
|
|
- this.OnAttachedToLogicalTreeCore(e);
|
|
|
- }
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
|
|
|
- {
|
|
|
- this.OnDetachedFromLogicalTreeCore(e);
|
|
|
- }
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e)
|
|
|
- {
|
|
|
- ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
|
|
|
- }
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- bool IResourceProvider.TryGetResource(string key, out object value)
|
|
|
- {
|
|
|
- value = null;
|
|
|
- return (_resources?.TryGetResource(key, out value) ?? false) ||
|
|
|
- (_styles?.TryGetResource(key, out value) ?? false);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Sets the control's logical parent.
|
|
|
- /// </summary>
|
|
|
- /// <param name="parent">The parent.</param>
|
|
|
- void ISetLogicalParent.SetParent(ILogical parent)
|
|
|
- {
|
|
|
- var old = Parent;
|
|
|
-
|
|
|
- if (parent != old)
|
|
|
- {
|
|
|
- if (old != null && parent != null)
|
|
|
- {
|
|
|
- throw new InvalidOperationException("The Control already has a parent.");
|
|
|
- }
|
|
|
-
|
|
|
- if (_isAttachedToLogicalTree)
|
|
|
- {
|
|
|
- var oldRoot = FindStyleRoot(old) ?? this as IStyleRoot;
|
|
|
-
|
|
|
- if (oldRoot == null)
|
|
|
- {
|
|
|
- throw new AvaloniaInternalException("Was attached to logical tree but cannot find root.");
|
|
|
- }
|
|
|
-
|
|
|
- var e = new LogicalTreeAttachmentEventArgs(oldRoot);
|
|
|
- OnDetachedFromLogicalTreeCore(e);
|
|
|
- }
|
|
|
-
|
|
|
- if (InheritanceParent == null || parent == null)
|
|
|
- {
|
|
|
- InheritanceParent = parent as AvaloniaObject;
|
|
|
- }
|
|
|
-
|
|
|
- _parent = (IControl)parent;
|
|
|
-
|
|
|
- if (old != null)
|
|
|
- {
|
|
|
- old.ResourcesChanged -= ThisResourcesChanged;
|
|
|
- }
|
|
|
- if (_parent != null)
|
|
|
- {
|
|
|
- _parent.ResourcesChanged += ThisResourcesChanged;
|
|
|
- }
|
|
|
- ((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
|
|
|
-
|
|
|
- if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true || this is IStyleRoot)
|
|
|
- {
|
|
|
- var newRoot = FindStyleRoot(this);
|
|
|
-
|
|
|
- if (newRoot == null)
|
|
|
- {
|
|
|
- throw new AvaloniaInternalException("Parent is atttached to logical tree but cannot find root.");
|
|
|
- }
|
|
|
-
|
|
|
- var e = new LogicalTreeAttachmentEventArgs(newRoot);
|
|
|
- OnAttachedToLogicalTreeCore(e);
|
|
|
- }
|
|
|
-
|
|
|
- RaisePropertyChanged(ParentProperty, old, _parent, BindingPriority.LocalValue);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Sets the control's inheritance parent.
|
|
|
- /// </summary>
|
|
|
- /// <param name="parent">The parent.</param>
|
|
|
- void ISetInheritanceParent.SetParent(IAvaloniaObject parent)
|
|
|
- {
|
|
|
- InheritanceParent = parent;
|
|
|
- }
|
|
|
-
|
|
|
/// <inheritdoc/>
|
|
|
void IVisualBrushInitialize.EnsureInitialized()
|
|
|
{
|
|
|
@@ -600,52 +145,6 @@ namespace Avalonia.Controls
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Adds a pseudo-class to be set when a property is true.
|
|
|
- /// </summary>
|
|
|
- /// <param name="property">The property.</param>
|
|
|
- /// <param name="className">The pseudo-class.</param>
|
|
|
- protected static void PseudoClass(AvaloniaProperty<bool> property, string className)
|
|
|
- {
|
|
|
- PseudoClass(property, x => x, className);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Adds a pseudo-class to be set when a property equals a certain value.
|
|
|
- /// </summary>
|
|
|
- /// <typeparam name="T">The type of the property.</typeparam>
|
|
|
- /// <param name="property">The property.</param>
|
|
|
- /// <param name="selector">Returns a boolean value based on the property value.</param>
|
|
|
- /// <param name="className">The pseudo-class.</param>
|
|
|
- protected static void PseudoClass<T>(
|
|
|
- AvaloniaProperty<T> property,
|
|
|
- Func<T, bool> selector,
|
|
|
- string className)
|
|
|
- {
|
|
|
- Contract.Requires<ArgumentNullException>(property != null);
|
|
|
- Contract.Requires<ArgumentNullException>(selector != null);
|
|
|
- Contract.Requires<ArgumentNullException>(className != null);
|
|
|
-
|
|
|
- if (string.IsNullOrWhiteSpace(className))
|
|
|
- {
|
|
|
- throw new ArgumentException("Cannot supply an empty className.");
|
|
|
- }
|
|
|
-
|
|
|
- property.Changed.Merge(property.Initialized)
|
|
|
- .Where(e => e.Sender is Control)
|
|
|
- .Subscribe(e =>
|
|
|
- {
|
|
|
- if (selector((T)e.NewValue))
|
|
|
- {
|
|
|
- ((Control)e.Sender).PseudoClasses.Add(className);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- ((Control)e.Sender).PseudoClasses.Remove(className);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Gets the element that recieves the focus adorner.
|
|
|
/// </summary>
|
|
|
@@ -655,22 +154,6 @@ namespace Avalonia.Controls
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Called when the control is added to a rooted logical tree.
|
|
|
- /// </summary>
|
|
|
- /// <param name="e">The event args.</param>
|
|
|
- protected virtual void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
|
|
- {
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Called when the control is removed from a rooted logical tree.
|
|
|
- /// </summary>
|
|
|
- /// <param name="e">The event args.</param>
|
|
|
- protected virtual void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
|
|
|
- {
|
|
|
- }
|
|
|
-
|
|
|
/// <inheritdoc/>
|
|
|
protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
|
|
|
{
|
|
|
@@ -685,29 +168,6 @@ namespace Avalonia.Controls
|
|
|
base.OnDetachedFromVisualTreeCore(e);
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Called when the <see cref="DataContext"/> property changes.
|
|
|
- /// </summary>
|
|
|
- /// <param name="e">The event args.</param>
|
|
|
- protected virtual void OnDataContextChanged(EventArgs e)
|
|
|
- {
|
|
|
- DataContextChanged?.Invoke(this, EventArgs.Empty);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Called when the <see cref="DataContext"/> begins updating.
|
|
|
- /// </summary>
|
|
|
- protected virtual void OnDataContextBeginUpdate()
|
|
|
- {
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Called when the <see cref="DataContext"/> finishes updating.
|
|
|
- /// </summary>
|
|
|
- protected virtual void OnDataContextEndUpdate()
|
|
|
- {
|
|
|
- }
|
|
|
-
|
|
|
/// <inheritdoc/>
|
|
|
protected override void OnGotFocus(GotFocusEventArgs e)
|
|
|
{
|
|
|
@@ -757,211 +217,5 @@ namespace Avalonia.Controls
|
|
|
_focusAdorner = null;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- private static void DataContextNotifying(IAvaloniaObject o, bool notifying)
|
|
|
- {
|
|
|
- if (o is Control control)
|
|
|
- {
|
|
|
- DataContextNotifying(control, notifying);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void DataContextNotifying(Control control, bool notifying)
|
|
|
- {
|
|
|
- if (notifying)
|
|
|
- {
|
|
|
- if (!control._dataContextUpdating)
|
|
|
- {
|
|
|
- control._dataContextUpdating = true;
|
|
|
- control.OnDataContextBeginUpdate();
|
|
|
-
|
|
|
- foreach (var child in control.LogicalChildren)
|
|
|
- {
|
|
|
- if (child is Control c &&
|
|
|
- c.InheritanceParent == control &&
|
|
|
- !c.IsSet(DataContextProperty))
|
|
|
- {
|
|
|
- DataContextNotifying(c, notifying);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if (control._dataContextUpdating)
|
|
|
- {
|
|
|
- control.OnDataContextEndUpdate();
|
|
|
- control._dataContextUpdating = false;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static IStyleRoot FindStyleRoot(IStyleHost e)
|
|
|
- {
|
|
|
- while (e != null)
|
|
|
- {
|
|
|
- if (e is IRenderRoot root)
|
|
|
- {
|
|
|
- return root as IStyleRoot;
|
|
|
- }
|
|
|
-
|
|
|
- e = e.StylingParent;
|
|
|
- }
|
|
|
-
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- private void ApplyStyling()
|
|
|
- {
|
|
|
- AvaloniaLocator.Current.GetService<IStyler>()?.ApplyStyles(this);
|
|
|
- }
|
|
|
-
|
|
|
- private void RegisterWithNameScope()
|
|
|
- {
|
|
|
- if (_nameScope == null)
|
|
|
- {
|
|
|
- _nameScope = NameScope.GetNameScope(this) ?? ((Control)Parent)?._nameScope;
|
|
|
- }
|
|
|
-
|
|
|
- if (Name != null)
|
|
|
- {
|
|
|
- _nameScope?.Register(Name, this);
|
|
|
-
|
|
|
- var visualParent = Parent as Visual;
|
|
|
-
|
|
|
- if (this is INameScope && visualParent != null)
|
|
|
- {
|
|
|
- // If we have e.g. a named UserControl in a window then we want that control
|
|
|
- // to be findable by name from the Window, so register with both name scopes.
|
|
|
- // This differs from WPF's behavior in that XAML manually registers controls
|
|
|
- // with name scopes based on the XAML file in which the name attribute appears,
|
|
|
- // but we're trying to avoid XAML magic in Avalonia in order to made code-
|
|
|
- // created UIs easy. This will cause problems if a UserControl declares a name
|
|
|
- // in its XAML and that control is included multiple times in a parent control
|
|
|
- // (as the name will be duplicated), however at the moment I'm fine with saying
|
|
|
- // "don't do that".
|
|
|
- var parentNameScope = NameScope.FindNameScope(visualParent);
|
|
|
- parentNameScope?.Register(Name, this);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void ValidateLogicalChild(ILogical c)
|
|
|
- {
|
|
|
- if (c == null)
|
|
|
- {
|
|
|
- throw new ArgumentException("Cannot add null to LogicalChildren.");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs e)
|
|
|
- {
|
|
|
- // This method can be called when a control is already attached to the logical tree
|
|
|
- // in the following scenario:
|
|
|
- // - ListBox gets assigned Items containing ListBoxItem
|
|
|
- // - ListBox makes ListBoxItem a logical child
|
|
|
- // - ListBox template gets applied; making its Panel get attached to logical tree
|
|
|
- // - That AttachedToLogicalTree signal travels down to the ListBoxItem
|
|
|
- if (!_isAttachedToLogicalTree)
|
|
|
- {
|
|
|
- _isAttachedToLogicalTree = true;
|
|
|
-
|
|
|
- InitializeStylesIfNeeded(true);
|
|
|
-
|
|
|
- OnAttachedToLogicalTree(e);
|
|
|
- AttachedToLogicalTree?.Invoke(this, e);
|
|
|
- }
|
|
|
-
|
|
|
- foreach (var child in LogicalChildren.OfType<Control>())
|
|
|
- {
|
|
|
- child.OnAttachedToLogicalTreeCore(e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void OnDetachedFromLogicalTreeCore(LogicalTreeAttachmentEventArgs e)
|
|
|
- {
|
|
|
- if (_isAttachedToLogicalTree)
|
|
|
- {
|
|
|
- if (Name != null)
|
|
|
- {
|
|
|
- _nameScope?.Unregister(Name);
|
|
|
- }
|
|
|
-
|
|
|
- _isAttachedToLogicalTree = false;
|
|
|
- _styleDetach.OnNext(this);
|
|
|
- OnDetachedFromLogicalTree(e);
|
|
|
- DetachedFromLogicalTree?.Invoke(this, e);
|
|
|
-
|
|
|
- foreach (var child in LogicalChildren.OfType<Control>())
|
|
|
- {
|
|
|
- child.OnDetachedFromLogicalTreeCore(e);
|
|
|
- }
|
|
|
-
|
|
|
-#if DEBUG
|
|
|
- if (((INotifyCollectionChangedDebug)_classes).GetCollectionChangedSubscribers()?.Length > 0)
|
|
|
- {
|
|
|
- Logger.Warning(
|
|
|
- LogArea.Control,
|
|
|
- this,
|
|
|
- "{Type} detached from logical tree but still has class listeners",
|
|
|
- this.GetType());
|
|
|
- }
|
|
|
-#endif
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void OnDataContextChangedCore(AvaloniaPropertyChangedEventArgs e)
|
|
|
- {
|
|
|
- OnDataContextChanged(EventArgs.Empty);
|
|
|
- }
|
|
|
-
|
|
|
- private void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
|
- {
|
|
|
- switch (e.Action)
|
|
|
- {
|
|
|
- case NotifyCollectionChangedAction.Add:
|
|
|
- SetLogicalParent(e.NewItems.Cast<ILogical>());
|
|
|
- break;
|
|
|
-
|
|
|
- case NotifyCollectionChangedAction.Remove:
|
|
|
- ClearLogicalParent(e.OldItems.Cast<ILogical>());
|
|
|
- break;
|
|
|
-
|
|
|
- case NotifyCollectionChangedAction.Replace:
|
|
|
- ClearLogicalParent(e.OldItems.Cast<ILogical>());
|
|
|
- SetLogicalParent(e.NewItems.Cast<ILogical>());
|
|
|
- break;
|
|
|
-
|
|
|
- case NotifyCollectionChangedAction.Reset:
|
|
|
- throw new NotSupportedException("Reset should not be signalled on LogicalChildren collection");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void SetLogicalParent(IEnumerable<ILogical> children)
|
|
|
- {
|
|
|
- foreach (var i in children)
|
|
|
- {
|
|
|
- if (i.LogicalParent == null)
|
|
|
- {
|
|
|
- ((ISetLogicalParent)i).SetParent(this);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void ClearLogicalParent(IEnumerable<ILogical> children)
|
|
|
- {
|
|
|
- foreach (var i in children)
|
|
|
- {
|
|
|
- if (i.LogicalParent == this)
|
|
|
- {
|
|
|
- ((ISetLogicalParent)i).SetParent(null);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
|
|
|
- {
|
|
|
- ((ILogical)this).NotifyResourcesChanged(e);
|
|
|
- }
|
|
|
}
|
|
|
}
|