WinUI 3 UserControl with ComboBox, DependencyProperty, and Enum - What's Going Wrong?

Daniel Parker 46 Reputation points
2022-05-23T19:34:09.363+00:00

Suppose we have

public enum MyEnum {None, First, Second}  

and in MainWindow.xaml.cs, we have

private IList<MyEnum> _myEnums = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().ToList();  
public IList<MyEnum> MyEnums => _myEnums;  
  
public MyEnum SelectedMyEnum {get;set;}   

and in MainWindow.xaml we have

<StackPanel>  
    <ComboBox ItemsSource="{x:Bind MyEnums}" SelectedItem="{x:Bind SelectedMyEnum, Mode=TwoWay}"/>  
</StackPanel>  

This works, as expected.

Now suppose we want to replace ComboBox with a user control, i.e.

<local:ExampleControl MyEnums="{x:Bind MyEnums}" SelectedMyEnum="{x:Bind SelectedMyEnum, Mode=TwoWay}"/>  

So, in ExampleControl.xaml we have

<StackPanel>  
    <ComboBox   
      ItemsSource="{x:Bind MyEnums}"  
      SelectedItem="{x:Bind SelectedMyEnum,Mode=TwoWay}">  
    </ComboBox>  
</StackPanel>  

and in ExampleControl.xaml.cs we have

// SelectedMyEnum  
  
public static readonly DependencyProperty SelectedMyEnumProperty =  
    DependencyProperty.Register(  
        "SelectedMyEnum",  
        typeof(MyEnum),  
        typeof(ExampleControl),  
        new PropertyMetadata(MyEnum.None));  // (1)  
  
public MyEnum SelectedMyEnum  
{  
    get { return (MyEnum)GetValue(SelectedMyEnumProperty); }  
    set { SetValue(SelectedMyEnumProperty, value); } // (2)  
}  
  
// MyEnums  
  
public static readonly DependencyProperty MyEnumsProperty =  
    DependencyProperty.Register(  
        "MyEnums",  
        typeof(IEnumerable<MyEnum>),  
        typeof(ExampleControl), null  
        );  
public IEnumerable<MyEnum> MyEnums  
{  
    get { return (IEnumerable<MyEnum>)GetValue(MyEnumsProperty); }  
    set { SetValue(MyEnumsProperty, value); }  
}  

But this doesn't work. On startup, the SelectedMyEnum setter (2) is called repeatedly with the value MyEnum.None until there is a stack overflow,

System.StackOverflowException  
  HResult=0x800703E9  
  Source=<Cannot evaluate the exception source>  
  StackTrace:  
<Cannot evaluate the exception stack trace>  

In (1), in place of

new PropertyMetadata(MyEnum.None)   

I tried

new PropertyMetadata(MyEnum.None, OnEnumChanged)  

with

private static void OnEnumChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)   
{  
    var control = (ExampleControl)obj;  
    MyEnum myVal = (MyEnum)args.NewValue; // (3)  
}  

but that made no difference, the function OnEnumChanged was called repeatedly, along with the setter, with myVal == MyEnum.None, until stack overflow.

I checked the WinUI Gallery for examples of dependency properties for enums, but couldn't find any, but there were plenty of examples for double, int, and bool, e.g. ItemHeight in WrapPanel, I think I'm doing this right.

I must be missing something, but can't see it. I've searched on ComboBox, DependencyProperty, Enum, and found some matches, e.g Enum as a DependencyProperty of a UserControl, but I didn't find them helpful. Any help is appreciated.

Note that if I change MyEnum to string in the control, and bind to an array of strings for the items source and a string for the selected item, everything works as expected, with no other changes. I can obviously work around this, but I would like to understand what's going on with enum and dependency properties, which is my preferred way to implement this.

Environment:

Microsoft Visual Studio Community 2022  
Version 17.1.0  
VisualStudio.17.Release/17.1.0+32210.238  
Microsoft .NET Framework  
Version 4.8.04161  
Windows development Windows App SDK
Developer technologies C#
0 comments No comments
{count} votes

Accepted answer
  1. Anonymous
    2022-05-24T02:49:18.227+00:00

    Hello,

    Welcome to Microsoft Q&A!

    This issue is related to the X:Bind markup extension. Please use Binding instead of x:bind in the UserControl and set the DataContext in the UserControl manually. Like this:

      <ComboBox   
           ItemsSource="{Binding MyEnums}"  
           SelectedItem="{Binding SelectedMyEnum,Mode=TwoWay}">  
            </ComboBox>  
    

    And:

            public UserControl1()  
            {  
                this.InitializeComponent();  
                this.DataContext = this;  
            }  
    

    Thank you.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.