microsoft-ui-xaml icon indicating copy to clipboard operation
microsoft-ui-xaml copied to clipboard

ThemeResource failed to assign to custom property

Open HO-COOH opened this issue 1 year ago • 2 comments

Describe the bug

I have a custom templated control, and I want to bind its property to a ThemeResource, and I got crash at xaml loading phase during app launch.

Steps to reproduce the bug

  1. Create a new WinUI3 C++ packaged project
  2. Create a new templated control, with this idl
    [default_interface]
    runtimeclass MyControl : Microsoft.UI.Xaml.Controls.Control
    {
        MyControl();
        String StringProperty;
    }
  1. In MainWindow.xaml, use this code
    <Grid>
        <Grid.Resources>
            <ResourceDictionary>
                <ResourceDictionary.ThemeDictionaries>
                    <ResourceDictionary x:Key="Light">
                        <x:String x:Key="MyStringResource">Light</x:String>
                    </ResourceDictionary>
                    <ResourceDictionary x:Key="Dark">
                        <x:String x:Key="MyStringResource">Dark</x:String>
                    </ResourceDictionary>
                </ResourceDictionary.ThemeDictionaries>
            </ResourceDictionary>
        </Grid.Resources>

        <local:MyControl StringProperty="{ThemeResource MyStringResource}" />
    </Grid>
  1. Build and run, see the crash

Expected behavior

No response

Screenshots

image

NuGet package version

WinUI 3 - Windows App SDK 1.6 Preview 1: 1.6.240807006-preview1

Windows version

Windows 11 (22H2): Build 22621

Additional context

Making it to a DependencyProperty does not solve this issue. I tried and this issue also happens in UWP too. Repro

HO-COOH avatar Aug 14 '24 03:08 HO-COOH

Making it to a DependencyProperty does not solve this issue.

The target property does need to be a DependencyProperty in order to style it with {ThemeResource}. What error did you hit when it was a DP?

codendone avatar Aug 16 '24 04:08 codendone

Making it to a DependencyProperty does not solve this issue.

The target property does need to be a DependencyProperty in order to style it with {ThemeResource}. What error did you hit when it was a DP?

Same exception. The getter function of that DependencyProperty does not even get called before the exception. image

HO-COOH avatar Aug 16 '24 06:08 HO-COOH

DependencyProperty requires some work to register, which I'm not sure if you did. See: https://learn.microsoft.com/windows/uwp/xaml-platform/custom-dependency-properties

codendone avatar Sep 12 '24 23:09 codendone

@codendone I figured out what's wrong here. I registered the DependencyProperty as a function local static, that is

    winrt::Microsoft::UI::Xaml::DependencyProperty MyControl::StringPropertyProperty()
    {
        static auto s_stringPropertyProperty =
            winrt::Microsoft::UI::Xaml::DependencyProperty::Register(
                L"StringProperty",
                winrt::xaml_typename<winrt::hstring>(),
                winrt::xaml_typename<class_type>(),
                nullptr
            );
        return s_stringPropertyProperty;
    }

This will cause the exception. But if I register it as a class static, it will work

    struct MyControl : MyControlT<MyControl>
    {
        MyControl() = default;

        winrt::hstring StringProperty() { return L""; }
        void StringProperty(winrt::hstring value){}

        static winrt::Windows::UI::Xaml::DependencyProperty StringPropertyProperty();
        static winrt::Windows::UI::Xaml::DependencyProperty s_stringPropertyProperty;
    };

Can you please confirm this is an expected behavior?

HO-COOH avatar Sep 19 '24 12:09 HO-COOH

The only change to get it working was pulling the variable out to a class static? I wouldn't expect that to make a difference, as long as the DependencyProperty::Register() call still happens before the property is needed and the right parameters are passed.

codendone avatar Sep 20 '24 04:09 codendone

The only change to get it working was pulling the variable out to a class static? I wouldn't expect that to make a difference, as long as the DependencyProperty::Register() call still happens before the property is needed and the right parameters are passed.

Please take a look at this. The first approach (I commented them) will work. The second will not.

HO-COOH avatar Sep 20 '24 14:09 HO-COOH