c# - WPF - DataGrid nested in Expander no scrolling datagrid content -
i have expander nested datagrid on wpf application - creates usercontrols. create control on codebehind each element data (from database) list. finally, have list each element expadned nested datagrid. when develop item see datadrid, when develop many components must scroll content. when cursor there on expander, element scroll works when mouse hover datagrid, scroll doesn't work.
sample code:
<scrollviewer horizontalalignment="left"> <dockpanel> <expander x:name="expander1" expanded="expander1_expanded"> <expander.content> <datagrid x:name="datagrid1" mouseleftbuttonup="datagrid1_mouseleftbuttondown" scrollviewer.horizontalscrollbarvisibility="hidden" scrollviewer.verticalscrollbarvisibility="hidden" > <datagrid.cellstyle> <style targettype="datagridcell"> <setter property="borderthickness" value="0"/> </style> </datagrid.cellstyle> <datagrid.columns> <datagridtextcolumn header="name1" binding="{binding name}" isreadonly="true" /> <datagridtextcolumn header="name2" binding="{binding value}" isreadonly="true"/> <datagridtextcolumn header="name3" binding="{binding unitname}" isreadonly="true"/> </datagrid.columns> </datagrid> </expander.content> <expander.style> <style targettype="expander"> <setter property="isexpanded" value="false" /> <style.triggers> <datatrigger binding="{binding isexpanded, relativesource={relativesource self}}" value="true"> </datatrigger> </style.triggers> </style> </expander.style> </expander> // , mor expander, added codebehind </dockpanel> </scrollviewer>
finally grid:
when mouse green ring scroll right works
this happens since datagrid
may contain scrollviewer
, appear when have more items fits within given height. in cases, want allow datagrid
attempt handle scroll event first, , if doesn't know it, instance when trying scroll down while @ bottom, pass scroll event along parent.
now, looks datagrid
s not scrollable means following might bit overkill, general solution achieves above obtained introducing following modification mouse wheel handler:
/// <summary> /// helper allowing scroll events pass <see cref="datagrid"/> parent. /// ensures "scroll down" event occurring @ scrolled-down /// <see cref="datagrid"/> passed on parent, might able handle /// instead. /// </summary> public class datagridscrollcorrector { public static bool getfixscrolling(dependencyobject obj) => (bool)obj.getvalue(fixscrollingproperty); public static void setfixscrolling(dependencyobject obj, bool value) => obj.setvalue(fixscrollingproperty, value); public static readonly dependencyproperty fixscrollingproperty = dependencyproperty.registerattached("fixscrolling", typeof(bool), typeof(datagridscrollcorrector), new frameworkpropertymetadata(false, onfixscrollingpropertychanged)); private static void onfixscrollingpropertychanged(object sender, dependencypropertychangedeventargs e) { var grid = sender datagrid; if (grid == null) throw new argumentexception("the dependency property can attached datagrid", nameof(sender)); if ((bool)e.newvalue) grid.previewmousewheel += handlepreviewmousewheel; else grid.previewmousewheel -= handlepreviewmousewheel; } /// <summary> /// finds first child of given type in given <see cref="dependencyobject"/>. /// </summary> /// <typeparam name="t">the type of child search for.</typeparam> /// <param name="depobj">the object children interested in.</param> /// <returns>the child object.</returns> private static t findvisualchild<t>(dependencyobject depobj) t : dependencyobject { if (depobj == null) return null; (var = 0; < visualtreehelper.getchildrencount(depobj); i++) { dependencyobject child = visualtreehelper.getchild(depobj, i); var visualchild = child t; if (visualchild != null) return visualchild; var childitem = findvisualchild<t>(child); if (childitem != null) return childitem; } return null; } /// <summary> /// attempts scroll <see cref="scrollviewer"/> in <see cref="datagrid"/>. /// if no scrolling occurs, pass event parent. /// </summary> private static void handlepreviewmousewheel(object sender, mousewheeleventargs e) { var grid = sender datagrid; var viewer = findvisualchild<scrollviewer>(grid); if (viewer != null) { // listen on changes scrollviewer's scroll offset; if changes // can consider our event handled. in case scrollchanged event never // raised, take mean @ top/bottom of our scroll viewer, // in case provide event our parent. scrollchangedeventhandler handler = (senderscroll, escroll) => e.handled = true; viewer.scrollchanged += handler; // scroll +/- 3 rows depending on whether scrolling or down. // forced layout update necessary ensure event called // (as opposed after small delay). double oldoffset = viewer.verticaloffset; double offsetdelta = e.delta > 0 ? -3 : 3; viewer.scrolltoverticaloffset(oldoffset + offsetdelta); viewer.updatelayout(); viewer.scrollchanged -= handler; } if (e.handled) return; e.handled = true; var eventarg = new mousewheeleventargs(e.mousedevice, e.timestamp, e.delta) { routedevent = uielement.mousewheelevent, source = sender }; var parent = ((control)sender).parent uielement; parent?.raiseevent(eventarg); } }
here, hardcoded 3
number of rows scroll in datagrid
. apply corrector relevant datagrid
s. instance, use on grids in application, add application.resources
in app.xaml
follows:
<application x:class="wpfapplication1.app" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:wpfapplication1" startupuri="mainwindow.xaml"> <application.resources> <style targettype="datagrid"> <setter property="local:datagridscrollcorrector.fixscrolling" value="true" /> </style> </application.resources> </application>
credit: solution based on/inspired 1 mentioned in blog post restricts function datagrid
(since in experience, otherwise break bunch of other controls aren't prepared tunnel events).
Comments
Post a Comment