Search code examples
c#wpflistviewwindows-store-appsbehavior

Trigger a ListViewItem Behavior on Property Change?


I have a Custom Behavior attached to my ListViewItems that triggers on Loaded and DataContextChanged.

What this behavior does is traverse the VisualTree and by determining its direct parents, set the Visibility to Visible or Collapsed.

On initial load, and whenever I add / remove a ListViewItem to the ListView, it works properly.

However, some interactions only changes the Property of the ViewModel that is binded to the ListViewItem. What I want to do is, whenever this property changes, I want to still trigger the custom behavior that set Visibility for that ListViewItem only. Since the DataContext and Loaded doesn't trigger, my behavior doesn't happen.

Is there a way to do this?

This is my code for reference:

<DataTemplate x:Key="DataTemplate_Item">
        <Grid x:Name="Grid_TemplateRoot">
            <i:Interaction.Behaviors>
                <Dovetail_UI_Register_Controls_Behaviors:SetItemVisibilityBehavior />
            </i:Interaction.Behaviors>
            <TextBlock Text={Binding Path="ItemName"}
        </Grid>
</DataTemplate>

And the behavior:

public class OnLoadedOrDatacontextChangedBehavior<T> : OnLoadedBehavior<T> where T : FrameworkElement
{
    protected override void OnAttached()
    {
        base.OnAttached();

        TypedAssociatedObject.Loaded += ChangeVisibility();
        TypedAssociatedObject.AddDataContextChangedHandler(OnDataContextChanged);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        TypedAssociatedObject.Loaded -= ChangeVisibility();
        TypedAssociatedObject.RemoveDataContextChangedHandler(OnDataContextChanged);
    }

    protected virtual void OnDataContextChanged(object sender, EventArgs args)
    {
        ChangeVisibility();
    }

    private void ChangeVisibility()
    {
    //Change visibility here
    }
}

Thank you!


Solution

  • If you're not publishing this as a class library that needs to support data context coming in as any object whatsoever, then you can update your handler to listen for those property changes on your data context.

    For example:

    protected virtual void OnDataContextChanged(object sender, EventArgs args)
    {
        ChangeVisibility();
    
        // Listen for any further changes which effect visibility.
        INotifyPropertyChanged context = ((FrameworkElement)sender).DataContext as INotifyPropertyChanged;
        context.PropertyChanged += (s, e) => ChangeVisibility();
    }
    

    You could additionally extend this further, for example if your handler methods for data context changing use DependencyPropertyChangedEventHandler, then you could cleanup that PropertyChanged handler. Also, you can watch for only specific properties in your PropertyChanged handler. Expanded example:

    protected virtual void OnDataContextChanged(object sender, DependencyPropertyChangedEventHandler args)
    {
        ChangeVisibility();
        INotifyPropertyChanged context;
    
        // Cleanup any handler attached to a previous data context object.
        context = e.OldValue as INotifyPropertyChanged;
        if (context != null)
            context.PropertyChanged -= DataContext_PropertyChanged;
    
        // Listen for any further changes which effect visibility.
        context = e.NewValue as INotifyPropertyChanged;
        if (context != null)
            context.PropertyChanged += DataContext_PropertyChanged;
    }
    
    private void DataContext_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "MyTargetProperty")
        {
            ChangeVisibility();
        }
    }