Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bind an Action to a property of a UserControl in XAML

I have a user control which has a button and a dependency property for the action the button is to execute. The page which contains the control sets the action in XAML.

MyUserControl.cs

A Button, and dependency property ButtonAction, of type Action. When the button is clicked it executes the ButtonAction.

MainPage.xaml.cs

Action Action1

Action Action2

MainPage.xaml

Present an instance of MyUserControl, with ButtonAction=Action1

The problem: The ButtonAction property is not assigned from the XAML

MyUserControl.cs

    public sealed partial class MyUserControl : UserControl
{

    public Action ButtonAction {
        get { return (Action)GetValue(ButtonActionProperty); }
        set { SetValue(ButtonActionProperty, value); }
    }

    public static readonly DependencyProperty ButtonActionProperty =
        DependencyProperty.Register("ButtonAction", typeof(Action), typeof(MyUserControl), new PropertyMetadata(null,ButtonAction_PropertyChanged));

    private static void ButtonAction_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        Debug.WriteLine("ButtonAction_PropertyChanged");
        // Is not called!
    }


    public MyUserControl() {
        this.InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e) {
        if (ButtonAction != null) {
            // Never reaches here!
            ButtonAction();
        }
    }
}

MyUserControl.xaml

    <Grid>
    <Button Click="Button_Click">Do The Attached Action!</Button>

</Grid>

MainPage.xaml.cs

    Action Action1 = (
        () => { Debug.WriteLine("Action1 called"); });

    Action Action2 = (() => { Debug.WriteLine("Action2 called"); });

MainPage.xaml

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <local:MyUserControl x:Name="myUserControl" ButtonAction="{Binding Action1}"/>
</Grid>

It does work if in the code-behind for MainPage (MainPage.xaml.cs) I assign the action in the Loaded event.

        private void Page_Loaded(object sender, RoutedEventArgs e) {
        this.myUserControl.ButtonAction = Action1;
    }

In this case the PropertyChanged callback in the user control is also called. (This handler is provided only for diagnostic purposes. I can't see how it can be used to support the property in practice).

like image 343
Stephen Hosking Avatar asked Sep 28 '22 16:09

Stephen Hosking


1 Answers

The issue is in your data binding. The Action1 in ButtonAction="{Binding Action1}" should be a public property while you defined it as a private variable.

Also, you cannot just declare a normal property directly in the code behind like that. You will need either a dependency property, or more commonly, a public property inside a viewmodel which implements INotifyPropertyChanged.

If we go with the second approach, we will need to create a viewmodel class like the following with an Action1 property. Note the OnPropertyChanged stuff is just the standard way of implementing INotifyPropertyChanged.

public class ViewModel : INotifyPropertyChanged
{
    private Action _action1;
    public Action Action1
    {
        get { return _action1; }
        set
        {
            _action1 = value;
            OnPropertyChanged("Action1");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

And then you just need to assign this to the DataContext of your main page.

    public MainPage()
    {
        this.InitializeComponent();

        var vm = new ViewModel();
        vm.Action1 = (() =>
        {
            Debug.WriteLine("Action1 called");
        });

        this.DataContext = vm;
    }

With these two changes, your ButtonAction callback should be firing now. :)

like image 188
Justin XL Avatar answered Oct 07 '22 21:10

Justin XL