NoesisApp.Behavior gets detached when manipulating UI from code
Hello, recently I've stumbled on a following problem: I have an ItemsControl in my UI and I wish to sort items in it. Items are created and added/removed from ItemsControl.Items from code-behind. Some items do have attached Behaviors. When sorting items I naturally have to remove and insert them to ItemsControl.Items collection.
The problem is, when an item is removed from Items collection, an implementation of AttachableObject receives an event that item is detached from visual tree, and Behaviors attached to this item gets detached. However, when an item is inserted back into Items collection Behaviors are not reattached and simply stop working.
I've prepared a small sample. Lets create a TestView.xaml with the following content:
TestView.xaml.cs:
TestBehaviour just logs its attachment actions in to debug log:
Clicking on a button in a TestView removes Border control with an attached behavior from Items collection and immediately adds it back, but debug log shows that after first click the behavior detaches and is never reattached back. I hope someone can suggest a generic workaround, because implementing some sort of custom tracking of behaviors attaching is going to be a pain. Thanks.
The problem is, when an item is removed from Items collection, an implementation of AttachableObject receives an event that item is detached from visual tree, and Behaviors attached to this item gets detached. However, when an item is inserted back into Items collection Behaviors are not reattached and simply stop working.
I've prepared a small sample. Lets create a TestView.xaml with the following content:
Code: Select all
<UserControl
... namespaces skipped
mc:Ignorable="d">
<StackPanel Orientation="Vertical">
<ItemsControl x:Name="itemsControl">
<Border Width="100" Height="100" Background="Red">
<i:Interaction.Behaviors>
<attach:TestBehaviour />
</i:Interaction.Behaviors>
</Border>
</ItemsControl>
<Button Click="Button_Click">Sort</Button>
</StackPanel>
</UserControl>
Code: Select all
public partial class TestView : UserControl
{
public TestView()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var itemsControl = FindName("itemsControl") as ItemsControl;
var item = itemsControl.Items[0];
itemsControl.Items.RemoveAt(0);
itemsControl.Items.Insert(0, item);
}
private void InitializeComponent()
{
NoesisUnity.LoadComponent(this);
}
protected override bool ConnectEvent(object source, string eventName, string handlerName)
{
if (eventName == nameof(Button.Click) && handlerName == nameof(Button_Click))
{
((Button)source).Click += this.Button_Click;
return true;
}
return base.ConnectEvent(source, eventName, handlerName);
}
}
Code: Select all
public class TestBehaviour : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
base.OnAttached();
System.Diagnostics.Debug.WriteLine($"Attached to {AssociatedObject}");
}
protected override void OnDetaching()
{
base.OnDetaching();
System.Diagnostics.Debug.WriteLine($"Detached");
}
}
-
-
sfernandez
Site Admin
- Posts: 2783
- Joined:
Re: NoesisApp.Behavior gets detached when manipulating UI from code
As it happens in WPF, the behavior is only Detached when it is removed from the associated object, or when the associated object is destroyed.
If you are interested in knowing when an item is removed from the tree and added back, your behavior can hook to the Loaded and Unloaded events of the associated object, is that what you need?
If you are interested in knowing when an item is removed from the tree and added back, your behavior can hook to the Loaded and Unloaded events of the associated object, is that what you need?
Re: NoesisApp.Behavior gets detached when manipulating UI from code
As you can see in my sample code, it's not exactly like that. In WPF I can temporarily remove object from visual tree and add it back, add behaviours on this object won't be detached. However, in Noesis removing an object from visual tree causes all attached behaviours to detach, because an implementation of NoesisApp.AttachableObject decides that removing object from visual tree means that it's about to be destroyed. To cite code from Unity SDK package:As it happens in WPF, the behavior is only Detached when it is removed from the associated object, or when the associated object is destroyed.
Code: Select all
public abstract class AttachableObject : Animatable, IAttachedObject
{
// We use a binding to get notified when the associated object is disconnected from the tree
// and is going to be destroyed, so we can properly detach from it
private static readonly DependencyProperty AttachmentProperty = DependencyProperty.Register(
".Attachment", typeof(Visibility), typeof(AttachableObject),
new PropertyMetadata(Detached, OnAttachmentChanged));
public void Attach(DependencyObject associatedObject)
{
if (AssociatedObject != associatedObject)
{
... skipped
BindingOperations.SetBinding(this, AttachmentProperty, new Binding("Visibility")
{
RelativeSource = new RelativeSource
{
AncestorType = typeof(UIElement)
}
});
InitObject();
OnAttached();
}
}
private static void OnAttachmentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((Visibility)e.NewValue == (Visibility)(-1))
{
AttachableObject attachableObject = (AttachableObject)d;
attachableObject.Detach();
}
}
}
-
-
sfernandez
Site Admin
- Posts: 2783
- Joined:
Re: NoesisApp.Behavior gets detached when manipulating UI from code
Oh, I see what you mean now... this looks like a bug in our C# implementation, because the behaviors shouldn't detach unless the object is going to be destroyed. Could you please report it in our bugtracker?
Re: NoesisApp.Behavior gets detached when manipulating UI from code
Sure, I filed a bug: https://www.noesisengine.com/bugs/view.php?id=2498
By the way, I've found a workaround: I can get AttachableObject.AttachmentProperty with reflection and use DependencyProperty.GetMetadata to replace original property change callback with my own. I wrapped this logic in a Disposable object, so on Dispose I can find out which objects should really be detached and set original callback back. WPF does not allow to set PropertyChangeCallback after it has been registered, but I'd be grateful if you won't fix this discrepancy in Noesis :)
By the way, I've found a workaround: I can get AttachableObject.AttachmentProperty with reflection and use DependencyProperty.GetMetadata to replace original property change callback with my own. I wrapped this logic in a Disposable object, so on Dispose I can find out which objects should really be detached and set original callback back. WPF does not allow to set PropertyChangeCallback after it has been registered, but I'd be grateful if you won't fix this discrepancy in Noesis :)
-
-
sfernandez
Site Admin
- Posts: 2783
- Joined:
Re: NoesisApp.Behavior gets detached when manipulating UI from code
Thanks for the report.
As for the workaround, could you post it in the ticket? just curious how you implement it :)
As for the workaround, could you post it in the ticket? just curious how you implement it :)
Who is online
Users browsing this forum: Ahrefs [Bot] and 1 guest