5

Can anyone tell me how to center a polygon object within a given row/column of a Grid control?

The example that I have tried is taken from msdn.

<Grid x:Name="LayoutRoot" >
  <Polygon Points="300,200 400,125 400,275 300,200" 
           Stroke="Purple" 
           StrokeThickness="2"
           HorizontalAlignment="Center"
           VerticalAlignment="Center" >
    <Polygon.Fill>
        <SolidColorBrush Color="Blue" Opacity="0.4" />
    </Polygon.Fill>
 </Polygon>

Cheers,

Xam

Xam
  • 271
  • 3
  • 12

3 Answers3

3

Add the attributes :-

 HorizontalAlignment="Center" VerticalAlignment="Center"

to the Polygon.

AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306
0

Although a height and width are implied by the bounds of the polygon, it defaults to the size of the container.

If you just set

HorizontalAlignment="Center" VerticalAlignment="Center"

it will position the polygon's top-left in the centre.

You also have to explicitly set the height and width of the polygon to centre it and retain its bounds

Sample polygon Xaml with dimensions added:

<Grid x:Name="LayoutRoot">
   <Path Data="M0.5,41.5 L201,0.5 L302,115 L157.25,157 z" Fill="#FFF4F4F5" Stroke="Black" UseLayoutRounding="False" HorizontalAlignment="Center" VerticalAlignment="Center" Width="302.5" Height="157.5"/>
</Grid>
iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
  • @HiTechMagic: How are you defining your container and polygon? I don't see it stretching with a simple grid and 4-point polygon in a quick example I threw together after seeing your post. – Wonko the Sane Sep 21 '10 at 13:03
  • @HiTechMagic: I have the same issue as Wonko. Despite following your direction, I can't get a simple polygon (in my case a triangle) to center within a Grid control. – Xam Sep 21 '10 at 13:09
  • @Wonko & Xam: My mistake. I had fill set on the polygon by default. Answer corrected. The basic solution is the same though. Make sure you set your size to the bounds of the Polygon. Cheers – iCollect.it Ltd Sep 21 '10 at 13:32
  • @HiTechMagic: Thanks again. Can you please provide a xaml snippet which demonstrates how you achieved this result? – Xam Sep 21 '10 at 22:06
  • @Xam: Xaml Added above. It would be more useful to see and correct your Xaml. Mine was a simple poly authored with the pen tool in Blend. – iCollect.it Ltd Sep 22 '10 at 10:31
  • @HiTechMagic: Thanks. I have added the sample above. – Xam Sep 22 '10 at 11:19
0

Maybe this answer applies here too.

It uses a CenterConverter

public class CenterConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[0] == DependencyProperty.UnsetValue || values[1] == DependencyProperty.UnsetValue)
        {
            return DependencyProperty.UnsetValue;
        }

        double width = (double) values[0];
        double height = (double)values[1];

        return new Thickness(-width/2, -height/2, 0, 0);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

And binds it in XAML like this

<Canvas>
    <TextBlock x:Name="txt" Canvas.Left="40" Canvas.Top="40" TextAlignment="Center" Text="MMMMMM">
        <TextBlock.Margin>
            <MultiBinding Converter="{StaticResource centerConverter}">
                    <Binding ElementName="txt" Path="ActualWidth"/>
                    <Binding ElementName="txt" Path="ActualHeight"/>
            </MultiBinding>
        </TextBlock.Margin>
    </TextBlock>
    <Rectangle Canvas.Left="39" Canvas.Top="39" Width="2" Height="2" Fill="Red"/>
</Canvas>

To be able to use this in C# too and not only in XAML you need this class

public class Mover : DependencyObject
{
    public static readonly DependencyProperty MoveToMiddleProperty =
        DependencyProperty.RegisterAttached("MoveToMiddle", typeof (bool), typeof (Mover),
        new PropertyMetadata(false, PropertyChangedCallback));

    public static void SetMoveToMiddle(UIElement element, bool value)
    {
        element.SetValue(MoveToMiddleProperty, value);
    }

    public static bool GetMoveToMiddle(UIElement element)
    {
        return (bool) element.GetValue(MoveToMiddleProperty);
    }

    private static void PropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement element = sender as FrameworkElement;
        if (element == null)
        {
            return;
        }

        if ((bool)e.NewValue)
        {
            MultiBinding multiBinding = new MultiBinding();
            multiBinding.Converter = new CenterConverter();
            multiBinding.Bindings.Add(new Binding("ActualWidth") {Source = element});
            multiBinding.Bindings.Add(new Binding("ActualHeight") {Source = element});
            element.SetBinding(FrameworkElement.MarginProperty, multiBinding);
        }
        else
        {
            element.ClearValue(FrameworkElement.MarginProperty);
        }
    }

}

Use it in XAML like so

<Canvas>
    <TextBlock Canvas.Left="40" Canvas.Top="40" TextAlignment="Center" Text="MMMMMM"
          local:Mover.MoveToMiddle="True"/>
</Canvas>

Or in C# like so

Mover.SetMoveToMiddle(UIElement, true);

Alternatively you can manipulate the RenderTransform

An alternative would be to bind to RenderTransform instead of Margin. In this case, the converter would return

return new TranslateTransform(-width / 2, -height / 2);

and the attached property's callback method would contain these lines:

if ((bool)e.NewValue)
{
    ...
    element.SetBinding(UIElement.RenderTransformProperty, multiBinding);
}
else
{
    element.ClearValue(UIElement.RenderTransformProperty);
}

This alternative has the advantage that the effect of the attached property is visible in the Visual Studio designer (which is not the case when setting the Margin property).

In XAML this would look like this:

<Canvas>
    <TextBlock x:Name="txt" Canvas.Left="40" Canvas.Top="40" TextAlignment="Center" Text="MMMMMM">
        <TextBlock.RenderTransform>
            <MultiBinding Converter="{StaticResource centerConverter}">
                    <Binding ElementName="txt" Path="ActualWidth"/>
                    <Binding ElementName="txt" Path="ActualHeight"/>
            </MultiBinding>
        </TextBlock.RenderTransform>
    </TextBlock>
    <Rectangle Canvas.Left="39" Canvas.Top="39" Width="2" Height="2" Fill="Red"/>
</Canvas>

TextBlock was the control in question of the original answer. This way should be applicable to all objects of the class UIElement though.

Note: all credit goes to the original poster of the above linked answer

IDarkCoder
  • 709
  • 7
  • 18