sohum
sohum

Reputation: 3227

WPF image host with centering and clipping

I feel like I may need some converters, but this is what I want to do. I want a single Image control (since it is in a data template that is bound to real data) with the following parameters.

I have my XAML code below. This works as expected for pictures that are exactly 90x90 (i.e. they don't stretch, they center the image and the clipping works). For images > 90x90, the clipping works correctly but the image is not getting centered. For images < 90x90, the image gets centered but the clipping seems to place the image in the top-left area of the Image content so, the clipping clips the top-left portion of the image.

<Style x:Key="ImageStyle">
    <Setter Property="Width" Value="90" />
    <Setter Property="Height" Value="90" />
    <Setter Property="Stretch" Value="None" />
    <Setter Property="HorizontalAlignment" Value="Center" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="Clip">
        <Setter.Value>
            <EllipseGeometry Center="45,45" RadiusX="40" RadiusY="40" />
        </Setter.Value>
    </Setter>
</Style>

<Grid>
    <!-- Other Stuff -->
    <Image Source="{Binding ImagePath}" Style="{StaticResource ImageStyle}" />
</Grid>

I can get rid of the second issue (small image clipping) by wrapping in a Grid and moving the clipping there, but large stuff doesn't center:

<Grid>
    <!-- Other Stuff -->
    <Grid Width="90" Height="90">
        <Grid.Clip>
            <EllipseGeometry Center="45,45" RadiusX="40" RadiusY="40" />
        </Grid.Clip>
        <Image Source="{Binding ImagePath}" Style="{StaticResource ImageStyle}" />
    </Grid>
</Grid>

Upvotes: 4

Views: 6487

Answers (3)

Petter Hesselberg
Petter Hesselberg

Reputation: 5498

The easiest way I've found to center and clip and image is to use a different element, such as a Rectangle, then fill it with an ImageBrush. For example:

<Rectangle>
    <Rectangle.Fill>
        <ImageBrush
            ImageSource="{Binding SomeUriPropertyOrOther}"
            Stretch="UniformToFill"/>
    </Rectangle.Fill>
</Rectangle>

This does centering and clipping. (Irrelevant for Stretch == Fill and Uniform, I guess.)

Upvotes: 3

sohum
sohum

Reputation: 3227

I ended up having to just remove the Width and Height properties from the Image Style. Looking at it with WPF Snoop, the image is now bigger than the containing Grid but since the Grid has a fixed size, it centers itself on that.

<Style x:Key="ImageStyle">
    <Setter Property="Stretch" Value="None" />
    <Setter Property="HorizontalAlignment" Value="Center" />
    <Setter Property="VerticalAlignment" Value="Center" />
</Style>

<Grid>
    <!-- Other Stuff -->
    <Grid Width="90" Height="90">
        <Grid.Clip>
            <EllipseGeometry Center="45,45" RadiusX="40" RadiusY="40" />
        </Grid.Clip>
        <Image Source="{Binding ImagePath}" Style="{StaticResource ImageStyle}" />
    </Grid>
</Grid>

Upvotes: 10

Walt Ritscher
Walt Ritscher

Reputation: 7037

Use an ImageBrush in your Datatemplate. Then you can paint into an Ellipse, and not use clipping.

XAML

  <Grid>
    <ListBox ItemsSource='{Binding}'>
      <ListBox.ItemTemplate>
        <DataTemplate>

          <Grid Width='90' Height='90' Background='Yellow'>

          <Ellipse Width='40'
                   Height='40'>
            <Ellipse.Fill>
              <ImageBrush ImageSource='{Binding ImagePath}'
                          Stretch='None' />
            </Ellipse.Fill>

          </Ellipse>
          </Grid>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>

Code

public partial class Window4 : Window {
    public Window4() {
      InitializeComponent();

      var data = new List<DemoData>();
      data.Add(new DemoData { Header="Large Image", ImagePath="flower.png"});
      data.Add(new DemoData { Header = "Medium Image", ImagePath = "flower_med.png" });
      data.Add(new DemoData { Header = "Small Image", ImagePath = "flower_small.png" });
      this.DataContext = data;
    }
  }

  internal class DemoData {
    public string Header { get; set; }
    public string ImagePath { get; set; }
  }

Result

ListBox with ellipses

Upvotes: 0

Related Questions