Call parent's method from child element

833 Views Asked by At

in the following structure

<Border ...>
    <ItemsControl>
        <ItemsControl.Template>
            <DataTemplate>
                <ACustomElement>
                    <Border MouseLeftButtonDown="method1">
                </ACustomElement>
            </DataTemplate>
        </ItemsControl.Template>
    </ItemsControl>
</Border>

I want to call a public method in the ACustomElement class from inside method1().

What I tried so far in method1():

var cr = ((Border)sender).Parent;
cr.method2();

method2 is a public method in my ACustomElement class. But it doesn't seem to recognize the method.

I'm getting the following error:

'DependencyObject' does not contain a definition for 'method2' and no extension method 'method2' accepting a first argument of type 'DependencyObject' could be found (are you missing a using directive or an assembly reference?)

Any suggestions on how to solve this problem? Certainly I'm just missing a cast or something else...

Edit: The following style will always be applied to ACustomElement:

<Style TargetType="{x:Type c:ACustomElement}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type c:ACustomElement}">
                <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
2

There are 2 best solutions below

7
On

You have to cast cr to ACustomElement type

var cr = (ACustomElement)((Border)sender).Parent;
cr.method2();

otherwise, your cr variable refers to DependencyObject type as you see in the exception.

if you are not sure about hierarchy use this method to find a parent of specific type.

private T FindParent<T>(DependencyObject child) where T : DependencyObject {
    var parent = VisualTreeHelper.GetParent(child) as T;
    if (parent != null)
        return parent;
    return FindParent<T>(parent);
}

// usage
private void method1(object sender, MouseButtonEventArgs e)
{
    var cr = FindParent<ACustomElement>((Border)sender);
}

Also, DateTemplate can be child of ItemsControl.ItemTemplate, but not of ItemsControl.Template (which expects ControlTemplate)

 <ItemsControl.ItemTemplate>
      <DataTemplate>
          <local:ACustomElement>
              <Border MouseLeftButtonDown="method1" />
          </local:ACustomElement>
      </DataTemplate>
 </ItemsControl.ItemTemplate>

Update

And as I pointed in the comment to the question, add an event handler to your type directly or bind a command. Why do you make it so complicated?

3
On

Try to bind the Tag property of the Border to the custom control:

<ACustomElement>
    <Border MouseLeftButtonDown="method1" Tag="{Binding RelativeSource={RelativeSource AncestorType=ACustomElement}}">
</ACustomElement>

...and cast the Tag property in the event handler:

var cr = sender as Border;
var ctrl = cr.Tag as ACustomElement;
ctrl.method2();