c# - How to achieve even horizontal spacing in ListView and WrapPanel template? -
heres example:
<window x:class="listviewitemspacing.mainwindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:c="clr-namespace:listviewitemspacing.controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:listviewitemspacing" mc:ignorable="d" title="mainwindow" height="350" width="525"> <grid> <listview flowdirection="lefttoright" background="#222"> <listview.itemspanel> <itemspaneltemplate> <wrappanel width="{binding (frameworkelement.actualwidth), relativesource={relativesource ancestortype=scrollcontentpresenter}}" /> </itemspaneltemplate> </listview.itemspanel> <listview.items> <rectangle fill="#27f" width="100" height="100"/> <rectangle fill="#27f" width="100" height="100"/> <rectangle fill="#27f" width="100" height="100"/> <rectangle fill="#27f" width="100" height="100"/> <rectangle fill="#27f" width="100" height="100"/> <rectangle fill="#27f" width="100" height="100"/> </listview.items> </listview> </grid> </window>
this xaml
produces 6 blue squares, 4 in first row, 2 in next row. after 4 first blue squares there space on right.
in other words - looks left aligned text.
i want justified text, want horizontal spacing between squares adjusted align listview
left , right, space evenly distributed between elements, not on right side.
although looks trivial do, don't know how it. don't know start. example - can see spacing added default. ugly default, because horizontal spacing greater vertical, it's not big enough align items both left , right side. spacing come from? how change manually? don't want mess elements themselves. elements in real world app come different module , modules should separated.
most obvious thought handle sizechanged
event of listview
, adjust horizontal spacing manually, - spacing? mean how can access items spacing in code behind?
to clarify: desired rendering should still contain 6 identical blue squares, first 4 aligned left , right control edge, remaining 2 aligned left , previous row squares.
default this:
---------------- |[] [] [] [] | |[] [] | ----------------
desired this:
---------------- |[] [] [] []| |[] [] | ----------------
thanks rachel , dtig made it. seems there's no panel capable of aligning elements wrappanel
customizable horizontalcontentalignment
. , - horizontalcontentalignment = horizontalalignment.stretch
needed here.
following rachel advice tried this, didn't support horizontalalignment.stretch
value.
so added support, works provided element widths equal each other:
/// <summary> /// <see cref="panel"/> <see cref="wrappanel"/> supports <see cref="horizontalcontentalignment"/> property. /// </summary> public class alignablewrappanel : panel { /// <summary> /// <see cref="horizontalalignment"/> property definition. /// </summary> public static readonly dependencyproperty horizontalcontentalignmentproperty = dependencyproperty.register( "horizontalcontentalignment", typeof(horizontalalignment), typeof(alignablewrappanel), new frameworkpropertymetadata(horizontalalignment.left, frameworkpropertymetadataoptions.affectsarrange) ); /// <summary> /// gets or sets horizontal alignment of control's content. /// </summary> [bindableattribute(true)] public horizontalalignment horizontalcontentalignment { { return (horizontalalignment)getvalue(horizontalcontentalignmentproperty); } set { setvalue(horizontalcontentalignmentproperty, value); } } /// <summary> /// measures size in layout required child elements , determines size <see cref="alignablewrappanel"/>. /// </summary> /// <param name="constraint">the available size element can give child elements. infinity can specified value indicate element size whatever content available.</param> /// <returns>the size element determines needs during layout, based on calculations of child element sizes.</returns> protected override size measureoverride(size constraint) { var curlinesize = new size(); var panelsize = new size(); var children = base.internalchildren; (var = 0; < children.count; i++) { var child = children[i] uielement; // flow passes own constraint children child.measure(constraint); var sz = child.desiredsize; if (curlinesize.width + sz.width > constraint.width) { //need switch line panelsize.width = math.max(curlinesize.width, panelsize.width); panelsize.height += curlinesize.height; curlinesize = sz; if (sz.width > constraint.width) { // if element wider constraint - give separate line panelsize.width = math.max(sz.width, panelsize.width); panelsize.height += sz.height; curlinesize = new size(); } } else { //continue accumulate line curlinesize.width += sz.width; curlinesize.height = math.max(sz.height, curlinesize.height); } } // last line size, if need added panelsize.width = math.max(curlinesize.width, panelsize.width); panelsize.height += curlinesize.height; return panelsize; } /// <summary> /// positions child elements , determines size <see cref="alignablewrappanel"/>. /// </summary> /// <param name="arrangebounds">the final area within parent element should use arrange , children.</param> /// <returns>the actual size used.</returns> protected override size arrangeoverride(size arrangebounds) { var firstinline = 0; var curlinesize = new size(); var accumulatedheight = 0.0; var children = internalchildren; (var = 0; < children.count; i++) { var sz = children[i].desiredsize; if (curlinesize.width + sz.width > arrangebounds.width) { //need switch line arrangeline(accumulatedheight, curlinesize, arrangebounds.width, firstinline, i); accumulatedheight += curlinesize.height; curlinesize = sz; if (sz.width > arrangebounds.width) { //the element wider constraint - give separate line arrangeline(accumulatedheight, sz, arrangebounds.width, i, ++i); accumulatedheight += sz.height; curlinesize = new size(); } firstinline = i; } else { //continue accumulate line curlinesize.width += sz.width; curlinesize.height = math.max(sz.height, curlinesize.height); } } if (firstinline < children.count) arrangeline(accumulatedheight, curlinesize, arrangebounds.width, firstinline, children.count); return arrangebounds; } /// <summary> /// arranges elements within line. /// </summary> /// <param name="y">line vertical coordinate.</param> /// <param name="linesize">size of items line.</param> /// <param name="boundswidth">width of panel bounds.</param> /// <param name="start">index of first child belongs line.</param> /// <param name="end">index of last child belongs line.</param> private void arrangeline(double y, size linesize, double boundswidth, int start, int end) { var children = internalchildren; var x = 0.0; var stretchoffset = 0.0; if (horizontalcontentalignment == horizontalalignment.center) x = (boundswidth - linesize.width) / 2; else if (horizontalcontentalignment == horizontalalignment.right) x = (boundswidth - linesize.width); else if (horizontalalignment == horizontalalignment.stretch) { var childwidth = children[start].desiredsize.width; // warning, works when children have equal widths int n = (int)boundswidth / (int)childwidth; if (children.count > n) { var takenwidth = n * childwidth; var spacewidth = boundswidth - takenwidth; stretchoffset = spacewidth / (n - 1); } } (var = start; < end; i++) { var child = children[i]; child.arrange(new rect(x, y, child.desiredsize.width, linesize.height)); x += child.desiredsize.width + stretchoffset; } } }
this tig's solution stretch
alignment added.
here's test xaml this:
<window x:class="listviewitemspacing.mainwindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:customcontrols" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:listviewitemspacing" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" title="mainwindow" width="525" height="250" mc:ignorable="d"> <grid> <listview background="#222"> <listview.itemspanel> <itemspaneltemplate> <c:alignablewrappanel width="{binding (frameworkelement.actualwidth), relativesource={relativesource ancestortype=scrollcontentpresenter}}" horizontalcontentalignment="stretch" /> </itemspaneltemplate> </listview.itemspanel> <listview.items> <rectangle width="100" height="100" fill="#27f" /> <rectangle width="100" height="100" fill="#27f" /> <rectangle width="100" height="100" fill="#27f" /> <rectangle width="100" height="100" fill="#27f" /> <rectangle width="100" height="100" fill="#27f" /> <rectangle width="100" height="100" fill="#27f" /> </listview.items> </listview> </grid> </window>
it's not perfect, specific job perfectly. won't work if sizes of child elements different - in such case takenwidth
should calculated sum of children indices start
end
. should specify different condition last line.
thanks again, kind strangers :)
Comments
Post a Comment