Search code examples
wpfmodel-view-controllerfunction-call

WPF: can I call a method from the xaml file that is in another class?


I am working on a WPF project, and I implemented the MVC -Pattern for my application . So I have the MainWindow.xaml and MainWindow.xaml.cs as the View, and then a Controller and a Model class (first time that I did this). Everything works great so far. But here is what i don't like: When I have an event defined in the xaml file, lets take:

<Button Content="Save" Click="SaveButton_ClickEvent"/>

then whenever the Save Button is clicked, the function in the xaml.cs file is called:

private void SaveButton_ClickEvent(object sender, RoutedEventArgs e)
{
    Controller.Save();
}

I actually have a lot of those functions in the xaml.cs file, where there is a function that just calls another fuction in the controller, and it seems kind of wasteful to me. Isn't there a better way of doing this? I think I have read somewhere that if one implements the MVC-pattern in WPF, the xaml.cs file stays almost empty. So my question is:

Is there a way to call a Method in my controller (or any other) class from within the .xaml file wihtout putting a function in the xaml.cs file? (and if yes, how would i do that)

Or did I get it all wrong and this is all done differently?


Solution

  • If you don't want to implement the recommended MVVM design pattern and replace the event handler with a binding that binds the Command property of the Button to an ICommand property of a view model, you could for example implement an attached behaviour that handles the Click event and calls your controller:

    public static class Mvc
    {
        public static readonly DependencyProperty ClickProperty =
            DependencyProperty.RegisterAttached(
                "Click", typeof(string), typeof(Mvc),
                new PropertyMetadata(string.Empty, OnChanged));
    
        public static string GetClick(Button obj) =>
            (string)obj.GetValue(ClickProperty);
    
        public static void SetClick(Button obj, string value) =>
            obj.SetValue(ClickProperty, value);
    
        private static void OnChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!string.IsNullOrEmpty(e.NewValue as string)
                && obj is Button button)
            {
                button.Click += OnButtonClick;
            }
        }
    
        private static void OnButtonClick(object sender, RoutedEventArgs e)
        {
            Button button = (Button)sender;
            object controller = button.DataContext;
            if (controller != null)
            {
                string methodName = GetClick(button);
                if (!string.IsNullOrEmpty(methodName))
                {
                    controller.GetType()
                        .GetMethod(methodName)?
                        .Invoke(controller, null);
                }
            }
        }
    }
    

    XAML:

    <Button Content="Save" local:Mvc.Click="Save"/>
    

    Code-behind:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new Controller();
        }
    }
    

    Controller:

    public class Controller
    {
        public void Save()
        { 
            //save... 
        }
    }