超炫酷的WPF实现Loading控件效果
win8系统的loading效果还是很不错的,网上也有人用css3等技术实现,研究了一下,并打算用wpf自定义一个loading控件实现类似的效果,并可以让用户对loading的颗粒(particle)背景颜色进行自定义,话不多说,直接上代码:
1、用vs2012新建一个wpf的用户控件库项目wpfcontrollibrarydemo,vs自动生成如下结构:
2、删除usercontrol1.xaml,并新建一个loading的customcontrol(不是usercontrol),如下图所示:
3、如果报错找不到loading类型,请编译,下面在generic.xaml主题文件中对loading的样式和内容进行定义(注意添加
xmlns:system = "clr-namespace:system;assembly=mscorlib"),代码如下: <resourcedictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system = "clr-namespace:system;assembly=mscorlib" xmlns:local="clr-namespace:wpfcontrollibrarydemo"> <style targettype="{x:type local:loading}"> <setter property="template"> <setter.value> <controltemplate targettype="{x:type local:loading}"> <border background="{templatebinding background}" borderbrush="{templatebinding borderbrush}" borderthickness="{templatebinding borderthickness}"> <grid width = "50" height = "50"> <grid.resources> <!-- value converters --> <!-- particle styling ,must to has relativesource --> <solidcolorbrush x:key = "particlecolor" color = "{binding path=fillcolor,relativesource={relativesource templatedparent}}" /> <solidcolorbrush x:key = "particlebackgroundcolor" color = "transparent"/> <system:double x:key = "particleopacity">1</system:double> <system:double x:key = "particleradius">5</system:double> <system:double x:key = "startingpointx">0</system:double> <system:double x:key = "startingpointy">-20</system:double> <system:double x:key = "rotationpointx">0.5</system:double> <system:double x:key = "rotationpointy">0.5</system:double> <!-- storyboard --> <system:timespan x:key = "storyboardbegintimep0">00:00:00.000</system:timespan> <system:timespan x:key = "storyboardbegintimep1">00:00:00.100</system:timespan> <system:timespan x:key = "storyboardbegintimep2">00:00:00.200</system:timespan> <system:timespan x:key = "storyboardbegintimep3">00:00:00.300</system:timespan> <system:timespan x:key = "storyboardbegintimep4">00:00:00.400</system:timespan> <duration x:key = "storyboardduration">00:00:01.800</duration> <!-- particle origin angles --> <system:double x:key = "particleoriginanglep0">0</system:double> <system:double x:key = "particleoriginanglep1">-10</system:double> <system:double x:key = "particleoriginanglep2">-20</system:double> <system:double x:key = "particleoriginanglep3">-30</system:double> <system:double x:key = "particleoriginanglep4">-40</system:double> <!-- particle position & timing 1 --> <system:double x:key = "particlebeginangle1">0</system:double> <system:double x:key = "particleendangle1">90</system:double> <system:timespan x:key = "particlebegintime1">00:00:00.000</system:timespan> <duration x:key = "particleduration1">00:00:00.750</duration> <!-- particle position & timing 2 --> <system:double x:key = "particlebeginangle2">90</system:double> <system:double x:key = "particleendangle2">270</system:double> <system:timespan x:key = "particlebegintime2">00:00:00.751</system:timespan> <duration x:key = "particleduration2">00:00:00.300</duration> <!-- particle position & timing 3 --> <system:double x:key = "particlebeginangle3">270</system:double> <system:double x:key = "particleendangle3">360</system:double> <system:timespan x:key = "particlebegintime3">00:00:01.052</system:timespan> <duration x:key = "particleduration3">00:00:00.750</duration> <style x:key = "ellipsestyle" targettype = "ellipse"> <setter property = "width" value = "{staticresource particleradius}"/> <setter property = "height" value = "{staticresource particleradius}"/> <setter property = "fill" value = "{staticresource particlecolor}"/> <setter property = "rendertransformorigin" value = "0.5, 0.5"/> <setter property = "opacity" value = "{staticresource particleopacity}"/> </style> </grid.resources> <canvas width = "1" height = "1" margin="0,0,0,0"> <canvas.triggers> <eventtrigger routedevent = "canvas.loaded"> <eventtrigger.actions> <beginstoryboard> <storyboard begintime = "{staticresource storyboardbegintimep0}" duration = "{staticresource storyboardduration}" repeatbehavior = "forever"> <doubleanimation storyboard.targetname = "p0" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle1}" to = "{staticresource particleendangle1}" begintime = "{staticresource particlebegintime1}" duration = "{staticresource particleduration1}"/> <doubleanimation storyboard.targetname = "p0" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle2}" to = "{staticresource particleendangle2}" begintime = "{staticresource particlebegintime2}" duration = "{staticresource particleduration2}"/> <doubleanimation storyboard.targetname = "p0" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle3}" to = "{staticresource particleendangle3}" begintime = "{staticresource particlebegintime3}" duration = "{staticresource particleduration3}"/> </storyboard> </beginstoryboard> <beginstoryboard> <storyboard begintime = "{staticresource storyboardbegintimep1}" duration = "{staticresource storyboardduration}" repeatbehavior = "forever"> <doubleanimation storyboard.targetname = "p1" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle1}" to = "{staticresource particleendangle1}" begintime = "{staticresource particlebegintime1}" duration = "{staticresource particleduration1}"/> <doubleanimation storyboard.targetname = "p1" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle2}" to = "{staticresource particleendangle2}" begintime = "{staticresource particlebegintime2}" duration = "{staticresource particleduration2}"/> <doubleanimation storyboard.targetname = "p1" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle3}" to = "{staticresource particleendangle3}" begintime = "{staticresource particlebegintime3}" duration = "{staticresource particleduration3}"/> </storyboard> </beginstoryboard> <beginstoryboard> <storyboard begintime = "{staticresource storyboardbegintimep2}" duration = "{staticresource storyboardduration}" repeatbehavior = "forever"> <doubleanimation storyboard.targetname = "p2" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle1}" to = "{staticresource particleendangle1}" begintime = "{staticresource particlebegintime1}" duration = "{staticresource particleduration1}"/> <doubleanimation storyboard.targetname = "p2" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle2}" to = "{staticresource particleendangle2}" begintime = "{staticresource particlebegintime2}" duration = "{staticresource particleduration2}"/> <doubleanimation storyboard.targetname = "p2" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle3}" to = "{staticresource particleendangle3}" begintime = "{staticresource particlebegintime3}" duration = "{staticresource particleduration3}"/> </storyboard> </beginstoryboard> <beginstoryboard> <storyboard begintime = "{staticresource storyboardbegintimep3}" duration = "{staticresource storyboardduration}" repeatbehavior = "forever"> <doubleanimation storyboard.targetname = "p3" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle1}" to = "{staticresource particleendangle1}" begintime = "{staticresource particlebegintime1}" duration = "{staticresource particleduration1}"/> <doubleanimation storyboard.targetname = "p3" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle2}" to = "{staticresource particleendangle2}" begintime = "{staticresource particlebegintime2}" duration = "{staticresource particleduration2}"/> <doubleanimation storyboard.targetname = "p3" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle3}" to = "{staticresource particleendangle3}" begintime = "{staticresource particlebegintime3}" duration = "{staticresource particleduration3}"/> </storyboard> </beginstoryboard> <beginstoryboard> <storyboard begintime = "{staticresource storyboardbegintimep4}" duration = "{staticresource storyboardduration}" repeatbehavior = "forever"> <doubleanimation storyboard.targetname = "p4" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle1}" to = "{staticresource particleendangle1}" begintime = "{staticresource particlebegintime1}" duration = "{staticresource particleduration1}"/> <doubleanimation storyboard.targetname = "p4" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle2}" to = "{staticresource particleendangle2}" begintime = "{staticresource particlebegintime2}" duration = "{staticresource particleduration2}"/> <doubleanimation storyboard.targetname = "p4" storyboard.targetproperty = "(uielement.rendertransform).(rotatetransform.angle)" from = "{staticresource particlebeginangle3}" to = "{staticresource particleendangle3}" begintime = "{staticresource particlebegintime3}" duration = "{staticresource particleduration3}"/> </storyboard> </beginstoryboard> </eventtrigger.actions> </eventtrigger> </canvas.triggers> <border x:name = "p0" background = "{staticresource particlebackgroundcolor}" opacity = "{staticresource particleopacity}"> <border.rendertransform> <rotatetransform/> </border.rendertransform> <border.rendertransformorigin> <point x = "{staticresource rotationpointx}" y = "{staticresource rotationpointy}"/> </border.rendertransformorigin> <ellipse style = "{staticresource ellipsestyle}"> <ellipse.rendertransform> <transformgroup> <translatetransform x = "{staticresource startingpointx}" y = "{staticresource startingpointy}"/> <rotatetransform angle = "{staticresource particleoriginanglep0}"/> </transformgroup> </ellipse.rendertransform> </ellipse> </border> <border x:name = "p1" background = "{staticresource particlebackgroundcolor}" opacity = "{staticresource particleopacity}"> <border.rendertransform> <rotatetransform/> </border.rendertransform> <border.rendertransformorigin> <point x = "{staticresource rotationpointx}" y = "{staticresource rotationpointy}"/> </border.rendertransformorigin> <ellipse style = "{staticresource ellipsestyle}"> <ellipse.rendertransform> <transformgroup> <translatetransform x = "{staticresource startingpointx}" y = "{staticresource startingpointy}"/> <rotatetransform angle = "{staticresource particleoriginanglep1}"/> </transformgroup> </ellipse.rendertransform> </ellipse> </border> <border x:name = "p2" background = "{staticresource particlebackgroundcolor}" opacity = "{staticresource particleopacity}"> <border.rendertransform> <rotatetransform/> </border.rendertransform> <border.rendertransformorigin> <point x = "{staticresource rotationpointx}" y = "{staticresource rotationpointy}"/> </border.rendertransformorigin> <ellipse style = "{staticresource ellipsestyle}"> <ellipse.rendertransform> <transformgroup> <translatetransform x = "{staticresource startingpointx}" y = "{staticresource startingpointy}"/> <rotatetransform angle = "{staticresource particleoriginanglep2}"/> </transformgroup> </ellipse.rendertransform> </ellipse> </border> <border x:name = "p3" background = "{staticresource particlebackgroundcolor}" opacity = "{staticresource particleopacity}"> <border.rendertransform> <rotatetransform/> </border.rendertransform> <border.rendertransformorigin> <point x = "{staticresource rotationpointx}" y = "{staticresource rotationpointy}"/> </border.rendertransformorigin> <ellipse style = "{staticresource ellipsestyle}"> <ellipse.rendertransform> <transformgroup> <translatetransform x = "{staticresource startingpointx}" y = "{staticresource startingpointy}"/> <rotatetransform angle = "{staticresource particleoriginanglep3}"/> </transformgroup> </ellipse.rendertransform> </ellipse> </border> <border x:name = "p4" background = "{staticresource particlebackgroundcolor}" opacity = "{staticresource particleopacity}"> <border.rendertransform> <rotatetransform/> </border.rendertransform> <border.rendertransformorigin> <point x = "{staticresource rotationpointx}" y = "{staticresource rotationpointy}"/> </border.rendertransformorigin> <ellipse style = "{staticresource ellipsestyle}"> <ellipse.rendertransform> <transformgroup> <translatetransform x = "{staticresource startingpointx}" y = "{staticresource startingpointy}"/> <rotatetransform angle = "{staticresource particleoriginanglep4}"/> </transformgroup> </ellipse.rendertransform> </ellipse> </border> </canvas> </grid> </border> </controltemplate> </setter.value> </setter> </style> </resourcedictionary>
在构建中发现,一开始在设定绑定时,写成<solidcolorbrush x:key = "particlecolor" color = "{binding path=fillcolor}" />一直都无法绑定成功,后来查了资料,改成<solidcolorbrush x:key = "particlecolor" color = "{binding path=fillcolor,relativesource={relativesource templatedparent}}" /> 后成功。
4、编辑loading.cs文件,对自定义属性fillcolor和逻辑进行编码:
using system; using system.collections.generic; using system.linq; using system.text; using system.threading.tasks; using system.windows; using system.windows.controls; using system.windows.data; using system.windows.documents; using system.windows.input; using system.windows.media; using system.windows.media.imaging; using system.windows.navigation; using system.windows.shapes; namespace wpfcontrollibrarydemo { using system.componentmodel; /// <summary> /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 xaml 文件中使用此自定义控件。 /// /// 步骤 1a) 在当前项目中存在的 xaml 文件中使用该自定义控件。 /// 将此 xmlnamespace 特性添加到要使用该特性的标记文件的根 /// 元素中: /// /// xmlns:mynamespace="clr-namespace:wpfcontrollibrarydemo" /// /// /// 步骤 1b) 在其他项目中存在的 xaml 文件中使用该自定义控件。 /// 将此 xmlnamespace 特性添加到要使用该特性的标记文件的根 /// 元素中: /// /// xmlns:mynamespace="clr-namespace:wpfcontrollibrarydemo;assembly=wpfcontrollibrarydemo" /// /// 您还需要添加一个从 xaml 文件所在的项目到此项目的项目引用, /// 并重新生成以避免编译错误: /// /// 在解决方案资源管理器中右击目标项目,然后依次单击 /// “添加引用”->“项目”->[浏览查找并选择此项目] /// /// /// 步骤 2) /// 继续操作并在 xaml 文件中使用控件。 /// /// <mynamespace:loading/> /// /// </summary> public class loading : control { static loading() { //重载默认样式 defaultstylekeyproperty.overridemetadata(typeof(loading), new frameworkpropertymetadata(typeof(loading))); //dependencyproperty 注册 fillcolor fillcolorproperty = dependencyproperty.register("fillcolor", typeof(color), typeof(loading), new uipropertymetadata(colors.darkblue, new propertychangedcallback(onurichanged)) ); //colors.darkblue为控件初始化默认值 } //属性变更回调函数 private static void onurichanged(dependencyobject d, dependencypropertychangedeventargs e) { //border b = (border)d; //messagebox.show(e.newvalue.tostring()); } #region 自定义fields // dependencyproperty属性定义 fillcolorproperty=fillcolor+property组成 public static readonly dependencyproperty fillcolorproperty; #endregion //vs设计器属性支持 [description("背景色"), category("个性配置"), defaultvalue("#ff668899")] public color fillcolor { //getvalue,setvalue为固定写法,此处一般不建议处理其他逻辑 get { return (color)getvalue(fillcolorproperty); } set { setvalue(fillcolorproperty, value); } } } }
5、编译,如果无误后,可以添加wpf应用程序wpfapploadingtest进行测试(添加项目引用)。
打开mainwindow.xaml,将loading控件拖放到设计界面上,如下图所示:
6、控件颜色修改,选中控件,在属性栏中进行配置即可:
7.总结
可以看到wpf自定义控件还是比较容易的,但是难点在于ui的设计,如果需要做的美观,需要美工的参与,而且需要转换成xaml。
以上就是wpf实现炫酷loading控件的全部内容,希望对大家的学习有所帮助。
上一篇: C#微信开发(服务器配置)
下一篇: 卢伟冰评“线下机”:话术是核心手段