Search code examples
c#xamlwinui-3winui

Programmatically retrieve DependencyProperty set by Setters within a Style


Working on a WinUI3 App. I have defined a style in my XMAL to use when creating controls programmatically

  <Canvas.Resources>
      <Style TargetType="Button" x:Key="NodeStyle">
          <Setter Property = "Height" Value="90"/>
          <Setter Property = "Width" Value="180"/>
          <Setter Property = "CornerRadius" Value="100"/>
          <Setter Property = "Background" Value="Black"/>
      </Style>
  </Canvas.Resources>

later the resource is used

Button button = new()
{
    Content = node.Key,
    Style = this.visualizationCanvas.Resources["NodeStyle"] as Style,
    ClickMode = ClickMode.Release
};

And it works. But, I can't access the properties values set by the setters within the style, within the code, in a controlled manner. I read the following documents

https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.setter.target?view=windows-app-sdk-1.5

https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.dependencyproperty?view=windows-app-sdk-1.5

https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/dependency-properties-overview

I have learned that it should be possible as it states

If you're accessing a Setter instance using code, you cannot change the value of any property of a Setter instance if the value of the IsSealed property on a parent Style is true.

Tried:

Style s = this.visualizationCanvas.Resources["NodeStyle"] as Style;
_log.Info(s.TargetType.FullName);
=================
output: Microsoft.UI.Xaml.Controls.Button

Then:

foreach (Setter setter in s.Setters)
{
     setter.Property.GetMetadata(typeof(Button)).DefaultValue;
     //NaN
     setter.Value;
     // gives me the values, but I don't know which property the value belongs to
     setter.GetValue(Button.HeightProperty);
     // System.Runtime.InteropServices.COMException (0x8000FFFF)
     setter.GetValue(setter.Property);
     // System.Runtime.InteropServices.COMException (0x8000FFFF)
}

Solution

  • You can get the property values for each setter like this:

    <Page.Resources>
        <Style
            x:Key="NodeStyle"
            TargetType="Button">
            <Setter Property="Height" Value="90" />
            <Setter Property="Width" Value="180" />
            <Setter Property="CornerRadius" Value="100" />
            <Setter Property="Background" Value="Black" />
        </Style>
    </Page.Resources>
    
    if (Resources.TryGetValue("NodeStyle", out var resource) is not true ||
        resource is not Style nodeStyle)
    {
        return;
    }
            
    var styledButton = new Button
    {
        Style = nodeStyle
    };
    
    var properties = nodeStyle.TargetType
        .GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
        .ToList();
    
    foreach (Setter setter in nodeStyle.Setters.Cast<Setter>())
    {
        if (properties
            .Where(propertyInfo => (propertyInfo.GetValue(styledButton) as DependencyProperty) == setter.Property)
            .FirstOrDefault() is not { } targetProperty ||
            styledButton.GetValue(setter.Property) is not { } value)
        {
            continue;
        }
    
        System.Diagnostics.Debug.WriteLine($"{targetProperty.Name} Value: {value}");
    }
    

    Output

    HeightProperty Value: 90
    WidthProperty Value: 180
    CornerRadiusProperty Value: 100,100,100,100
    BackgroundProperty Value: Microsoft.UI.Xaml.Media.SolidColorBrush