views:

22

answers:

1

Hi folks,

A quick question regarding WPF: How can I select child elements within a Canvas/Grid?

For example, in the following XAML snippet:

    <Canvas Name="parentCanvas" Width="200" Height="200" Background="AliceBlue" MouseMove="parentCanvas_MouseMove" MouseLeave="parentCanvas_MouseLeave">
        <Border Name="border1" Margin="10" Height="100" Width="180" Background="Maroon" Canvas.Top="47" />
    </Canvas>

...how can I access border1 within parentCanvas_MouseMove and parentCanvas_MouseLeave methods?

Also, the way I access the canvas itself within the event handlers looks a bit hacky, I do something along the lines of:

    private void parentCanvas_MouseLeave(object sender, MouseEventArgs e)
    {
        Canvas c = (Canvas)sender;
        c.Background = Brushes.Blue;
    }

Is that the right way of accessing it or is there a more elegant way of doing this?

Many thanks,
Hamza.

+1  A: 

When you name a control with a Name attribute, it will generate a member in the class, so you should be able to access them by name:

private void parentCanvas_MouseLeave(object sender, MouseEventArgs e)
{
    Canvas c = parentCanvas;
    c.Background = Brushes.Blue;
    Border b = border1;
}

If the controls are part of a ControlTemplate or DataTemplate, then you would need to get a reference to the parent and look it up in the template. For example, if your XAML is part of a DataTemplate, you would have to do something like this:

private void parentCanvas_MouseLeave(object sender, MouseEventArgs e)
{
    Canvas c = (Canvas)sender;
    var contentPresenter = (ContentPresenter)c.TemplatedParent;
    var b = contentPresenter.ContentTemplate.FindName(
        "border1", contentPresenter);
}

In any case, casting the sender parameter to the appropriate type is perfectly reasonable, and it lets you make your event handlers more reusable.


If you don't have a name for your canvas, you can also use the Children property of the Canvas to iterate through its visual children:

private void parentCanvas_MouseLeave(object sender, MouseEventArgs e)
{
    Canvas c = (Canvas)sender;
    var firstChild = c.Children[0];
    var firstBorderChild = c.Children.OfType<Border>().FirstOrDefault();
}

Finally, if you have a visual that isn't a Panel, you can use VisualTreeHelper.GetChildrenCount and VisualTreeHelper.GetChild:

for (int childIndex = 0; 
     childIndex < VisualTreeHelper.GetChildrenCount(c); 
     childIndex++)
{
    var child = VisualTreeHelper.GetChild(c, childIndex);
}
Quartermeister
Thanks for the answer Quartermeister, that indeed works. A followup question of you don't mind... If I were to share this event handler with multiple Canvas elements or if the controls did not have any `Name` attributes how would I be able to achieve this? Thanks.
Hamza
@Hamza: You can use the Children property of the Canvas, or methods on VisualTreeHelper for other visuals. See my updated answer.
Quartermeister
I think I get it now. Thanks a lot for explaining it very clearly, cheers! :)
Hamza