c# - Android's Ripple Effect in WPF -
i love androids new animation touch control (listviewitem, button etc etc) , neat animation this:

i'm wondering how can implemented in nice way globally 'clickable' controls in wpf.
what need how circles should created on control. thing i've thought of create own user-controls each other control (buttons, radiobuttons, etc) have parent ellipse original control itself.
<usercontrol> <grid mouseleftbuttondown="handler"> <button/> <--- button place </grid > </usercontrol> and in handler-method create ellipse on point e.getposition(handler) using margin-properties , later animate it. solution would work. hassle every control want ripple effect on. this:
void handler(object sender, mousebuttoneventargs e) { grid parent = (grid)sender; ellipse ellipse = new ellipse(); ellipse.height = 10; // animated ellipse.width = 10; // animated point p = e.getposition(parent); ellipse.margin = new thickness(p.x, p.y, 0, 0); parent.children.add(ellipse); // animation parts later remove ellipse } is there cleaner, more expandable way place ellipses on controls other way earlier demonstrated since not controls support having children?
update: problem interesting me implemented it. can find on github page: https://github.com/domysee/wpfcustomcontrols. there multiple custom controls, 1 looking rippleeffectdecorator.
now explain did:
i created custom control inherits contentcontrol, rippleeffectdecorator. defines additional dependency property highlightbackground, used background after clicked element.
the controltemplate of rippleeffectdecorator consists of grid, ellipse , contentpresenter.
<controltemplate targettype="{x:type l:rippleeffectdecorator}"> <grid x:name="part_grid" cliptobounds="true" background="{templatebinding background}" width="{binding elementname=part_contentpresenter, path=actualwidth}" height="{binding elementname=part_contentpresenter, path=actualheight}"> <ellipse x:name="part_ellipse" fill="{binding path=highlightbackground, relativesource={relativesource templatedparent}}" width="0" height="{binding path=width, relativesource={relativesource self}}" horizontalalignment="left" verticalalignment="top"/> <contentpresenter x:name="part_contentpresenter" /> </grid> </controltemplate> i used grid instead of border can add multiple child elements (necessary ellipse , contentpresenter can overlap). ellipse binds height property own width, circle.
now important part: animation.
the grid defines in resources storyboard, played on every mousedown event.
<storyboard x:key="part_animation" storyboard.targetname="part_ellipse"> <doubleanimation storyboard.targetproperty="width" from="0" /> <thicknessanimation storyboard.targetproperty="margin" /> <doubleanimation begintime="0:0:1" duration="0:0:0.25" storyboard.targetproperty="opacity" from="1" to="0" /> <doubleanimation storyboard.targetproperty="width" to="0" begintime="0:0:1.25" duration="0:0:0" /> <doubleanimation begintime="0:0:1.25" duration="0:0:0" storyboard.targetproperty="opacity" to="1" /> </storyboard> the storyboard animates width property of ellipse fills area completely. has animate margin, because ellipse positions relative upper left point (not around center).
the start position of ellipse, target width , position in container throughout effect has set programmatically. overwrite onapplytemplate() method add event handler mouse down event, starts storyboard , sets necessary values.
public override void onapplytemplate() { base.onapplytemplate(); ellipse = gettemplatechild("part_ellipse") ellipse; grid = gettemplatechild("part_grid") grid; animation = grid.findresource("part_animation") storyboard; this.addhandler(mousedownevent, new routedeventhandler((sender, e) => { var targetwidth = math.max(actualwidth, actualheight) * 2; var mouseposition = (e mousebuttoneventargs).getposition(this); var startmargin = new thickness(mouseposition.x, mouseposition.y, 0, 0); //set initial margin mouse position ellipse.margin = startmargin; //set value of animation animates width target width (animation.children[0] doubleanimation).to = targetwidth; //set , values of animation animates distance relative container (grid) (animation.children[1] thicknessanimation).from = startmargin; (animation.children[1] thicknessanimation).to = new thickness(mouseposition.x - targetwidth / 2, mouseposition.y - targetwidth / 2, 0, 0); ellipse.beginstoryboard(animation); }), true); } note: last parameter of addhandler() determines whether or not want receive handled events. important set true, because uielements handle mouse events (e.g. button). otherwise mousedownevent not fire , therefore animation not executed.
to use add element on want have effect child of rippleeffectdecorator, , background transparent:
<cc:rippleeffectdecorator background="green" highlightbackground="lightgreen"> <button fontsize="60" background="transparent">stuff</button> </cc:rippleeffectdecorator> note2: elements include triggers set template on mouseover (e.g. button) , therefore hide effect. if dont want have set template of button , remove these triggers. easiest way use blend, template of button it, remove triggers , add template of button.
Comments
Post a Comment