Хост изображений WPF с центрированием и отсечкой

Я чувствую, что мне могут понадобиться некоторые конвертеры, но это то, что я хочу сделать. Я хочу один элемент управления изображения (так как он находится в шаблоне данных, который связан с реальными данными) со следующими параметрами.

  • Сосредоточьтесь на расстоянии 90х90 (без растяжения).
  • Имеют круговое отсечение радиусом 40 пикселей (как горизонтальное, так и вертикальное).
  • Если изображение> 90x90, оно должно центрироваться в пространстве 90x90 и обрезать круг 40x40 от середины.
  • Если изображение <90x90, оно должно центрироваться в пределах пространства 90x90. Обтравочный круг не должен иметь никакого эффекта, поскольку все изображение содержится в области клипа.

У меня есть код XAML ниже. Это работает, как и ожидалось, для изображений размером точно 90x90 (то есть они не растягиваются, они центрируют изображение и отсечение работает). Для изображений> 90x90 отсечение работает правильно, но изображение не центрируется. Для изображений <90x90 изображение центрируется, но отсечение, кажется, помещает изображение в верхнюю левую область содержимого изображения, поэтому отсечение обрезает верхнюю левую часть изображения.

<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>

Я могу избавиться от второй проблемы (маленькое обрезание изображения), обернув его в Grid и переместив его туда, но большие объекты не центрируются:

<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>

3 ответа

Решение

В итоге мне пришлось просто удалить свойства "Ширина" и "Высота" из стиля изображения. Глядя на это с помощью WPF Snoop, изображение теперь больше, чем содержащая Grid, но, поскольку Grid имеет фиксированный размер, он центрируется на этом.

<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>

Самый простой способ, который я нашел для центрирования, обрезки и изображения, - это использовать другой элемент, такой как Rectangle, а затем заполнить его ImageBrush. Например:

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

Это делает центрирование и отсечение. (Не имеет значения для Stretch == Fill and Uniform, я думаю.)

Используйте ImageBrush в вашей Datatemplate. Тогда вы можете рисовать в эллипс, а не использовать отсечение.

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>

Код

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; }
  }

Результат

ListBox с эллипсами

Другие вопросы по тегам