Stewbob
Stewbob

Reputation: 16899

How can a button click on a UserControl execute a method in the containing Window?

I have a UserControl, we'll call it myUC, that is one among several UserControls in the main window (myWindow) of my WPF application. myUC contains a number of standard controls, one of them being a button, we'll call it myButton.

When I click myButton, I would like to execute myMethod(), which exists in the code-behind of myWindow.

The problem being that myUC doesn't have any idea that myWindow even exists, much less that myMethod exists.

How can I send the message: 'Hey, myWindow, wake up. myButton on myUc was just clicked; please run myMethod'?

Upvotes: 8

Views: 11485

Answers (6)

Alex B
Alex B

Reputation: 2814

You can create a command in the window and set the Command property of the button to the name of this command. Clicking the button will fire the command, without a need for a reference to the parent window.

This tutorial explains everything very clearly.

Upvotes: 8

JRBlack
JRBlack

Reputation: 31

The above is all well and good... but seems a bit convoluted to me...

My similar problem: I have a Window (MainWindow.xaml) with a UserControl (SurveyHeader.xaml) in it and inside that UserControl is another UserControl (GetSurveyByID.xaml). I have a private void in MainWindow.xaml that i want to run when a button (btnGetSurvey) is clicked in GetSurveyByID.xaml.

This one line solution worked quite well for me.

this.ucSurveyHeader.ucGetSurveyByID.btnGetSurvey.Click += new RoutedEventHandler(btnGetSurvey_Click);

Upvotes: 3

Stewbob
Stewbob

Reputation: 16899

What I actually ended up having to do in VB:

Create a new Public Class for my custom commands because it was undesirable to have my MainWindow class as Public:

Public Class Commands
  Public Shared myCmd As New RoutedCommand
End Class

Create the Execute and CanExecute methods that run the desired code. These two methods were created in the MainWindow code behind:

Class MainWindow

Private Sub myCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
  e.CanExecute = True
  e.Handled = True
End Sub

Private Sub myCmdExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
  //Do stuff here...
  e.Handled = True
End Sub

End Class

Create the Command binding in the MainWindow code-behind and add the two handler methods to the binding (this is the part that is quite different between C# and VB):

Class MainWindow
 Public Sub New()
      // This call is required by the designer.
      InitializeComponent()
      //Add any initialization after the InitializeComponent() call.
      //Create command bindings.
      Dim cb As New CommandBinding(Commands.myCmd)
      AddHandler cb.CanExecute, AddressOf myCmdCanExecute
      AddHandler cb.Executed, AddressOf myCmdExecuted
      Me.CommandBindings.Add(cb)
 End Sub
End Class

Add the new custom command to the button object on the UserControl. With a custom command, this did not seem to be possible in XAML, so I had to do it in code-behind. The Commands class needed to be Public so the commands were accessible in this UserControl:

Public Class myUserControl
  Public Sub New()
    //This call is required by the designer.
    InitializeComponent()
    // Add any initialization after the InitializeComponent() call.
    myButton.Command = Commands.myCmd
  End Sub
End Class

Upvotes: 5

littlekujo
littlekujo

Reputation: 286

Try something like this static helper method:

    public static T GetParentOfType<T>(DependencyObject currentObject)
        where T : DependencyObject
    {
        // Get the parent of the object in question
        DependencyObject parentObject = GetParentObject(currentObject);

        if (parentObject == null) 
            return null;

        // if the parent is the type then return
        T parent = parentObject as T;
        if (parent != null)
        {
            return parent;
        }
        else // if not the type, recursively continue up
        {
            return GetParentOfType<T>(parentObject);
        }
    }
    public static DependencyObject GetParent(DependencyObject currentObject)
    {
        if (currentObject == null)
            return null;
        // Convert the object in question to a content element
        ContentElement contentEl = currentObject as ContentElement;

        if (contentEl != null)
        {
            // try dependencyobject
            DependencyObject parent = System.Windows.ContentOperations.GetParent(contentEl);
            if (parent != null) 
                return parent;

            // Convert the contentEl to a FrameworkContentElement
            FrameworkContentElement frameworkEl = contentEl as FrameworkContentElement;
            // try frameworkcontentelement
            if (frameworkEl != null)
                return frameworkEl.Parent;
            else
                return null;
        }

        // couldn't get the content element, so return the parent of the visual element
        return System.Windows.Media.VisualTreeHelper.GetParent(currentObject);
    }

Execute it like so:

StaticHelpers.GetParentOfType<Window>(this);

Upvotes: 0

Dan J
Dan J

Reputation: 16708

I recommend learning about Routed Events and Routed Commands - this is the sort of thing they're meant to do.

Upvotes: 4

Maghoumi
Maghoumi

Reputation: 3380

Every control has a parent property which tells you that who actually "owns" this control. You can use that along side with type casting to access your myWindow's myMethod.
You could also use the sender argument of your event handler like this:

(sender as MyWindow).myMethod()

Upvotes: 0

Related Questions