c# - WPF Custom GroupBox Header OpacityMask issues -
i've created custom groupbox control provides button @ top right. noticed border not provide gap button header, wrote custom bordergapconverter
. works until content of header changes. border, , believe opacitymask doesn't change until window contains control resized.
buttonedgroupbox.cs
namespace my.controls { /// <summary> /// interaction logic buttonedgroupbox.xaml /// </summary> public partial class buttonedgroupbox : groupbox { public static readonly dependencyproperty commandproperty = dependencyproperty.register("command", typeof(icommand), typeof(buttonedgroupbox), new frameworkpropertymetadata(null)); public static readonly dependencyproperty commandparameterproperty = dependencyproperty.register("commandparameter", typeof(object), typeof(buttonedgroupbox), new frameworkpropertymetadata(null)); protected override void onheaderchanged(object oldheader, object newheader) { base.onheaderchanged(oldheader, newheader); try { getbindingexpression(opacitymaskproperty).updatetarget(); } catch (nullreferenceexception) { } } public icommand command { { return (icommand)getvalue(commandproperty); } set { setvalue(commandproperty, value); } } public object commandparameter { { return getvalue(commandparameterproperty); } set { setvalue(commandparameterproperty, value); } } public buttonedgroupbox() { initializecomponent(); } } }
buttonedgroupbox.xaml
<groupbox x:class="my.controls.buttonedgroupbox" x:name="ctrlthis" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:cv="clr-namespace:my.valueconverters" mc:ignorable="d" d:designheight="300" d:designwidth="300"> <groupbox.resources> <bordergapmaskconverter x:key="bordergapmaskconverter"/> <cv:bordergapconverter x:key="mybordergapmaskconverter"/> </groupbox.resources> <groupbox.style> <style targettype="{x:type groupbox}"> <setter property="borderbrush" value="#d5dfe5"/> <setter property="borderthickness" value="1"/> </style> </groupbox.style> <groupbox.template> <controltemplate targettype="{x:type groupbox}"> <grid snapstodevicepixels="true"> <grid.columndefinitions> <columndefinition width="6"/> <columndefinition width="auto"/> <columndefinition width="*"/> <columndefinition width="auto"/> <columndefinition width="6"/> </grid.columndefinitions> <grid.rowdefinitions> <rowdefinition height="auto"/> <rowdefinition height="auto"/> <rowdefinition height="*"/> <rowdefinition height="6"/> </grid.rowdefinitions> <border borderbrush="transparent" borderthickness="{templatebinding borderthickness}" background="{templatebinding background}" grid.columnspan="5" grid.column="0" cornerradius="4" grid.row="1" grid.rowspan="3"/> <border borderbrush="white" borderthickness="{templatebinding borderthickness}" grid.columnspan="5" cornerradius="4" grid.row="1" grid.rowspan="3"> <border.opacitymask> <multibinding converterparameter="6" converter="{staticresource mybordergapmaskconverter}"> <binding relativesource="{relativesource mode=findancestor, ancestortype=grid}"/> <binding path="actualwidth" relativesource="{relativesource self}"/> <binding path="actualheight" relativesource="{relativesource self}"/> </multibinding> </border.opacitymask> <border borderbrush="{templatebinding borderbrush}" borderthickness="{templatebinding borderthickness}" cornerradius="3"> <border borderbrush="white" borderthickness="{templatebinding borderthickness}" cornerradius="2"/> </border> </border> <border x:name="header" grid.column="1" padding="3,1,3,1" grid.row="0" grid.rowspan="2"> <contentpresenter contentsource="header" recognizesaccesskey="true" snapstodevicepixels="{templatebinding snapstodevicepixels}"/> </border> <border grid.column="3" padding="3,1,3,1" grid.row="0" grid.rowspan="2"> <button x:name="copybutton" content="copy" grid.column="3" grid.row="0" grid.rowspan="2" horizontalalignment="stretch" snapstodevicepixels="{templatebinding snapstodevicepixels}" horizontalcontentalignment="center" verticalcontentalignment="center" command="{binding command, elementname=ctrlthis}" commandparameter="{binding commandparameter, elementname=ctrlthis}"/> </border> <contentpresenter grid.columnspan="3" grid.column="1" margin="{templatebinding padding}" grid.row="2" snapstodevicepixels="{templatebinding snapstodevicepixels}"/> </grid> </controltemplate> </groupbox.template> </groupbox>
bordergapconverter
public class bordergapconverter : imultivalueconverter { public object convert(object[] values, type targettype, object parameter, system.globalization.cultureinfo culture) { type doubletype = typeof(double); // data validation if (values == null) return dependencyproperty.unsetvalue; foreach (var value in values) { type valuetype = value.gettype(); if (value == null || (!typeof(frameworkelement).isassignablefrom(valuetype) && !doubletype.isassignablefrom(valuetype))) return dependencyproperty.unsetvalue; } type paramtype = parameter.gettype();; if (parameter == null || (!doubletype.isassignablefrom(paramtype) && !typeof(string).isassignablefrom(paramtype))) return dependencyproperty.unsetvalue; double offset = 0d; if (parameter string) offset = double.parse((string)parameter, numberformatinfo.invariantinfo); else offset = (double)parameter; grid hostgrid = (grid)values[0]; double borderwidth = (double)values[1]; double borderheight = (double)values[2]; if (borderwidth == 0d || borderheight == 0d) return null; // create 5x2 grid grid grid = new grid() { width = borderwidth, height = borderheight }; foreach(columndefinition col in hostgrid.columndefinitions) grid.columndefinitions.add(new columndefinition() { width = new gridlength(col.actualwidth) }); // define rows grid.rowdefinitions.add(new rowdefinition() { height = new gridlength(borderheight / 2d) }); grid.rowdefinitions.add(new rowdefinition() { height = new gridlength(1d, gridunittype.star) }); /** * cartesian coordinates * - 0 1 2 * 0|-|-|-| * 1|-|-|-| */ // set rectangle 1 0, 0 0, 1 rectangle rectangle = new rectangle() { fill = brushes.black }; grid.setrowspan(rectangle, 2); grid.setrow(rectangle, 0); grid.setcolumn(rectangle, 0); // set rectangle2 1, 1 rectangle rectangle2 = new rectangle() { fill = brushes.black }; grid.setrow(rectangle2, 1); grid.setcolumn(rectangle2, 1); // set rectangle3 0, 2 1, 2 rectangle rectangle3 = new rectangle() { fill = brushes.black }; grid.setrowspan(rectangle3, 2); grid.setrow(rectangle3, 0); grid.setcolumn(rectangle3, 2); // set rectangle4 0, 2 1, 2 rectangle rectangle4 = new rectangle() { fill = brushes.black }; grid.setrow(rectangle4, 1); grid.setcolumn(rectangle4, 3); // set rectangle3 0, 2 1, 2 rectangle rectangle5 = new rectangle() { fill = brushes.black }; grid.setrowspan(rectangle5, 2); grid.setrow(rectangle5, 0); grid.setcolumn(rectangle5, 4); grid.children.add(rectangle); grid.children.add(rectangle2); grid.children.add(rectangle3); grid.children.add(rectangle4); grid.children.add(rectangle5); return new visualbrush(grid); } public object[] convertback(object value, type[] targettypes, object parameter, system.globalization.cultureinfo culture) { return new object[] { binding.donothing }; } }
i've tried override metadata on headerproperty
, override onheaderchanged
, changed using headertemplate
, , bound header
, set headerstringformat
no avail.
here how i'm using control:
<ctrl:buttonedgroupbox command="{binding copycontentscommand}" header="{binding dynamictitle}" headerstringformat="static - {0}"> <textblock> lorem ipsum sit dolorem </textblock> </ctrl:buttonedgroupbox>
how opacitymask update when header's underlying textblock changes?
edit
so added header element's actualwidth multibinding, works seems unnecessary.
<multibinding converterparameter="6" converter="{staticresource mybordergapmaskconverter}"> <binding relativesource="{relativesource mode=findancestor, ancestortype=grid}"/> <binding path="actualwidth" relativesource="{relativesource self}"/> <binding path="actualheight" relativesource="{relativesource self}"/> <binding elementname="header" path="actualwidth"/> </multibinding>
Comments
Post a Comment