c# - Android's Ripple Effect in WPF -


i love androids new animation touch control (listviewitem, button etc etc) , neat animation this:

enter image description here

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

Popular posts from this blog

shopping cart - Page redirect not working PHP -

php - How to modify a menu to show sub-menus -

python - Installing PyDev in eclipse is failed -