WPF进阶 | 深入 WPF 依赖项属性:理解其强大功能与应用场景
- 前言
- 一、依赖项属性基础概念
- 1.1 什么是依赖项属性
- 1.2 依赖项属性与 CLR 属性的区别
- 1.3 依赖项属性的定义与注册
- 二、依赖项属性的原理深入剖析
- 2.1 依赖项属性系统的工作机制
- 2.2 元数据(Metadata)的作用
- 三、依赖项属性的应用场景
- 3.1 数据绑定
- 3.2 样式与模板
- 3.3 动画
- 3.4 自定义控件开发
- 四、依赖项属性的高级应用
- 4.1 附加属性(Attached Properties)
- 4.2 依赖项属性的继承与重写
- 4.3 依赖项属性在复杂界面布局中的应用
- 五、依赖项属性的性能优化与常见问题处理
- 5.1 性能优化
- 5.2 常见问题处理
- 六、总结
- 结束语
- 优质源码分享
WPF进阶 | 深入 WPF 依赖项属性:理解其强大功能与应用场景
,在 Windows Presentation Foundation(WPF)开发中,依赖项属性是一个核心且强大的特性,它为构建灵活、可扩展的用户界面提供了关键支持。理解并熟练运用依赖项属性,对于开发高效、健壮的 WPF 应用程序至关重要。本文将深入探讨 WPF 依赖项属性,通过丰富的代码示例和对关键概念的详细解释,帮助读者全面掌握这一重要特性及其应用场景。
前言
在数字浪潮汹涌澎湃的时代,程序开发宛如一座神秘而宏伟的魔法城堡,矗立在科技的浩瀚星空中。代码的字符,似那闪烁的星辰,按照特定的轨迹与节奏,组合、交织、碰撞,即将开启一场奇妙且充满无限可能的创造之旅。当空白的文档界面如同深邃的宇宙等待探索,程序员们则化身无畏的星辰开拓者,指尖在键盘上轻舞,准备用智慧与逻辑编织出足以改变世界运行规则的程序画卷,在 0 和 1 的二进制世界里,镌刻下属于人类创新与突破的不朽印记。
在当今数字化时代,桌面应用程序的用户界面(UI)设计至关重要,它直接影响着用户体验与产品的竞争力。而 WPF(Windows Presentation Foundation)作为微软推出的一款强大的 UI 框架,其布局系统更是构建精美界面的核心要素。WPF 布局系统为开发者提供了丰富多样的布局方式,能够轻松应对各种复杂的界面设计需求,无论是简洁明了的工具软件,还是功能繁杂的企业级应用,都能借助其打造出令人惊艳的视觉效果与流畅的交互体验。
WPF从入门到精通专栏,旨在为读者呈现一条从对 WPF(Windows Presentation Foundation)技术懵懂无知到精通掌握的学习路径。首先从基础入手,介绍 WPF 的核心概念,涵盖其独特的架构特点、开发环境搭建流程,详细解读布局系统、常用控件以及事件机制等基础知识,帮助初学者搭建起对 WPF 整体的初步认知框架。随着学习的深入,进阶部分聚焦于数据绑定、样式模板、动画特效等关键知识点,进一步拓展 WPF 开发的能力边界,使开发者能够打造出更为个性化、交互性强的桌面应用界面。高级阶段则涉及自定义控件开发、MVVM 设计模式应用、多线程编程等深层次内容,助力开发者应对复杂的业务需求,构建大型且可维护的应用架构。同时,通过实战项目案例解析,展示如何将所学知识综合运用到实际开发中,从需求分析到功能实现再到优化测试,全方位积累实践经验。此外,还探讨了性能优化、与其他技术集成以及安全机制等拓展性话题,让读者对 WPF 技术在不同维度有更深入理解,最终实现对 WPF 技术的精通掌握,具备独立开发高质量桌面应用的能力。
🛕 点击进入WPF从入门到精通专栏
一、依赖项属性基础概念
1.1 什么是依赖项属性
依赖项属性是一种特殊类型的属性,它的值不仅取决于自身的赋值,还可能依赖于其他因素,如数据绑定、样式设置、动画等。与传统的 CLR 属性不同,依赖项属性通过 WPF 的依赖项属性系统进行管理,这使得它们具有很多独特的功能和优势。
例如,在一个简单的 WPF 按钮中,Button的Background属性就是一个依赖项属性。我们可以通过直接赋值来设置按钮的背景颜色,也可以通过数据绑定将背景颜色与某个数据源的值关联起来,还可以使用样式来统一设置按钮的背景颜色,甚至可以通过动画来动态改变按钮的背景颜色。
1.2 依赖项属性与 CLR 属性的区别
存储方式:CLR 属性的值直接存储在对象的字段中,而依赖项属性的值存储在一个独立的属性存储机制中,称为依赖项对象的属性存储区。这种分离的存储方式使得依赖项属性能够支持更多的功能,如数据绑定、样式设置等。
功能特性:CLR 属性主要用于简单的数据存储和访问,而依赖项属性具有更丰富的功能,包括:
值继承:依赖项属性可以在元素树中进行值继承。例如,在一个包含多个子控件的容器中,如果容器设置了某个依赖项属性的值,其子控件可以自动继承该值,除非子控件显式地设置了不同的值。
数据绑定:依赖项属性天生支持数据绑定,这使得我们可以方便地将界面元素的属性与数据源进行绑定,实现数据的双向同步。
样式设置:依赖项属性可以通过样式进行统一设置和修改,从而实现界面的统一风格和主题切换。
动画支持:依赖项属性可以参与动画,通过改变属性值随时间的变化来创建动态的视觉效果。
1.3 依赖项属性的定义与注册
在 WPF 中,定义一个依赖项属性需要以下几个步骤:
定义依赖项属性字段:首先,需要在类中定义一个静态的DependencyProperty
字段,用于标识这个依赖项属性。例如:
public class MyControl : Control
{
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(MyControl), new PropertyMetadata("默认值"));
}
在这个例子中,MyPropertyProperty
是一个静态的DependencyProperty
字段,Register
方法用于注册这个依赖项属性。Register
方法的第一个参数是属性的名称(MyProperty
),第二个参数是属性的类型(typeof(string))
,第三个参数是拥有该属性的类的类型(typeof(MyControl))
,第四个参数是属性的元数据,这里使用PropertyMetadata
来设置属性的默认值为 “默认值”。
定义 CLR 属性包装器:为了方便在代码中使用依赖项属性,通常会定义一个 CLR 属性包装器,通过这个包装器来访问和设置依赖项属性的值。例如:
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
在这个 CLR 属性包装器中,GetValue
方法用于获取依赖项属性的值,SetValue
方法用于设置依赖项属性的值。
二、依赖项属性的原理深入剖析
2.1 依赖项属性系统的工作机制
WPF 的依赖项属性系统是一个复杂而高效的机制,它负责管理依赖项属性的各种行为。当获取或设置一个依赖项属性的值时,依赖项属性系统会按照一定的规则来确定最终的值。这个规则涉及到多个因素,包括:
本地值:如果在代码中直接对依赖项属性进行了赋值,这个值就是本地值。本地值具有最高的优先级,会覆盖其他来源的值。
继承值:如果依赖项属性支持值继承,并且在当前元素的父元素中设置了该属性的值,当前元素会继承这个值,除非它自己设置了本地值。
默认值:在注册依赖项属性时,可以通过PropertyMetadata设置默认值。如果没有其他值来源,依赖项属性会使用默认值。
数据绑定值:如果依赖项属性与某个数据源进行了绑定,数据绑定的值会参与最终值的确定。数据绑定的值的优先级低于本地值,但高于继承值和默认值。
样式和模板值:通过样式和模板设置的依赖项属性值也会影响最终的值。样式和模板值的优先级低于本地值和数据绑定值,但高于继承值和默认值。
例如,假设我们有一个Button
控件,其Background
属性是一个依赖项属性。如果我们在代码中直接设置了Button.Background = Brushes.Red;
,这就是本地值,按钮的背景颜色将是红色。如果没有设置本地值,而按钮的父容器设置了Background
属性,按钮会继承父容器的背景颜色。如果既没有本地值也没有继承值,Button
会使用Background
属性的默认值。如果Button.Background
与某个数据源进行了绑定,并且数据源的值发生了变化,按钮的背景颜色会根据绑定的值进行更新。
2.2 元数据(Metadata)的作用
在注册依赖项属性时,PropertyMetadata
用于定义依赖项属性的元数据,它包含了很多重要的信息,如:
默认值:前面已经提到,通过PropertyMetadata
可以设置依赖项属性的默认值。
属性更改回调:可以定义一个回调方法,当依赖项属性的值发生变化时,会自动调用这个回调方法。例如:
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(MyControl),
new PropertyMetadata("默认值", OnMyPropertyChanged));
private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyControl control = (MyControl)d;
string oldValue = (string)e.OldValue;
string newValue = (string)e.NewValue;
// 在这里处理属性值变化的逻辑,例如更新界面显示
control.UpdateUI(oldValue, newValue);
}
在这个例子中,OnMyPropertyChanged是属性更改回调方法,当MyProperty属性的值发生变化时,会传入DependencyObject(即拥有该属性的对象)和DependencyPropertyChangedEventArgs(包含旧值和新值等信息),我们可以在这个方法中处理属性值变化的逻辑。
强制值回调:在某些情况下,可能需要在设置依赖项属性值之前对值进行验证或修正,这时可以使用强制值回调。例如:
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(MyControl),
new PropertyMetadata(0, null, CoerceMyProperty));
private static object CoerceMyProperty(DependencyObject d, object baseValue)
{
int value = (int)baseValue;
if (value < 0)
{
value = 0;
}
else if (value > 100)
{
value = 100;
}
return value;
}
在这个例子中,CoerceMyProperty
是强制值回调方法,当设置MyProperty
属性的值时,如果值小于 0 或大于 100,会将其修正为 0 或 100。
三、依赖项属性的应用场景
3.1 数据绑定
数据绑定是依赖项属性最常见的应用场景之一。通过数据绑定,我们可以将界面元素的属性与数据源进行关联,实现数据的双向同步。例如,我们有一个包含TextBox和Button的窗口,TextBox用于输入文本,Button用于显示输入的文本:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Data Binding Example" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<TextBox Text="{Binding UserInput}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="200"/>
<Button Content="{Binding UserInput}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,50,0,0"/>
</Grid>
</Window>
在后台代码中,定义MainViewModel类:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApp1
{
public class MainViewModel : INotifyPropertyChanged
{
private string _userInput;
public string UserInput
{
get { return _userInput; }
set
{
_userInput = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
在这个例子中,TextBox
的Text
属性和Button
的Content
属性都绑定到了MainViewModel
的UserInput
属性。当用户在TextBox
中输入文本时,UserInput属性的值会更新,同时Button的Content
也会随之更新,实现了数据的双向同步。
3.2 样式与模板
依赖项属性在样式和模板中起着关键作用。通过样式,我们可以统一设置一组控件的外观和行为;通过模板,我们可以自定义控件的可视化结构。例如,我们可以定义一个按钮的样式,设置按钮的背景颜色、字体大小等依赖项属性:
<Window.Resources>
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
</Style>
</Window.Resources>
<Grid>
<Button Content="Click Me" Style="{StaticResource MyButtonStyle}"/>
</Grid>
在这个例子中,MyButtonStyle
样式通过Setter
设置了Button
的Background
、Foreground
和FontSize
等依赖项属性,所有应用该样式的按钮都会具有相同的外观。
对于模板,我们可以通过依赖项属性来控制模板中的元素。例如,定义一个自定义的ProgressBar
模板,通过依赖项属性来控制进度条的进度:
<ControlTemplate x:Key="MyProgressBarTemplate" TargetType="ProgressBar">
<Grid>
<Rectangle Fill="Gray" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"/>
<Rectangle Fill="Green" Width="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ProgressConverter}}" Height="{TemplateBinding Height}"/>
</Grid>
</ControlTemplate>
在这个模板中,Rectangle
的Width
属性通过TemplateBinding绑定到ProgressBar
的Value
属性(经过ProgressConverter
转换),实现了根据ProgressBar
的进度值来动态改变绿色矩形的宽度,从而显示进度条的进度。
3.3 动画
依赖项属性是实现 WPF 动画的基础。通过动画,我们可以改变依赖项属性的值随时间的变化,创建出各种动态的视觉效果。例如,我们可以创建一个动画,让按钮在点击时逐渐放大:
<Window.Resources>
<Storyboard x:Key="ButtonGrowStoryboard">
<DoubleAnimation
Storyboard.TargetName="MyButton"
Storyboard.TargetProperty="Width"
From="100" To="150" Duration="0:0:0.5"/>
</Storyboard>
</Window.Resources>
<Grid>
<Button x:Name="MyButton" Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource ButtonGrowStoryboard}"/>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
在这个例子中,DoubleAnimation
动画作用于Button
的Width
依赖项属性,从 100 逐渐变化到 150,持续时间为 0.5 秒。当按钮被点击时,触发动画,按钮的宽度会逐渐增加,实现放大效果。
3.4 自定义控件开发
在自定义控件开发中,依赖项属性是必不可少的。通过定义依赖项属性,我们可以为自定义控件提供丰富的功能和可定制性。例如,我们创建一个自定义的NumericUpDown
控件,用于输入和调整数值:
public class NumericUpDown : Control
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown),
new PropertyMetadata(0, OnValueChanged));
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
NumericUpDown control = (NumericUpDown)d;
int oldValue = (int)e.OldValue;
int newValue = (int)e.NewValue;
// 在这里处理数值变化的逻辑,例如更新显示
control.UpdateDisplay(oldValue, newValue);
}
}
在 XAML 中使用这个自定义控件:
<local:NumericUpDown Value="5" HorizontalAlignment="Left" VerticalAlignment="Top"/>
在这个例子中,NumericUpDown
控件定义了一个Value
依赖项属性,用于存储当前的数值。当Value
属性的值发生变化时,会调用OnValueChanged
回调方法,我们可以在这个方法中处理数值变化的逻辑,如更新控件的显示。
四、依赖项属性的高级应用
4.1 附加属性(Attached Properties)
附加属性是一种特殊的依赖项属性,它可以附加到任何依赖项对象上,而不需要在该对象的类中预先定义。附加属性通常用于在不修改现有类的情况下为其添加额外的功能。例如,Grid
的Row
和Column
属性就是附加属性,它们可以附加到Grid的子控件上,用于指定子控件在Grid
中的位置:
<Grid>
<Button Content="Button 1" Grid.Row="0" Grid.Column="0"/>
<Button Content="Button 2" Grid.Row="0" Grid.Column="1"/>
</Grid>
在代码中定义附加属性的方式与普通依赖项属性类似,只是注册方法略有不同。例如,定义一个自定义的附加属性MyAttachedProperty
:
public class MyAttachedProperties
{
public static readonly DependencyProperty MyAttachedPropertyProperty =
DependencyProperty.RegisterAttached("MyAttachedProperty", typeof(string), typeof(MyAttachedProperties),
new PropertyMetadata(null));
public static void SetMyAttachedProperty(DependencyObject element, string value)
{
element.SetValue(MyAttachedPropertyProperty, value);
}
public static string GetMyAttachedProperty(DependencyObject element)
{
return (string)element.GetValue(MyAttachedPropertyProperty);
}
}
在 XAML 中使用这个附加属性:
<Button Content="My Button" local:MyAttachedProperties.MyAttachedProperty="Some Value"/>
4.2 依赖项属性的继承与重写
依赖项属性支持继承和重写。当一个类继承自另一个类时,它会继承父类的依赖项属性。如果子类需要修改依赖项属性的行为,可以通过重写元数据来实现。例如,假设我们有一个BaseControl
类,定义了一个MyProperty
依赖项属性:
public class BaseControl : Control
{
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(BaseControl),
new PropertyMetadata("Base Value"));
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
}
假设我们有一个自定义的AgeInput
控件,用于输入年龄,要求年龄必须是大于 0 且小于 150 的整数。可以这样实现:
public class AgeInput : Control
{
public static readonly DependencyProperty AgeProperty =
DependencyProperty.Register("Age", typeof(int), typeof(AgeInput),
new PropertyMetadata(0, OnAgeChanged, CoerceAge));
public int Age
{
get { return (int)GetValue(AgeProperty); }
set { SetValue(AgeProperty, value); }
}
private static void OnAgeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
int oldAge = (int)e.OldValue;
int newAge = (int)e.NewValue;
// 这里可以添加一些年龄变化时的额外逻辑,比如更新界面提示
AgeInput control = (AgeInput)d;
control.UpdateAgeDisplay(oldAge, newAge);
}
private static object CoerceAge(DependencyObject d, object baseValue)
{
int value = (int)baseValue;
if (value <= 0)
{
value = 1;
// 可以添加提示信息,告知用户输入不符合要求
}
else if (value >= 150)
{
value = 149;
// 可以添加提示信息,告知用户输入不符合要求
}
return value;
}
}
在 XAML 中使用该控件:
<local:AgeInput Age="25" HorizontalAlignment="Left" VerticalAlignment="Top"/>
在这个示例中,CoerceAge
方法作为强制值回调,会在设置Age
属性值时检查输入值是否在合理范围内。如果不在,会将其修正为合理的值,确保了数据的有效性。
4.3 依赖项属性在复杂界面布局中的应用
在构建复杂的用户界面时,依赖项属性的灵活性和强大功能得以充分体现。例如,在一个具有动态布局的多文档界面(MDI)应用中,每个文档窗口的大小、位置等属性都可以定义为依赖项属性。
public class DocumentWindow : Window
{
public static readonly DependencyProperty WindowWidthProperty =
DependencyProperty.Register("WindowWidth", typeof(double), typeof(DocumentWindow),
new PropertyMetadata(400.0));
public static readonly DependencyProperty WindowHeightProperty =
DependencyProperty.Register("WindowHeight", typeof(double), typeof(DocumentWindow),
new PropertyMetadata(300.0));
public double WindowWidth
{
get { return (double)GetValue(WindowWidthProperty); }
set { SetValue(WindowWidthProperty, value); }
}
public double WindowHeight
{
get { return (double)GetValue(WindowHeightProperty); }
set { SetValue(WindowHeightProperty, value); }
}
// 可以添加更多依赖项属性,如窗口位置等
}
在 XAML 中:
<local:DocumentWindow WindowWidth="500" WindowHeight="400" Title="文档1"/>
通过这种方式,可以方便地在 XAML 中配置每个文档窗口的大小,也可以通过数据绑定或其他方式动态改变这些属性,实现灵活的界面布局。
五、依赖项属性的性能优化与常见问题处理
5.1 性能优化
减少不必要的属性更改通知:依赖项属性的属性更改回调会在属性值变化时被调用,如果频繁触发属性更改通知,可能会影响性能。在代码中,尽量批量处理属性值的变化,而不是每次小幅度更改都触发通知。例如,在更新一个包含多个依赖项属性的自定义控件时,可以先设置一个标志位,在所有属性都更新完成后,再统一触发属性更改通知。
合理使用默认值:依赖项属性的默认值设置对性能也有一定影响。如果默认值设置得合理,可以减少不必要的计算和赋值操作。例如,对于一个在大多数情况下都显示为黑色的文本控件,将Foreground
属性的默认值设置为Brushes.Black
,避免在每个实例创建时都进行额外的初始化操作。
避免过度依赖值继承:虽然依赖项属性的值继承机制很方便,但在大型复杂的元素树中,过度使用值继承可能会导致性能下降。因为值继承需要在元素树中向上或向下查找属性值,这会增加查找的时间开销。尽量在必要的情况下使用值继承,并且注意元素树的层级深度。
5.2 常见问题处理
属性值不更新问题:有时会遇到依赖项属性的值在代码中修改了,但界面上没有及时更新的情况。这可能是因为没有正确触发属性更改通知,或者数据绑定的更新模式设置不正确。确保在属性值变化时,正确调用OnPropertyChanged
方法(如果使用INotifyPropertyChanged
接口),并且检查数据绑定的UpdateSourceTrigger
属性,确保其设置符合需求。
依赖项属性冲突:在复杂的项目中,可能会出现不同的库或模块定义了相同名称的依赖项属性,导致冲突。为了避免这种情况,在定义依赖项属性时,尽量使用唯一的命名空间或前缀。例如,在自定义控件库中,使用库的名称作为依赖项属性名称的前缀,如MyLibrary_MyProperty
。
附加属性的使用误区:在使用附加属性时,容易出现将附加属性添加到不支持的对象上,或者在获取和设置附加属性时出现类型错误。在使用附加属性之前,仔细检查目标对象是否支持该附加属性,并且在代码中进行必要的类型检查和转换。
六、总结
WPF 依赖项属性是 WPF 框架中一个极其强大且核心的特性,它为开发者提供了构建灵活、可扩展用户界面的有力工具。通过本文对依赖项属性的基础概念、原理、应用场景、高级应用以及性能优化和常见问题处理的全面深入探讨,读者应该对依赖项属性有了全面而深入的理解。在实际的 WPF 应用开发中,根据具体的需求,合理、巧妙地运用依赖项属性,可以显著提高代码的质量和可维护性,打造出更加高效、健壮的用户界面。随着 WPF 技术的不断发展和应用场景的日益丰富,依赖项属性的应用也将不断拓展和深化,开发者需要持续关注和学习,以充分发挥其强大功能。
结束语
展望未来,WPF 布局系统依然有着广阔的发展前景。随着硬件技术的不断革新,如高分辨率屏幕、折叠屏设备的日益普及,WPF 布局系统有望进一步强化其自适应能力,为用户带来更加流畅、一致的体验。在应对高分辨率屏幕时,能够更加智能地缩放和布局元素,确保文字清晰可读、图像不失真;对于折叠屏设备,可动态调整布局结构,充分利用多屏空间,实现无缝切换。
性能优化方面,微软及广大开发者社区将持续努力,进一步降低复杂布局的计算开销,提高布局更新的效率,使得 WPF 应用在处理大规模数据、动态界面时依然能够保持高效响应。通过改进算法、优化内存管理等手段,让 WPF 布局系统在性能上更上一层楼。
亲爱的朋友,无论前路如何漫长与崎岖,都请怀揣梦想的火种,因为在生活的广袤星空中,总有一颗属于你的璀璨星辰在熠熠生辉,静候你抵达。
愿你在这纷繁世间,能时常收获微小而确定的幸福,如春日微风轻拂面庞,所有的疲惫与烦恼都能被温柔以待,内心永远充盈着安宁与慰藉。
至此,文章已至尾声,而您的故事仍在续写,不知您对文中所叙有何独特见解?期待您在心中与我对话,开启思想的新交流。
优质源码分享
-
【百篇源码模板】html5各行各业官网模板源码下载
-
【模板源码】html实现酷炫美观的可视化大屏(十种风格示例,附源码)
-
【VUE系列】VUE3实现个人网站模板源码
-
【HTML源码】HTML5小游戏源码
-
【C#实战案例】C# Winform贪吃蛇小游戏源码
💞 关注博主 带你实现畅游前后端
🏰 大屏可视化 带你体验酷炫大屏
💯 神秘个人简介 带你体验不一样得介绍
🎀 酷炫邀请函 带你体验高大上得邀请
① 🉑提供云服务部署(有自己的阿里云);
② 🉑提供前端、后端、应用程序、H5、小程序、公众号等相关业务;
如🈶合作请联系我,期待您的联系。
注:本文撰写于CSDN平台,作者:xcLeigh(所有权归作者所有) ,https://blog.csdn.net/weixin_43151418,如果相关下载没有跳转,请查看这个地址,相关链接没有跳转,皆是抄袭本文,转载请备注本文原地址。
亲,码字不易,动动小手,欢迎 点赞 ➕ 收藏,如 🈶 问题请留言(评论),博主看见后一定及时给您答复,💌💌💌
原文地址:https://blog.csdn.net/weixin_43151418/article/details/145325587(防止抄袭,原文地址不可删除)