Friday, 15 May 2015

c# - Maintain scrollviewer's relative scrollbar offset when resizing child -



c# - Maintain scrollviewer's relative scrollbar offset when resizing child -

i have scrollviewer grid child. changing grid's width , height properties show different "zoom" levels. grid contains 2 rows many columns of images, same size.

however, want relative position of scrollbar remain same. whatever on center of screen should still on center of screen after changing grid's size.

default "zoomed in" view:

private void sizegrid() { grid1.width = (scrollviewer1.viewportwidth / 2) * grid1.columndefinitions.count; grid1.height = (scrollviewer1.viewportheight / 2) * grid1.rowdefinitions.count; }

"zoomed out" view:

private void scrollviewer1_keydown(object sender, keyeventargs e) { if (e.keyboarddevice.iskeydown(key.insert)) { grid1.width = (scrollviewer1.viewportwidth / 2) * grid1.columndefinitions.count / 5; grid1.height = (scrollviewer1.viewportheight / 2) * grid1.rowdefinitions.count / 3; } }

what tried doing...

if know column focused (i don't want need know this):

double shiftamount = (scrollviewer1.scrollablewidth / (grid1.columndefinitions.count - columnsonscreen)); scrollviewer1.scrolltohorizontaloffset(column * shiftamount);

if don't know column looking at, want maintain relative position...

double previousscrollratio = scrollviewer1.horizontaloffset / scrollviewer1.scrollablewidth; //resize grid... scrollviewer1.scrolltohorizontaloffset(previousscrollratio * scrollviewer1.scrollablewidth);

neither approach works. if zoom out scrollbar centered, scrollbar go far right. idea?

a minimal code illustration can found here plus scroll_keydown method above.

screenshot of default zoom:

screenshot after zooming out, incorrectly (the navy bluish , pinkish squares far off screen):

screenshot after zooming out, should like:

here solution maintain content in center while zooming in or out

class="lang-cs prettyprint-override"> //variables store offset values double relx; double rely; void scrollviewer1_scrollchanged(object sender, scrollchangedeventargs e) { scrollviewer scroll = sender scrollviewer; //see if content size changed if (e.extentwidthchange != 0 || e.extentheightchange != 0) { //calculate , set accordingly scroll.scrolltohorizontaloffset(calculateoffset(e.extentwidth, e.viewportwidth, scroll.scrollablewidth, relx)); scroll.scrolltoverticaloffset(calculateoffset(e.extentheight, e.viewportheight, scroll.scrollableheight, rely)); } else { //store relative values if normal scroll relx = (e.horizontaloffset + 0.5 * e.viewportwidth) / e.extentwidth; rely = (e.verticaloffset + 0.5 * e.viewportheight) / e.extentheight; } } private static double calculateoffset(double extent, double viewport, double scrollwidth, double relbefore) { //calculate new offset double offset = relbefore * extent - 0.5 * viewport; //see if negative because of initial values if (offset < 0) { //center content //this can set 0 if center default not needed offset = 0.5 * scrollwidth; } homecoming offset; }

idea behind store lastly scroll position , utilize calculate new offset whenever content size changed create alter in extent.

just attach event scrollchanged of scrollviewer event handler in constructor etc. , leave rest it.

eg

class="lang-cs prettyprint-override"> scrollviewer1.scrollchanged += scrollviewer1_scrollchanged;

above solution ensure maintain grid in center, first load

sample of centered content

zoomed in

zoomed out

extra

i tried create attachable behavior same not need wire events, setting property enable or disable behavior

class="lang-cs prettyprint-override">namespace csharpwpf { public class advancedzooming : dependencyobject { public static bool getkeepincenter(dependencyobject obj) { homecoming (bool)obj.getvalue(keepincenterproperty); } public static void setkeepincenter(dependencyobject obj, bool value) { obj.setvalue(keepincenterproperty, value); } // using dependencyproperty backing store keepincenter. enables animation, styling, binding, etc... public static readonly dependencyproperty keepincenterproperty = dependencyproperty.registerattached("keepincenter", typeof(bool), typeof(advancedzooming), new propertymetadata(false, onkeepincenterchanged)); // using dependencyproperty backing store behavior. enables animation, styling, binding, etc... public static readonly dependencyproperty behaviorproperty = dependencyproperty.registerattached("behavior", typeof(advancedzooming), typeof(advancedzooming), new propertymetadata(null)); private static void onkeepincenterchanged(dependencyobject d, dependencypropertychangedeventargs e) { scrollviewer scroll = d scrollviewer; if ((bool)e.newvalue) { //attach behavior advancedzooming behavior = new advancedzooming(); scroll.scrollchanged += behavior.scroll_scrollchanged; scroll.setvalue(behaviorproperty, behavior); } else { //dettach behavior advancedzooming behavior = scroll.getvalue(behaviorproperty) advancedzooming; if (behavior != null) scroll.scrollchanged -= behavior.scroll_scrollchanged; scroll.setvalue(behaviorproperty, null); } } //variables store offset values double relx; double rely; void scroll_scrollchanged(object sender, scrollchangedeventargs e) { scrollviewer scroll = sender scrollviewer; //see if content size changed if (e.extentwidthchange != 0 || e.extentheightchange != 0) { //calculate , set accordingly scroll.scrolltohorizontaloffset(calculateoffset(e.extentwidth, e.viewportwidth, scroll.scrollablewidth, relx)); scroll.scrolltoverticaloffset(calculateoffset(e.extentheight, e.viewportheight, scroll.scrollableheight, rely)); } else { //store relative values if normal scroll relx = (e.horizontaloffset + 0.5 * e.viewportwidth) / e.extentwidth; rely = (e.verticaloffset + 0.5 * e.viewportheight) / e.extentheight; } } private static double calculateoffset(double extent, double viewport, double scrollwidth, double relbefore) { //calculate new offset double offset = relbefore * extent - 0.5 * viewport; //see if negative because of initial values if (offset < 0) { //center content //this can set 0 if center default not needed offset = 0.5 * scrollwidth; } homecoming offset; } } }

enabling behavior

via xaml

class="lang-xml prettyprint-override"><scrollviewer l:advancedzooming.keepincenter="true">

or

class="lang-xml prettyprint-override"><style targettype="scrollviewer" x:key="zoomcenter"> <setter property="l:advancedzooming.keepincenter" value="true" /> </style>

or via code like

class="lang-cs prettyprint-override">scrollviewer1.setvalue(advancedzooming.keepincenterproperty, true);

or

class="lang-cs prettyprint-override">advancedzooming.setkeepincenter(scrollviewer1, true);

set property l:advancedzooming.keepincenter="true" inline, via styles or programmatically in order enable behavior on scrollviewer

l: refers namespace advancedzooming class xmlns:l="clr-namespace:csharpwpf" in example

c# wpf

No comments:

Post a Comment