Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BringToFront in WPF

I need to bring to front a custom control in WPF.

pseudoCode

OnMouseDown()
{
    if (this.parent != null)
        this.parent.BringToFront(this);
}

I know, I know that there are a ZIndex, but still don't understand how to replace the simple WinForm BringToFront to parent.SetZIndex(this, ?MaxZIndex(parent)? + 1) ?

Perhaps there is a better way of doing it in the such a cool thing like WPF?!..

like image 814
serhio Avatar asked Apr 14 '11 16:04

serhio


2 Answers

Here is an extension function that adds the method BringToFront functionality to all FrameworkElements that are contained in a Panel.

  public static class FrameworkElementExt
  {
    public static void BringToFront(this FrameworkElement element)
    {
      if (element == null) return;

      Panel parent = element.Parent as Panel;
      if (parent == null) return;

      var maxZ = parent.Children.OfType<UIElement>()
        .Where(x => x != element)
        .Select(x => Panel.GetZIndex(x))
        .Max();
      Panel.SetZIndex(element, maxZ + 1);
    }
  }
like image 180
Erik Rydgren Avatar answered Nov 11 '22 19:11

Erik Rydgren


Really the best that you can hope for is to make an element top-most with respect to any sibling elements in the same parent Panel (such as StackPanel, Grid, Canvas, etc). The z-order of the items in the panels is controlled by the Panel.ZIndex attached property.

For example:

<Grid>
    <Rectangle Fill="Blue" Width="50" Height="50" Panel.ZIndex="1" />
    <Rectangle Fill="Red" Width="50" Height="50" Panel.ZIndex="0" />
</Grid>

In this example, the blue rectangle would appear on top. This is explained a bit more in this blog post.

There is also an issue with changes to the Panel.ZIndex not being immediately reflected in the UI. There is a private method that allows refreshes the z-index, but since it's private it's not a good idea to use it. To work-around it, you'd have to remove and re-add the children.

You cannot however make any given element top-most. For example:

<Grid>
    <Grid>
        <Rectangle Fill="Blue" Width="50" Height="50" Panel.ZIndex="1" />
        <Rectangle Fill="Red" Width="50" Height="50" Panel.ZIndex="0" />
    </Grid>
    <Grid>
        <Rectangle Fill="Green" Width="50" Height="50" Panel.ZIndex="0" />
        <Rectangle Fill="Yellow" Width="50" Height="50" Panel.ZIndex="0" />
    </Grid>
</Grid>

In this case, the yellow rectangle will be displayed. Because the second inner grid is shown on top of the first inner grid and all it's content. You'd have to alter it like so to bring the blue rectangle to the top:

<Grid>
    <Grid Panel.ZIndex="1">
        <Rectangle Fill="Blue" Width="50" Height="50" Panel.ZIndex="1" />
        <Rectangle Fill="Red" Width="50" Height="50" Panel.ZIndex="0" />
    </Grid>
    <Grid Panel.ZIndex="0">
        <Rectangle Fill="Green" Width="50" Height="50" Panel.ZIndex="0" />
        <Rectangle Fill="Yellow" Width="50" Height="50" Panel.ZIndex="0" />
    </Grid>
</Grid>

When dealing with an ItemsControl, this question is relevant. Most other controls have a single child, but you'd still need to bring them forward to make any of it's children top-most.

Finally, Adorners are a good way to put things on top of everything, but they are not part of the main visual tree. So I'm not sure that would work in your case.

like image 30
CodeNaked Avatar answered Nov 11 '22 17:11

CodeNaked