Refactor Flyout Bindings

This commit is contained in:
chinboy
2025-08-27 15:28:02 +08:00
parent e2f36c75cd
commit b5ca0d3add
6 changed files with 129 additions and 47 deletions
+16 -12
View File
@@ -104,7 +104,9 @@ public class Flyout : PopupFlyoutBase
#endregion
private Classes? _classes;
private CompositeDisposable? _presenterBindingDisposables;
private CompositeDisposable? _popupBindingDisposables;
/// <summary>
/// Gets the Classes collection to apply to the FlyoutPresenter this Flyout is hosting
/// </summary>
@@ -150,12 +152,12 @@ public class Flyout : PopupFlyoutBase
protected override Control CreatePresenter()
{
var presenter = new FlyoutPresenter
{
[!FlyoutPresenter.ContentProperty] = this[!ContentProperty],
[!FlyoutPresenter.IsMotionEnabledProperty] = this[!IsMotionEnabledProperty],
[!FlyoutPresenter.IsShowArrowProperty] = this[!IsShowArrowEffectiveProperty],
};
_presenterBindingDisposables?.Dispose();
_presenterBindingDisposables = new CompositeDisposable(3);
var presenter = new FlyoutPresenter();
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, ContentProperty, presenter, FlyoutPresenter.ContentProperty));
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, presenter, FlyoutPresenter.IsMotionEnabledProperty));
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, presenter, FlyoutPresenter.IsShowArrowProperty));
CalculateShowArrowEffective();
SetupArrowPosition(Popup, presenter);
return presenter;
@@ -164,11 +166,13 @@ public class Flyout : PopupFlyoutBase
protected internal override void NotifyPopupCreated(Popup popup)
{
base.NotifyPopupCreated(popup);
BindUtils.RelayBind(this, PlacementProperty, popup, Popup.PlacementProperty);
BindUtils.RelayBind(this, PlacementAnchorProperty, popup, Popup.PlacementAnchorProperty);
BindUtils.RelayBind(this, PlacementGravityProperty, popup, Popup.PlacementGravityProperty);
BindUtils.RelayBind(this, MaskShadowsProperty, popup, Popup.MaskShadowsProperty);
BindUtils.RelayBind(this, IsMotionEnabledProperty, popup, Popup.IsMotionEnabledProperty);
_popupBindingDisposables?.Dispose();
_popupBindingDisposables = new CompositeDisposable(5);
_popupBindingDisposables.Add(BindUtils.RelayBind(this, PlacementProperty, popup, Popup.PlacementProperty));
_popupBindingDisposables.Add(BindUtils.RelayBind(this, PlacementAnchorProperty, popup, Popup.PlacementAnchorProperty));
_popupBindingDisposables.Add(BindUtils.RelayBind(this, PlacementGravityProperty, popup, Popup.PlacementGravityProperty));
_popupBindingDisposables.Add(BindUtils.RelayBind(this, MaskShadowsProperty, popup, Popup.MaskShadowsProperty));
_popupBindingDisposables.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, popup, Popup.IsMotionEnabledProperty));
SetupArrowPosition(popup);
}
+17 -14
View File
@@ -1,5 +1,6 @@
using System.Collections;
using System.ComponentModel;
using System.Reactive.Disposables;
using AtomUI.Controls.Utils;
using AtomUI.Data;
using Avalonia;
@@ -44,17 +45,17 @@ public class MenuFlyout : Flyout
get => GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
#endregion
public Func<IPopupHostProvider, RawPointerEventArgs, bool>? ClickHideFlyoutPredicate;
private IDisposable? _detectMouseClickDisposable;
private MenuFlyoutPresenter? _presenter;
[Content]
public ItemCollection Items { get; }
public Func<IPopupHostProvider, RawPointerEventArgs, bool>? ClickHideFlyoutPredicate;
#endregion
private IDisposable? _detectMouseClickDisposable;
private MenuFlyoutPresenter? _presenter;
private CompositeDisposable? _presenterBindingDisposables;
public MenuFlyout()
{
var itemCollectionType = typeof(ItemCollection);
@@ -63,16 +64,16 @@ public class MenuFlyout : Flyout
protected override Control CreatePresenter()
{
_presenterBindingDisposables?.Dispose();
_presenterBindingDisposables = new CompositeDisposable(4);
_presenter = new MenuFlyoutPresenter
{
ItemsSource = Items,
[!ItemsControl.ItemTemplateProperty] = this[!ItemTemplateProperty],
[!ItemsControl.ItemContainerThemeProperty] = this[!ItemContainerThemeProperty],
[!MenuFlyoutPresenter.IsMotionEnabledProperty] = this[!IsMotionEnabledProperty],
MenuFlyout = this
MenuFlyout = this
};
BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, _presenter, MenuFlyoutPresenter.IsShowArrowProperty);
BindUtils.RelayBind(this, IsMotionEnabledProperty, _presenter, MenuFlyoutPresenter.IsMotionEnabledProperty);
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, ItemTemplateProperty, _presenter, MenuFlyoutPresenter.ItemTemplateProperty));
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, ItemContainerThemeProperty, _presenter, MenuFlyoutPresenter.ItemContainerThemeProperty));
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, _presenter, MenuFlyoutPresenter.IsShowArrowProperty));
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, _presenter, MenuFlyoutPresenter.IsMotionEnabledProperty));
SetupArrowPosition(Popup, _presenter);
CalculateShowArrowEffective();
@@ -173,6 +174,7 @@ public class MenuFlyout : Flyout
{
if (_detectMouseClickDisposable is not null)
{
_detectMouseClickDisposable.Dispose();
CompositeDisposable?.Remove(_detectMouseClickDisposable);
}
@@ -184,6 +186,7 @@ public class MenuFlyout : Flyout
{
if (_detectMouseClickDisposable is not null)
{
_detectMouseClickDisposable.Dispose();
CompositeDisposable?.Remove(_detectMouseClickDisposable);
}
}
@@ -1,4 +1,6 @@
using System.Diagnostics;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Reactive.Disposables;
using AtomUI.Controls.Themes;
using AtomUI.Data;
using AtomUI.Theme;
@@ -76,17 +78,41 @@ public class MenuFlyoutPresenter : MenuBase,
#endregion
private ArrowDecoratedBox? _arrowDecoratedBox;
private Dictionary<MenuItem, CompositeDisposable> _itemsBindingDisposables = new();
public MenuFlyoutPresenter()
: base(new DefaultMenuInteractionHandler(true))
{
this.RegisterResources();
Items.CollectionChanged += HandleCollectionChanged;
}
public MenuFlyoutPresenter(IMenuInteractionHandler menuInteractionHandler)
: base(menuInteractionHandler)
{
this.RegisterResources();
Items.CollectionChanged += HandleCollectionChanged;
}
private void HandleCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 0)
{
foreach (var item in e.OldItems)
{
if (item is MenuItem menuItem)
{
if (_itemsBindingDisposables.TryGetValue(menuItem, out var disposable))
{
disposable.Dispose();
_itemsBindingDisposables.Remove(menuItem);
}
}
}
}
}
}
public override void Close()
@@ -244,7 +270,14 @@ public class MenuFlyoutPresenter : MenuBase,
{
if (container is MenuItem menuItem)
{
BindUtils.RelayBind(this, IsMotionEnabledProperty, menuItem, MenuItem.IsMotionEnabledProperty);
var disposables = new CompositeDisposable(1);
disposables.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, menuItem, MenuItem.IsMotionEnabledProperty));
if (_itemsBindingDisposables.TryGetValue(menuItem, out var oldDisposables))
{
oldDisposables.Dispose();
_itemsBindingDisposables.Remove(menuItem);
}
_itemsBindingDisposables.Add(menuItem, disposables);
}
base.PrepareContainerForItemOverride(container, item, index);
+10 -6
View File
@@ -1,4 +1,5 @@
using System.ComponentModel;
using System.Reactive.Disposables;
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.MotionScene;
@@ -226,6 +227,7 @@ public abstract class PopupFlyoutBase : FlyoutBase, IPopupHostProvider
private PixelRect? _enlargePopupRectScreenPixelRect;
private IDisposable? _transientDisposable;
private Action<IPopupHost?>? _popupHostChangedHandler;
private CompositeDisposable? _popupBindingDisposables;
static PopupFlyoutBase()
{
@@ -239,12 +241,14 @@ public abstract class PopupFlyoutBase : FlyoutBase, IPopupHostProvider
protected internal virtual void NotifyPopupCreated(Popup popup)
{
BindUtils.RelayBind(this, MarginToAnchorProperty, popup);
BindUtils.RelayBind(this, IsDetectMouseClickEnabledProperty, popup, Popup.IsDetectMouseClickEnabledProperty);
BindUtils.RelayBind(this, IsMotionEnabledProperty, popup, Popup.IsMotionEnabledProperty);
BindUtils.RelayBind(this, MotionDurationProperty, popup, Popup.MotionDurationProperty);
BindUtils.RelayBind(this, OpenMotionProperty, popup, Popup.OpenMotionProperty);
BindUtils.RelayBind(this, CloseMotionProperty, popup, Popup.CloseMotionProperty);
_popupBindingDisposables?.Dispose();
_popupBindingDisposables = new CompositeDisposable(6);
_popupBindingDisposables.Add(BindUtils.RelayBind(this, MarginToAnchorProperty, popup));
_popupBindingDisposables.Add(BindUtils.RelayBind(this, IsDetectMouseClickEnabledProperty, popup, Popup.IsDetectMouseClickEnabledProperty));
_popupBindingDisposables.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, popup, Popup.IsMotionEnabledProperty));
_popupBindingDisposables.Add(BindUtils.RelayBind(this, MotionDurationProperty, popup, Popup.MotionDurationProperty));
_popupBindingDisposables.Add(BindUtils.RelayBind(this, OpenMotionProperty, popup, Popup.OpenMotionProperty));
_popupBindingDisposables.Add(BindUtils.RelayBind(this, CloseMotionProperty, popup, Popup.CloseMotionProperty));
PopupCreated?.Invoke(this, new FlyoutPopupCreatedEventArgs(popup));
}
+16 -12
View File
@@ -1,5 +1,6 @@
using System.Collections;
using System.ComponentModel;
using System.Reactive.Disposables;
using AtomUI.Controls.Utils;
using AtomUI.Data;
using Avalonia;
@@ -44,15 +45,15 @@ public class TreeViewFlyout : Flyout
get => GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
#endregion
[Content] public ItemCollection Items { get; }
public Func<IPopupHostProvider, RawPointerEventArgs, bool>? ClickHideFlyoutPredicate;
#endregion
private IDisposable? _detectMouseClickDisposable;
[Content] public ItemCollection Items { get; }
private CompositeDisposable? _presenterBindingDisposables;
public TreeViewFlyout()
{
var itemCollectionType = typeof(ItemCollection);
@@ -61,16 +62,17 @@ public class TreeViewFlyout : Flyout
protected override Control CreatePresenter()
{
_presenterBindingDisposables?.Dispose();
_presenterBindingDisposables = new CompositeDisposable(4);
var presenter = new TreeViewFlyoutPresenter
{
ItemsSource = Items,
[!ItemsControl.ItemTemplateProperty] = this[!ItemTemplateProperty],
[!ItemsControl.ItemContainerThemeProperty] = this[!ItemContainerThemeProperty],
ItemsSource = Items
};
presenter.TreeViewFlyout = this;
BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, presenter, TreeViewFlyoutPresenter.IsShowArrowProperty);
BindUtils.RelayBind(this, IsMotionEnabledProperty, presenter, TreeViewFlyoutPresenter.IsMotionEnabledProperty);
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, ItemTemplateProperty, presenter, TreeViewFlyoutPresenter.ItemTemplateProperty));
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, ItemContainerThemeProperty, presenter, TreeViewFlyoutPresenter.ItemContainerThemeProperty));
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, IsShowArrowEffectiveProperty, presenter, TreeViewFlyoutPresenter.IsShowArrowProperty));
_presenterBindingDisposables.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, presenter, TreeViewFlyoutPresenter.IsMotionEnabledProperty));
SetupArrowPosition(Popup, presenter);
CalculateShowArrowEffective();
@@ -172,6 +174,7 @@ public class TreeViewFlyout : Flyout
{
if (_detectMouseClickDisposable is not null)
{
_detectMouseClickDisposable.Dispose();
CompositeDisposable?.Remove(_detectMouseClickDisposable);
}
@@ -183,6 +186,7 @@ public class TreeViewFlyout : Flyout
{
if (_detectMouseClickDisposable is not null)
{
_detectMouseClickDisposable.Dispose();
CompositeDisposable?.Remove(_detectMouseClickDisposable);
}
}
@@ -1,3 +1,5 @@
using System.Collections.Specialized;
using System.Reactive.Disposables;
using AtomUI.Controls.Themes;
using AtomUI.Data;
using AtomUI.Theme;
@@ -68,16 +70,41 @@ public class TreeViewFlyoutPresenter : FloatableTreeView,
#endregion
private Dictionary<MenuItem, CompositeDisposable> _itemsBindingDisposables = new();
public TreeViewFlyoutPresenter()
: base(new DefaultTreeViewInteractionHandler(true))
{
this.RegisterResources();
Items.CollectionChanged += HandleCollectionChanged;
}
public TreeViewFlyoutPresenter(ITreeViewInteractionHandler menuInteractionHandler)
: base(menuInteractionHandler)
{
this.RegisterResources();
Items.CollectionChanged += HandleCollectionChanged;
}
private void HandleCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Count > 0)
{
foreach (var item in e.OldItems)
{
if (item is MenuItem menuItem)
{
if (_itemsBindingDisposables.TryGetValue(menuItem, out var disposable))
{
disposable.Dispose();
_itemsBindingDisposables.Remove(menuItem);
}
}
}
}
}
}
protected override void ContainerForItemPreparedOverride(Control container, object? item, int index)
@@ -172,7 +199,14 @@ public class TreeViewFlyoutPresenter : FloatableTreeView,
{
if (container is MenuItem menuItem)
{
BindUtils.RelayBind(this, IsMotionEnabledProperty, menuItem, MenuItem.IsMotionEnabledProperty);
var disposables = new CompositeDisposable(1);
disposables.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, menuItem, MenuItem.IsMotionEnabledProperty));
if (_itemsBindingDisposables.TryGetValue(menuItem, out var oldDisposables))
{
oldDisposables.Dispose();
_itemsBindingDisposables.Remove(menuItem);
}
_itemsBindingDisposables.Add(menuItem, disposables);
}
base.PrepareContainerForItemOverride(container, item, index);