Search code examples
c#xamluser-interfacewinui-3

Subscribe XAML event to a event handler in a different C# file


The problem occour when I try to assign (subscribe) a ToggleButton to a event handler in a different file (for example: XAML is named PrimaryPage.xaml and a C# file is named different like EventHandler.cs, not PrimaryPage.xaml) in the same solution as I wanted to seperate things up.

Here's what I meant:

  1. Create a ToggleButton in XAML:
<ToggleButton
    x:Name="PumpBTN"
    IsThreeState="False"
    Checked="PumpBTN_C"
    Unchecked="PumpBTN_U"
    Content="Pump" />
  1. Create a event subscription in PrimaryPage.xaml.cs
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml;
using WinUI.XXXX.EventHandler; // Include EventHandler

namespace WinUI.XXXX.Views;

public partial class PrimaryPage : Page // Unsealed
{
    public PrimaryViewModel ViewModel
    {
        get;
    }

    public PrimaryPage()
    {
        ViewModel = App.GetService<PrimaryViewModel>();
        InitializeComponent();

        Event event = new Event();
        PumpBTN.Checked += event.PumpBTN_C; // Pump-checked subscription
        PumpBTN.Unchecked += event.PumpBTN_U; // Pump-unchecked subscription
    }
}
  1. Create a event handler in EventHandler.cs:
using System.IO.Ports;
using WinUI.XXXX.Views;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml;

namespace WinUI.XXXX.EventHandler;

public partial class Event
{
    public readonly SerialPort serial_port = new();

    public Serial()
    {
        Main();
    }

    public string Main()
    {
        // Serial port stuffs
        return 'strings' // Just for an example
    }

    public void PumpBTN_C(object sender, RoutedEventArgs e) // Event handler
    {
        // Code
    }
    public void PumpBTN_U(object sender, RoutedEventArgs e) // Event handler
    {
        // Code
    }

I think I alredy do everything that I needed to, I aleady type using followed by the corresponding namespaces, I already subscribe and create a event handler as needed.

I'm just started developing GUI with C# and XAML in WinUI3 a few weeks ago, and I've no experience in writing in OOP languages before, So I have no idea what should I do next. Any ideas?


Solution

  • The typical way is to use MVVM to bind the IsChecked property of the View to a property in the ViewModel:

    view

    <CheckBox IsChecked="{Binding MyIsCheckedProperty}">
    Pump
    </CheckBox>
    

    ViewModel

    public class MyViewModel : INotifyPropertyChanged
    {
        private bool myIsCheckedValue;
        public bool MyIsCheckedProperty
        {
            get => myIsCheckedValue;
            set
            {
                if (value != myIsCheckedValue)
                {
                    myIsCheckedValue = value;                
                    OnPropertyChanged();
                    // Do other stuff
                }
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    You can add an event to your MyViewModel, and raise it at the // Do other stuff point to inform other components. Or just make a call to some method to inform it that the checkbox has changed. The idea with the pattern is that the "ViewModel" classes should interact with and update a "Model" that represents the overall state of your application.

    Note that you will need to ensure the DataContext object of your view is set to the viewModel object. For example by using a little bit of code behind:

    public partial class MyMainWindow : Window
    {
            private MainWindowViewModel mwmv;
            public MyMainWindow ()
            {
                InitializeComponent();
                DataContext = new MyViewModel;
            }
    }
    

    This is typically only done for the root window, and use bindings to associate any other views with viewModels. This needs to be done since all bindings are relative to the DataContextObject.