csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
5.7 KiB
148 lines
5.7 KiB
// Copyright (c) The Avalonia Project. All rights reserved.
|
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Reactive.Disposables;
|
|
using System.Runtime.CompilerServices;
|
|
using Avalonia.Collections;
|
|
using Avalonia.Controls.Presenters;
|
|
using Avalonia.Controls.Primitives;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.LogicalTree;
|
|
|
|
namespace Avalonia.Controls.Mixins
|
|
{
|
|
/// <summary>
|
|
/// Adds content control functionality to control classes.
|
|
/// </summary>
|
|
/// <para>
|
|
/// The <see cref="ContentControlMixin"/> adds behavior to a control which acts as a content
|
|
/// control such as <see cref="ContentControl"/> and <see cref="HeaderedItemsControl"/>. It
|
|
/// updates keeps the control's logical children in sync with the content being displayed by
|
|
/// the control.
|
|
/// </para>
|
|
public class ContentControlMixin
|
|
{
|
|
private static Lazy<ConditionalWeakTable<TemplatedControl, IDisposable>> subscriptions =
|
|
new Lazy<ConditionalWeakTable<TemplatedControl, IDisposable>>(() =>
|
|
new ConditionalWeakTable<TemplatedControl, IDisposable>());
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="SelectableMixin"/> class.
|
|
/// </summary>
|
|
/// <typeparam name="TControl">The control type.</typeparam>
|
|
/// <param name="content">The content property.</param>
|
|
/// <param name="logicalChildrenSelector">
|
|
/// Given an control of <typeparamref name="TControl"/> should return the control's
|
|
/// logical children collection.
|
|
/// </param>
|
|
/// <param name="presenterName">
|
|
/// The name of the content presenter in the control's template.
|
|
/// </param>
|
|
public static void Attach<TControl>(
|
|
AvaloniaProperty content,
|
|
Func<TControl, IAvaloniaList<ILogical>> logicalChildrenSelector,
|
|
string presenterName = "PART_ContentPresenter")
|
|
where TControl : TemplatedControl
|
|
{
|
|
Contract.Requires<ArgumentNullException>(content != null);
|
|
Contract.Requires<ArgumentNullException>(logicalChildrenSelector != null);
|
|
|
|
void TemplateApplied(object s, RoutedEventArgs ev)
|
|
{
|
|
if (s is TControl sender)
|
|
{
|
|
var e = (TemplateAppliedEventArgs)ev;
|
|
var presenter = (IControl)e.NameScope.Find(presenterName);
|
|
|
|
if (presenter != null)
|
|
{
|
|
presenter.ApplyTemplate();
|
|
|
|
var logicalChildren = logicalChildrenSelector(sender);
|
|
var subscription = presenter
|
|
.GetPropertyChangedObservable(ContentPresenter.ChildProperty)
|
|
.Subscribe(c => UpdateLogicalChild(
|
|
sender,
|
|
logicalChildren,
|
|
c.OldValue,
|
|
c.NewValue));
|
|
|
|
UpdateLogicalChild(
|
|
sender,
|
|
logicalChildren,
|
|
null,
|
|
presenter.GetValue(ContentPresenter.ChildProperty));
|
|
|
|
if (subscriptions.Value.TryGetValue(sender, out IDisposable previousSubscription))
|
|
{
|
|
subscription = new CompositeDisposable(previousSubscription, subscription);
|
|
subscriptions.Value.Remove(sender);
|
|
}
|
|
|
|
subscriptions.Value.Add(sender, subscription);
|
|
}
|
|
}
|
|
}
|
|
|
|
TemplatedControl.TemplateAppliedEvent.AddClassHandler(
|
|
typeof(TControl),
|
|
TemplateApplied,
|
|
RoutingStrategies.Direct);
|
|
|
|
content.Changed.Subscribe(e =>
|
|
{
|
|
if (e.Sender is TControl sender)
|
|
{
|
|
var logicalChildren = logicalChildrenSelector(sender);
|
|
UpdateLogicalChild(sender, logicalChildren, e.OldValue, e.NewValue);
|
|
}
|
|
});
|
|
|
|
Control.TemplatedParentProperty.Changed.Subscribe(e =>
|
|
{
|
|
if (e.Sender is TControl sender)
|
|
{
|
|
var logicalChild = logicalChildrenSelector(sender).FirstOrDefault() as IControl;
|
|
logicalChild?.SetValue(Control.TemplatedParentProperty, sender.TemplatedParent);
|
|
}
|
|
});
|
|
|
|
TemplatedControl.TemplateProperty.Changed.Subscribe(e =>
|
|
{
|
|
if (e.Sender is TControl sender)
|
|
{
|
|
if (subscriptions.Value.TryGetValue(sender, out IDisposable subscription))
|
|
{
|
|
subscription.Dispose();
|
|
subscriptions.Value.Remove(sender);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private static void UpdateLogicalChild(
|
|
IControl control,
|
|
IAvaloniaList<ILogical> logicalChildren,
|
|
object oldValue,
|
|
object newValue)
|
|
{
|
|
if (oldValue != newValue)
|
|
{
|
|
if (oldValue is IControl child)
|
|
{
|
|
logicalChildren.Remove(child);
|
|
}
|
|
|
|
child = newValue as IControl;
|
|
|
|
if (child != null && !logicalChildren.Contains(child))
|
|
{
|
|
child.SetValue(Control.TemplatedParentProperty, control.TemplatedParent);
|
|
logicalChildren.Add(child);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|