Cheshire Cat
Cheshire Cat

Reputation: 1961

Add custom event to a CustomControl in WinUI 3

I've created a CustomControl in WinUI 3 with some custom properties (DependencyProperty).

Now I also want to add a custom Event (not sure if it has to be a RoutedEvent?) that has to trigger whenever the CurrentStatus property of my control changes. I'd like to be able to manage it just like a simple Button's Click, so something like:

public sealed class MyCustomTextBlock : Control
{
    public enum Status
    { 
        Waiting,
        Busy,
    }
    
    private Status currentStatus;
    public Status CurrentStatus
    {
        get { return currentStatus; }
    }

    public MyCustomTextBlock()
    {
        this.DefaultStyleKey = typeof(MyCustomTextBlock);
        currentStatus = Status.Waiting;
    }

    // The currentStatus variable is set on some custom methods inside the implementation of the control.
}
<local:MyCustomTextBlock x:Name="MessageBlock"
                    Message="{TemplateBinding Message}"
                    DisplayMode="{TemplateBinding DisplayMode}"
                    StatusChanged="MessageBlock_StatusChanged">
</local:MyCustomTextBlock>
private void MessageBlock_StatusChanged(object sender, RoutedEventArgs e)
{
    // Check the CurrentStatus property
    if (MyCustomTextBlock.CurrentStatus == MyCustomTextBlock.Status.Waiting)
        // Do something...
}

How should I declare the Event and the EventHandler on the code behind of the MyCustomTextBlock control?

I'm new to WinUI 3 and XAML and looking for "winui 3 custom event" I found this but it's related to WPF and so I cannot find the EventManager.RegisterRoutedEvent() which is cited in the WinUI Microsoft.UI.Xaml library.

Update 2022-09-13

I followed @AndrewKeepCoding suggestion and now this is my code on the MainWindow page where I placed MyCustomTextBlock control.

<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
    <local:MyCustomTextBlock x:Name="MessageBlock"
                             Message="{TemplateBinding Message}"
                             DisplayMode="{TemplateBinding DisplayMode}"
                             StatusChanged="MessageBlock_StatusChanged">
    </local:MyCustomTextBlock>
</StackPanel>
public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();
    }
    
    private void MessageBlock_StatusChanged(object sender, StatusChangedEventArgs e)
    {
        MyCustomTextBlock control = (MyCustomTextBlock)sender;
        if (e.NewValue == Status.Waiting)
        {
            // Do something...
        }
        else if (e.NewValue == Status.Busy)
        {
            // Do something else...
        }
    }
}

There's no warnings or compilation errors, but now every time I start the application in debug mode it crashes without trapping any specific Exception with the following error description:

Win32 unmanaged Exception in [10584] WinUI3TestApp.exe

win32 unmanaged exception

The MainWindow.InitializeComponent() method is called without any issue but after passing the last line of the the public MyCustomTextBlock() constructor method, I don't know what happens but something makes the app crash.

Update 2022-09-14

It seems to be related to this Microsoft.UI.Xaml Issue. There's probably something wrong somewhere inside an async method but it's not possible to catch the Exception due to this bug.

Workaround

My problem is related to the Event handler method added in XAML (StatusChanged="MessageBlock_StatusChanged").

After removing it and replacing with the event handler declaration (MessageBlock.StatusChanged += MessageBlock_StatusChanged;) in code behind on MyCustomTextBlock.Loaded() method, everything worked correctly.

Upvotes: 0

Views: 1688

Answers (1)

Andrew KeepCoding
Andrew KeepCoding

Reputation: 13666

You can implement a custom EventHandler like this.

public class StatusChangedEventArgs : EventArgs
{
    public Status OldValue { get; }
    public Status NewValue { get; }

    public StatusChangedEventArgs(Status oldValue, Status newValue)
    {
        OldValue = oldValue;
        NewValue = newValue;
    }
}

public sealed class MyCustomTextBlock : Control
{
    public enum Status
    { 
        Waiting,
        Busy,
    }
    
    private Status currentStatus;
    public Status CurrentStatus
    {
        get { return currentStatus; }
    }

    public MyCustomTextBlock()
    {
        this.DefaultStyleKey = typeof(MyCustomTextBlock);
        currentStatus = Status.Waiting;
    }

    public event EventHandler<StatusChangedEventArgs>? StatusChanged;

    protected void OnStatusChanged(Status oldValue, Status newValue)
    {
        StatusChanged?.Invoke(this, new StatusChangedEventArgs(oldValue, newValue));
    }
}

Upvotes: 1

Related Questions