Reputation: 21
In WPF, I have a DataGrid, and the DataGrid has an attached property (LoadingProperty). When LoadingProperty value is True, I set the DataGrid background with VisualBrush, why does the LoadingCircle UserControl Loaded event not Trigger?
DataGrid Style
<Style TargetType="{x:Type DataGrid}">
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="CanUserAddRows" Value="False" />
<Setter Property="HeadersVisibility" Value="Column" />
<Setter Property="Foreground" Value="{StaticResource ForegroundLightBrush}" />
<Setter Property="Background" Value="{StaticResource BackgroundLoadingBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource BackgroundDarkBrush}" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="HorizontalGridLinesBrush" Value="{StaticResource BackgroundDarkBrush}" />
<Setter Property="VerticalGridLinesBrush" Value="{StaticResource BackgroundDarkBrush}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="EnableRowVirtualization" Value="True" />
<Setter Property="EnableColumnVirtualization" Value="True" />
<Setter Property="materialDesign:DataGridAssist.CellPadding" Value="16,8,16,8" />
<Style.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Foreground="{StaticResource ForegroundLightBrush}" Text="No record found" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="local:LoadingProperty.Value" Value="True">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<local:LoadingCircle Width="50" Height="50" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
LoadingProperty Code
public class LoadingProperty : BaseAttachedProperty<LoadingProperty, bool>
{
}
BaseAttachedProperty Code
using System;
using System.Windows;
namespace TransportsSys.App
{
/// <summary>
/// A base attached property to replace the vanilla WPF attached property
/// </summary>
/// <typeparam name="Parent">The parent class to be the attached property</typeparam>
/// <typeparam name="Property">The type of this attached property</typeparam>
public abstract class BaseAttachedProperty<Parent, Property>
where Parent : new()
{
#region Public Events
/// <summary>
/// Fired when the value changes
/// </summary>
public event Action<DependencyObject, DependencyPropertyChangedEventArgs> ValueChanged = (sender, e) => { };
/// <summary>
/// Fired when the value changes, even when the value is the same
/// </summary>
public event Action<DependencyObject, object> ValueUpdated = (sender, value) => { };
#endregion
#region Public Properties
/// <summary>
/// A singleton instance of our parent class
/// </summary>
public static Parent Instance { get; private set; } = new Parent();
#endregion
#region Attached Property Definitions
/// <summary>
/// The attached property for this class
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached(
"Value",
typeof(Property),
typeof(BaseAttachedProperty<Parent, Property>),
new UIPropertyMetadata(
default(Property),
new PropertyChangedCallback(OnValuePropertyChanged),
new CoerceValueCallback(OnValuePropertyUpdated)
));
/// <summary>
/// The callback event when the <see cref="ValueProperty"/> is changed
/// </summary>
/// <param name="d">The UI element that had it's property changed</param>
/// <param name="e">The arguments for the event</param>
private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Call the parent function
(Instance as BaseAttachedProperty<Parent, Property>)?.OnValueChanged(d, e);
// Call event listeners
(Instance as BaseAttachedProperty<Parent, Property>)?.ValueChanged(d, e);
}
/// <summary>
/// The callback event when the <see cref="ValueProperty"/> is changed, even if it is the same value
/// </summary>
/// <param name="d">The UI element that had it's property changed</param>
/// <param name="e">The arguments for the event</param>
private static object OnValuePropertyUpdated(DependencyObject d, object value)
{
// Call the parent function
(Instance as BaseAttachedProperty<Parent, Property>)?.OnValueUpdated(d, value);
// Call event listeners
(Instance as BaseAttachedProperty<Parent, Property>)?.ValueUpdated(d, value);
// Return the value
return value;
}
/// <summary>
/// Gets the attached property
/// </summary>
/// <param name="d">The element to get the property from</param>
/// <returns></returns>
public static Property GetValue(DependencyObject d) => (Property)d.GetValue(ValueProperty);
/// <summary>
/// Sets the attached property
/// </summary>
/// <param name="d">The element to get the property from</param>
/// <param name="value">The value to set the property to</param>
public static void SetValue(DependencyObject d, Property value) => d.SetValue(ValueProperty, value);
#endregion
#region Event Methods
/// <summary>
/// The method that is called when any attached property of this type is changed
/// </summary>
/// <param name="sender">The UI element that this property was changed for</param>
/// <param name="e">The arguments for this event</param>
public virtual void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { }
/// <summary>
/// The method that is called when any attached property of this type is changed, even if the value is the same
/// </summary>
/// <param name="sender">The UI element that this property was changed for</param>
/// <param name="e">The arguments for this event</param>
public virtual void OnValueUpdated(DependencyObject sender, object value) { }
#endregion
}
}
LoadingCircle UserControl Code
// LoadingCircle.xmal
<UserControl
x:Class="TransportsSys.App.LoadingCircle"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<TextBlock FontSize="{StaticResource FontSizeXXXXLarge}" Foreground="{StaticResource ForegroundLightBrush}" Style="{StaticResource DataGridSpinningText}" />
</Grid>
</UserControl>
// LoadingCircle.xmal.cs
using System.Windows.Controls;
namespace TransportsSys.App
{
/// <summary>
/// LoadingCircle.xaml 的交互逻辑
/// </summary>
public partial class LoadingCircle : UserControl
{
public LoadingCircle()
{
InitializeComponent();
Loaded += (s, e) =>
{
// can not trigger
};
}
}
}
I wonder why that is. When I use LoadingCircle alone in a page, its Loaded event fires normally.
Upvotes: 0
Views: 214
Reputation: 8359
This looks like a WPF bug.
A workaround is to put the VisualBrush
in resources and use it in the trigger:
<UserControl.Resources>
<VisualBrush x:Key="Brush" Stretch="None">
<VisualBrush.Visual>
<local:LoadingCircle Width="50" Height="50" />
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
...
<Trigger Property="local:LoadingProperty.Value" Value="True">
<Setter Property="Background" Value="{StaticResource Brush}" />
</Trigger>
Upvotes: 0