Home > other >  Attaching CLR event handler in xaml style in WPF
Attaching CLR event handler in xaml style in WPF

Time:11-05

Is there a way to attach a handler for the IsVisibleChanged event for a DataGridRow in a DataGridRow style definition? That is, is there a way to do something like the following:

<DataGrid.RowStyle>
    <Style TargetType="{x:Type DataGridRow}">
        <EventSetter Event="IsVisibleChanged" Handler="OnIsVisibleChanged"/>
    </Style>
</DataGrid.RowStyle>

The above won't work because EventSetter can only be applied to RoutedEvents and not regular CLR events, like IsVisibleChanged.

CodePudding user response:

We'll have to make an attached property and an event.

using System;
using System.Windows;

namespace CommonCore.AttachedEvents
{
    public static class UIElementHelper
    {
        public static readonly RoutedEvent IsVisibleChangedEvent = EventManager.RegisterRoutedEvent(
           "IsVisibleChanged", RoutingStrategy.Bubble, typeof(IsVisibleChangedEventHandler), typeof(UIElementHelper));

        public static void AddIsVisibleChangedHandler(DependencyObject dependencyObject, IsVisibleChangedEventHandler handler)
        {
            if (dependencyObject is not UIElement uiElement)
                return;

            uiElement.AddHandler(IsVisibleChangedEvent, handler);
        }

        private static void RaiseIsVisibleChangedEvent(object sender, DependencyPropertyChangedEventArgs e)
        {
            ((UIElement)sender).RaiseEvent(new IsVisibleChangedEventHandlerArgs(IsVisibleChangedEvent, (bool)e.NewValue, (bool)e.OldValue));
        }

        public static void RemoveIsVisibleChangedHandler(DependencyObject dependencyObject, IsVisibleChangedEventHandler handler)
        {
            if (dependencyObject is not UIElement uiElement)
                return;

            uiElement.RemoveHandler(IsVisibleChangedEvent, handler);
        }
        public static bool GetRaiseIsVisibleChanged(UIElement uiElement)
        {
            return (bool)uiElement.GetValue(RaiseIsVisibleChangedProperty);
        }

        public static void SetRaiseIsVisibleChanged(UIElement uiElement, bool value)
        {
            uiElement.SetValue(RaiseIsVisibleChangedProperty, value);
        }

        // Using a DependencyProperty as the backing store for RaiseIsVisibleChanged.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RaiseIsVisibleChangedProperty =
            DependencyProperty.RegisterAttached(
                "RaiseIsVisibleChanged",
                typeof(bool),
                typeof(UIElementHelper),
                new PropertyMetadata(false, OnRaiseIsVisibleChanged));

        private static void OnRaiseIsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is not UIElement uiElement)
            {
                throw new InvalidOperationException("Implemented only for UIElement.");
            }
            if ((bool)e.NewValue)
            {
                uiElement.IsVisibleChanged  = RaiseIsVisibleChangedEvent;
            }
            else
            {
                uiElement.IsVisibleChanged -= RaiseIsVisibleChangedEvent;
            }
        }
    }

    public class IsVisibleChangedEventHandlerArgs : RoutedEventArgs
    {
        public bool NewValue { get; }
        public bool OldValue { get; }
        public IsVisibleChangedEventHandlerArgs(RoutedEvent routedEvent, bool newValue, bool oldValue)
            : base(routedEvent)
        {
            NewValue = newValue;
            OldValue = oldValue;
        }
    }

    public delegate void IsVisibleChangedEventHandler(object sender, IsVisibleChangedEventHandlerArgs args);
}

Their use:

<StackPanel>
    <CheckBox x:Name="checkBox" Content="Видимость" IsChecked="False"/>
    <Border Background="AliceBlue" Padding="10" Margin="10">
        <Grid Height="20" Background="Aqua"
                Visibility="{Binding IsChecked, ElementName=checkBox, Converter={commcnvs:BooleanToVisibility}}">
            <FrameworkElement.Style>
                <Style TargetType="Grid">
                    <Setter Property="aev:UIElementHelper.RaiseIsVisibleChanged" Value="True"/>
                    <EventSetter Event="aev:UIElementHelper.IsVisibleChanged" Handler="OnIsVisibleChanged"/>
                </Style>
            </FrameworkElement.Style>
        </Grid>
    </Border>
</StackPanel>
private void OnIsVisibleChanged(object sender, IsVisibleChangedEventHandlerArgs args)
{
    MessageBox.Show($"OldValu = {args.OldValue}; NewValue = {args.NewValue};");
}
  • Related