NoesisApp.Behavior gets detached when manipulating UI from code
Posted: 26 Jan 2023, 14:37
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");
}
}