0

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.

1 Answer 1

1

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));
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

I was almost there! Good for me! :-) Anyway, I created all the classes, methods and handler like you did in your code but if I enable the event management on MyCustomTextBlock control by adding the StatusChanged="MessageBlock_StatusChanged" line in XAML and the corresponding method in code behind, I get a nasty unhandled exception that I'm not able to catch! It seems something crashes... I updated my code.
I forgot to mention that your code is almost correct but it lacks the coupling between the property CurrentStatus and the raise of the event StatusChanged. In my code I've added the call to OnStatusChanged() on the setter of the property, after having saved the old value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.