WPF实现在控件上显示Loading等待动画的方法详解
WPF 如何在控件上显示 Loading 等待动画
- 框架使用
.net40
; Visual Studio 2022
;- 使用方式需引入命名空间后设置控件的附加属性
wd:Loading.Isshow="true"
,即可显示默认等待动画效果如下:
- 如需自定义
Loading
一定要 先设置wd:Loading.Child
在设置IsShow="true"
。 - 显示不同
Loading
内容需wd:Loading.Child ={x:Static wd:NORMalLoading.Default}
进行复赋值显示NormalLoading
效果如下:
GitHub[2]
github xaml[3]
Gitee[4]
Gitee xaml[5]
实现代码
也可以自定义 Loading
动画如下:
1、自定义控件 CustomLoading
。
public class CustomLoading : Control
{
public static CustomLoading Default = new CustomLoading();
static CustomLoading()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomLoading),
new FrameworkPropertyMetadata(typeof(CustomLoading)));
}
}
2、编写 CustomLoading.xaml
代码如下。
<Style TargetType="{x:Type controls:CustomLoading}">
<Setter Property="Width" Value="40" />
<Setter Property="Height" Value="40" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:CustomLoading}">
<!--此处编写自定义的动画逻辑-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
1)创建装饰 AdornerContainer
代码如下:
using System.windows;
using System.Windows.Documents;
using System.Windows.Media;
namespace WPFDevelopers.Utilities
{
public class AdornerContainer : Adorner
{
private UIElement _child;
public AdornerContainer(UIElement adornedElement) : base(adornedElement)
{
}
public UIElement Child
{
get => _child;
set
{
if (value == null)
{
RemoveVisualChild(_child);
_child = value;
return;
}
AddVisualChild(value);
_child = value;
}
}
protected override int VisualChildrenCount
{
get
{
return _child != null ? 1 : 0;
}
}
protected override Size ArrangeOverride(Size finalSize)
{
_child?.Arrange(new Rect(finalSize));
return finalSize;
}
protected override Visual GetVisualChild(int index)
{
if (index == 0 && _child != null) return _child;
return base.GetVisualChild(index);
}
}
}
2)创建蒙板控件 MaskControl
代码如下:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WPFDevelopers.Controls
{
public class MaskControl : ContentControl
{
private readonly Visual visual;
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.ReGISter("CornerRadius", typeof(CornerRadius), typeof(MaskControl),
new PropertyMetadata(new CornerRadius(0)));
public MaskControl(Visual _visual)
{
visual = _visual;
}
public CornerRadius CornerRadius
{
get => (CornerRadius)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
}
}
3)创建 Loading
继承 BaseControl
增加附加属性 IsShow
代码如下:
True
则动态添加装饰器AdornerContainer
并将MaskControl
添加到AdornerContainer.Child
中。False
则移除装饰器。
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
using WPFDevelopers.Helpers;
using WPFDevelopers.Utilities;
namespace WPFDevelopers.Controls
{
public class Loading : BaseControl
{
public static readonly DependencyProperty IsShowProperty =
DependencyProperty.RegisterAttached("IsShow", typeof(bool), typeof(Loading),
new PropertyMetadata(false, OnIsLoadinGChanged));
private const short SIZE = 25;
private const double MINSIZE = 40;
private static FrameworkElement oldFrameworkElement;
private static void OnIsLoadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is bool isMask && d is FrameworkElement parent)
{
if (isMask)
{
if (!parent.IsLoaded)
parent.Loaded += Parent_Loaded;
else
CreateMask(parent);
}
else
{
parent.Loaded -= Parent_Loaded;
CreateMask(parent, true);
}
}
}
private static void Parent_Loaded(object sender, RoutedEventArgs e)
{
if (sender is UIElement element)
CreateMask(element);
}
static void CreateMask(UIElement uIElement, bool isRemove = false)
{
var layer = AdornerLayer.GetAdornerLayer(uIElement);
if (layer == null) return;
if (isRemove && uIElement != null)
{
var adorners = layer.GetAdorners(uIElement);
if (adorners != null)
{
foreach (var item in adorners)
{
if (item is AdornerContainer container)
{
var isAddChild = (bool)Loading.GetIsAddChild(uIElement);
if (!isAddChild)
Loading.SetChild(uIElement, null);
container.Child = null;
layer.Remove(container);
}
}
}
return;
}
var adornerContainer = new AdornerContainer(uIElement);
var value = Loading.GetChild(uIElement);
if (value == null)
{
var isLoading = GetIsShow(uIElement);
if (isLoading)
{
var w = (double)uIElement.GetValue(ActualWidthProperty);
var h = (double)uIElement.GetValue(ActualHeightProperty);
var defaultLoading = new DefaultLoading();
if (w < MINSIZE || h < MINSIZE)
{
defaultLoading.Width = SIZE;
defaultLoading.Height = SIZE;
defaultLoading.StrokeArray = new DoubleCollection { 10, 100 };
}
SetChild(uIElement, defaultLoading);
value = Loading.GetChild(uIElement);
}
if (value != null)
adornerContainer.Child = new MaskControl(uIElement) { Content = value, Background = ControlsHelper.Brush };
}
else
{
var normalLoading = (FrameworkElement)value;
var frameworkElement = (FrameworkElement)uIElement;
Loading.SetIsAddChild(uIElement, true);
if (oldFrameworkElement != null)
value = oldFrameworkElement;
else
{
string xaml = XamlWriter.Save(normalLoading);
oldFrameworkElement = (FrameworkElement) XamlReader.Parse(xaml);
}
var _size = frameworkElement.ActualHeight < frameworkElement.ActualWidth ? frameworkElement.ActualHeight : frameworkElement.ActualWidth;
if(_size < MINSIZE)
{
normalLoading.Width = SIZE;
normalLoading.Height = SIZE;
value = normalLoading;
}
adornerContainer.Child = new MaskControl(uIElement) { Content = value, Background = ControlsHelper.Brush };
}
layer.Add(adornerContainer);
}
public static bool GetIsShow(DependencyObject obj)
{
return (bool)obj.GetValue(IsShowProperty);
}
public static void SetIsShow(DependencyObject obj, bool value)
{
obj.SetValue(IsShowProperty, value);
}
}
}
4)创建 DefaultLoading.xaml
代码如下:
<ResourceDictionary xmlns="Http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WPFDevelopers.Controls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Basic/ControlBasic.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type controls:DefaultLoading}">
<Setter Property="Width" Value="40" />
<Setter Property="Height" Value="40" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:DefaultLoading}">
<Viewbox Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}">
<controls:SmallPanel>
<controls:SmallPanel.Resources>
<Storyboard x:Key="StarStoryboard" RepeatBehavior="Forever">
<DoubleAnimation
Storyboard.TargetName="PART_Ellipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
To="360"
Duration="0:0:1.0" />
</Storyboard>
</controls:SmallPanel.Resources>
<Ellipse
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Stroke="{DynamicResource BaseSolidColorBrush}"
StrokeDashArray="100,100"
StrokeThickness="2" />
<Ellipse
x:Name="PART_Ellipse"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Stretch="Uniform"
RenderTransformOrigin=".5,.5"
Stroke="{DynamicResource PrimaryPressedSolidColorBrush}"
StrokeDashArray="{TemplateBinding StrokeArray}"
StrokeThickness="2">
<Ellipse.RenderTransform>
<RotateTransform Angle="0" />
</Ellipse.RenderTransform>
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard Storyboard="{StaticResource StarStoryboard}" />
</EventTrigger>
</Ellipse.Triggers>
</Ellipse>
</controls:SmallPanel>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
5)创建 LoadingExample.xaml
实例代码如下:
<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.LoadingExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Margin="10">
<StackPanel Grid.Column="1">
<CheckBox Name="MyCheckBox" Content="启动 Loading 动画"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<UniformGrid Margin="10" Rows="2" Columns="3">
<Border Background="Red"
wd:Loading.IsShow="{Binding ElementName=MyCheckBox,Path=IsChecked}">
<TextBlock Text="Mask 0"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Border>
<Image Source="pack://application:,,,/WPFDevelopers.Samples;component/Images/Breathe/0.jpg"
wd:Loading.IsShow="{Binding ElementName=MyCheckBox,Path=IsChecked}"
wd:Loading.Child="{x:Static wd:NormalLoading.Default}"/>
<Button Content="Mask 1" wd:Loading.IsShow="{Binding ElementName=MyCheckBox,Path=IsChecked}" Height="28"
VerticalAlignment="Top" HorizontalAlignment="Center"/>
<Button Content="Mask 2" wd:Loading.IsShow="{Binding ElementName=MyCheckBox,Path=IsChecked}"
VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,10"/>
<Button Content="提交" wd:Loading.IsShow="{Binding ElementName=MyCheckBox,Path=IsChecked}"
VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,10"
Style="{StaticResource PrimaryButton}"/>
</UniformGrid>
</StackPanel>
</Grid>
</UserControl>
效果图
到此这篇关于WPF实现在控件上显示Loading等待动画的方法详解的文章就介绍到这了,更多相关WPF控件显示Loading等待动画内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
相关文章