MatthiasG
MatthiasG

Reputation: 4532

Get the shape of a control as Geometry

Is there a way to get the shape of a control and convert it into a Geometry object?

I have the following situation: In a WPF application a popup will be shown. This popup is no windows, it's a control, that will get visible. The rest of the application will get darker as a gray layer is above the application.

Now the problem is that this gray layer is also above the popup itself which is caused by the design of the application plus the element that was clicked and opened the popup should also not be hidden by the layer. I decided to attach a clipping geometry to the gray layer which is fine, but I have to detect all forms and paths that I don't want to hide by myself.

So to get back to my question: Is there a way to get the shape of a control and convert it into a Geometry object? E.g. I found ways to get the VisualBrush of a control but also cannot convert that - or just do not see how it is possible.

Upvotes: 3

Views: 1692

Answers (1)

SvenG
SvenG

Reputation: 5195

you could do it this way: Remove the button from the visual tree and place it on the adorner. When the adorner closes attach it to the original parent again. I think this is much more flexible than clipping any geometries and makes it much more flexible (you could e.g. place complex content like usercontrols on the adorner)

The following example uses a Panel as container for the button.

The Xaml (Window):

<Grid Margin="50" x:Name="myGrid" Background="LightBlue">
<Button x:Name="myButton" Width="80" Height="30" Click="myButton_Click">Show popup</Button>

Code Behind: private FrameworkElementAdorner _adorner; private Panel _originalParent;

private void myButton_Click(object sender, RoutedEventArgs e)
{
  if (_adorner == null)
  {
    _adorner = new FrameworkElementAdorner(myGrid);
  }

  // remove the button from the parent panel and attach it to the adorner
  // otherwise remove from adorner and attach to original parent again
  if (_adorner.IsVisible)
  {
    AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(myGrid);
    adornerLayer.Remove(_adorner);

    Panel parent = VisualTreeHelper.GetParent(myButton) as Panel;
    if (parent != null)
    {
      parent.Children.Remove(myButton);
    }
    _originalParent.Children.Add(myButton);
  }
  else
  {
    _originalParent = VisualTreeHelper.GetParent(myButton) as Panel;
    if (_originalParent != null)
    {
      _originalParent.Children.Remove(myButton);
    }

    // Create the Adorner with the original button in it
    _adorner.Child = CreateAdornerContent(myButton);

    AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(myGrid);
    adornerLayer.Add(_adorner);
  }
}

/// <summary>
/// Creates some dummy content for the adorner
/// </summary>
private FrameworkElement CreateAdornerContent(Button myButton)
{
  Grid g = new Grid();
  g.Background = new SolidColorBrush(Colors.Yellow);

  TextBlock tb = new TextBlock();
  tb.Text = "I am the Adorner";

  g.Children.Add(tb);
  g.Children.Add(myButton);
  return g;
}

And here the simple adorner which just displays a frameworkElement: class FrameworkElementAdorner : Adorner { private FrameworkElement _child;

public FrameworkElementAdorner(UIElement adornedElement)
  : base(adornedElement)
{
}

protected override int VisualChildrenCount
{
  get
  {
    return 1;
  }
}

protected override Visual GetVisualChild(int index)
{
  if (index != 0) throw new ArgumentOutOfRangeException();
  return _child;
}

public FrameworkElement Child
{
  get
  {
    return _child;
  }
  set
  {
    if (_child != null)
    {
      RemoveVisualChild(_child);
    }
    _child = value;
    if (_child != null)
    {
      AddVisualChild(_child);
    }
  }
}

protected override Size ArrangeOverride(Size finalSize)
{
  _child.Arrange(new Rect(new Point(0, 0), finalSize));
  return new Size(_child.ActualWidth, _child.ActualHeight);
}

}

I can also upload the full sln if you like. Is this possible here somehow?

Upvotes: 2

Related Questions