WPF 在椭圆中显示 gif

WPF displaying a gif in an ellipse

我需要在 xaml 中以椭圆显示 gif。我知道 NuGet-Package "wpfanimatedgif" 但它不适用于 ImageBrush。

有什么办法可以实现吗? 这是我的示例代码

<Ellipse Grid.Column="2" Grid.Row="0" 
                         Grid.RowSpan="5" 
                         Height="140" Width="140" 
                         HorizontalAlignment="Right" VerticalAlignment="Top" Margin="15">
                          <Ellipse.Fill>
               <ImageBrush Stretch="Fill" ImageSource="..\Resources\MyAnimatedGif.gif"></ImageBrush>
                         </Ellipse.Fill>
                         <Ellipse.Effect>
                            <DropShadowEffect BlurRadius="8" Direction="300"></DropShadowEffect>
                         </Ellipse.Effect>
</Ellipse>

您可以使用普通的 UIElement 并设置 Clip 属性 将其剪裁成椭圆形。在这种情况下,您可以将任何 UI 显示为椭圆,而忽略它的工作原理。但是更新裁剪几何体的大小并不是很容易。

所以我写了一个AttachedProperty 命名为EllipseClipper.IsClipping 给你。通过将此值设置为 True,您可以将 UIElement 剪裁成一个椭圆。此外,EllipseClipper.IsClipping 属性 是一个常见的 属性,任何人都可以在其他情况下使用它。

<Grid Grid.Column="2" Grid.Row="0" 
      Grid.RowSpan="5" 
      HorizontalAlignment="Right" VerticalAlignment="Top" Margin="15">
    <Image Stretch="Fill" Height="140" Width="140"
           local:EllipseClipper.IsClipping="True"
           Source="..\Resources\MyAnimatedGif.gif" />
    <Grid.Effect>
        <DropShadowEffect BlurRadius="8" Direction="300"></DropShadowEffect>
    </Grid.Effect>
</Grid>

因为我没有你的图片,所以我写了一个更简单的示例并运行它来查看结果。它工作正常。

您可以复制并粘贴下面的帮助程序代码,将 EllipseClipper.IsClipping 属性 引入到您的项目中。

/// <summary>
/// Provide the ability to clip an UIElement to an ellipse.
/// </summary>
public static class EllipseClipper
{
    /// <summary>
    /// The attached property of IsClipping.
    /// </summary>
    public static readonly DependencyProperty IsClippingProperty = DependencyProperty.RegisterAttached(
        "IsClipping", typeof(bool), typeof(EllipseClipper), new PropertyMetadata(false, OnIsClippingChanged));

    public static void SetIsClipping(DependencyObject element, bool value)
        => element.SetValue(IsClippingProperty, value);

    public static bool GetIsClipping(DependencyObject element)
        => (bool) element.GetValue(IsClippingProperty);

    private static void OnIsClippingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var source = (UIElement) d;
        if (e.NewValue is false)
        {
            // If IsClipping is set to false, clear the Clip property.
            source.ClearValue(UIElement.ClipProperty);
            return;
        }

        // If the Clip property is using for other usage, throw an exception.
        var ellipse = source.Clip as EllipseGeometry;
        if (source.Clip != null && ellipse == null)
        {
            throw new InvalidOperationException(
                $"{typeof(EllipseClipper).FullName}.{IsClippingProperty.Name} " +
                $"is using {source.GetType().FullName}.{UIElement.ClipProperty.Name} " +
                "for clipping, dont use this property manually.");
        }

        // Use the clip property.
        ellipse = ellipse ?? new EllipseGeometry();
        source.Clip = ellipse;

        // Update the clip property by Bindings.
        var xBinding = new Binding(FrameworkElement.ActualWidthProperty.Name)
        {
            Source = source,
            Mode = BindingMode.OneWay,
            Converter = new HalfConverter(),
        };
        var yBinding = new Binding(FrameworkElement.ActualHeightProperty.Name)
        {
            Source = source,
            Mode = BindingMode.OneWay,
            Converter = new HalfConverter(),
        };
        var xyBinding = new MultiBinding
        {
            Converter = new SizeToClipCenterConverter(),
        };
        xyBinding.Bindings.Add(xBinding);
        xyBinding.Bindings.Add(yBinding);
        BindingOperations.SetBinding(ellipse, EllipseGeometry.RadiusXProperty, xBinding);
        BindingOperations.SetBinding(ellipse, EllipseGeometry.RadiusYProperty, yBinding);
        BindingOperations.SetBinding(ellipse, EllipseGeometry.CenterProperty, xyBinding);
    }

    /// <summary>
    /// Convert the size to ellipse center point.
    /// </summary>
    private sealed class SizeToClipCenterConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            => new Point((double) values[0], (double) values[1]);

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            => throw new NotSupportedException();
    }

    /// <summary>
    /// Calculate half of a double so that RadiusX and RadiusY can be calculated correctly.
    /// </summary>
    private sealed class HalfConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            => (double) value / 2;

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            => throw new NotSupportedException();
    }
}