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();
}
}
我需要在 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();
}
}