龙空技术网

WPF 子窗打开时在父窗显示蒙板

opendotnet 738

前言:

此刻姐妹们对“windows7net40”大体比较关注,大家都需要学习一些“windows7net40”的相关知识。那么小编在网摘上汇集了一些关于“windows7net40””的相关文章,希望大家能喜欢,姐妹们快快来学习一下吧!

WPF 如何在控件上显示 Loading 等待动画

控件名:Loading

作 者:WPFDevelopersOrg - 驚鏵

原文链接[1]:

框架使用.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 == )

{

RemoveVisualChild(_child);

_child = value;

return;

}

AddVisualChild(value);

_child = value;

}

}

protected override int VisualChildrenCount

{

get

{

return _child != ? 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 != ) 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 == ) return;

if (isRemove && uIElement != )

{

var adorners = layer.GetAdorners(uIElement);

if (adorners != )

{

foreach (var item in adorners)

{

if (item is AdornerContainer container)

{

var isAddChild = (bool)Loading.GetIsAddChild(uIElement);

if (!isAddChild)

Loading.SetChild(uIElement, );

container.Child = ;

layer.Remove(container);

}

}

}

return;

}

var adornerContainer = new AdornerContainer(uIElement);

var value = Loading.GetChild(uIElement);

if (value == )

{

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 != )

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 != )

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

xmlns:x=""

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

xmlns:x=""

xmlns:mc=""

xmlns:d=""

xmlns:wd=""

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>

参考资料[1]

原文链接:

[2]

Github:

[3]

Github xaml:

[4]

Gitee:

[5]

Gitee xaml:

标签: #windows7net40