xaml - Custom WPF TextBox with attached unit TextBlock -


my application has tons of textbox controls user can enter numeric values. of these values in physical unit. unit indicator displayed @ right side of textbox control.

that looks following sketch: [________] km (where unit "km")

currently have done stackpanel instances everywhere. it's same pattern. makes xaml less readable should be.

i'm looking textbox control includes textblock @ side display unit.

my first try class derived textbox, xaml file replaces template property this:

<textbox     x:class="wpfapplication1.unittextbox"     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"     x:name="_this"     keyboardnavigation.istabstop="false"     style="{staticresource {x:type textbox}}">     <textbox.template>         <controltemplate>             <grid>                 <grid.columndefinitions>                     <columndefinition width="*"/>                     <columndefinition width="auto"/>                 </grid.columndefinitions>                 <textbox                     foreground="{templatebinding foreground}"                     isenabled="{templatebinding isenabled}"                     isreadonly="{binding isreadonly, elementname=_this}"                     style="{templatebinding style}"                     text="{binding text, elementname=_this}"                     width="{templatebinding width}"                     ... lots more ...                     verticalalignment="center"/>                 <textblock                     grid.column="1"                     text="{binding unit, elementname=_this}"                     margin="4,0,0,0"                     verticalalignment="center"/>             </grid>         </controltemplate>     </textbox.template> </textbox> 

unit dependency property in unittextbox code-behind class:

public partial class unittextbox : textbox {     public static dependencyproperty unitproperty = dependencyproperty.register(         name: "unit",         propertytype: typeof(string),         ownertype: typeof(unittextbox));      public string unit     {         { return (string) getvalue(unitproperty); }         set { setvalue(unitproperty, value); }     }      public unittextbox()     {         initializecomponent();     } } 

unfortunately, there's number of issues approach. need pass through virtually properties inner textbox can see (i abbreviated here). also, i'd width property apply inner textbox usual, not outer grid. think need separate property , bind inner textbox instance that. , currently, style set when using unittextbox class ignored. don't know how solve that.

is there possibility create such combined control wpf? should act textbox event handlers, bindable properties etc., include unit string in appearance, assignable additional property.

could instead use custom style adds textblock somewhere around (but think need outer grid aligning things), , declare unit attached property?

the annoying thing extending templated control typically need define new template each system theme, or customized textbox out of place next regular textbox. however, since "enhancement" simple, can avoid entirely overriding layout , rendering code include unit text:

public class unittextbox : textbox {     private formattedtext _unittext;     private rect _unittextbounds;      public static dependencyproperty unittextproperty =         dependencyproperty.register(             name: "unittext",             propertytype: typeof(string),             ownertype: typeof(unittextbox),             typemetadata: new frameworkpropertymetadata(                 default(string),                 frameworkpropertymetadataoptions.affectsmeasure |                 frameworkpropertymetadataoptions.affectsarrange |                 frameworkpropertymetadataoptions.affectsrender));      public string unittext     {         { return (string)getvalue(unittextproperty); }         set { setvalue(unittextproperty, value); }     }      public static dependencyproperty unitpaddingproperty =         dependencyproperty.register(             name: "unitpadding",             propertytype: typeof(thickness),             ownertype: typeof(unittextbox),             typemetadata: new frameworkpropertymetadata(                 new thickness(5d, 0d, 0d, 0d),                 frameworkpropertymetadataoptions.affectsmeasure |                 frameworkpropertymetadataoptions.affectsarrange |                 frameworkpropertymetadataoptions.affectsrender));      public thickness unitpadding     {         { return (thickness)getvalue(unitpaddingproperty); }         set { setvalue(unitpaddingproperty, value); }     }      public static dependencyproperty textboxwidthproperty =         dependencyproperty.register(             name: "textboxwidth",             propertytype: typeof(double),             ownertype: typeof(unittextbox),             typemetadata: new frameworkpropertymetadata(                 double.nan,                 frameworkpropertymetadataoptions.affectsmeasure));      public double textboxwidth     {         { return (double)getvalue(textboxwidthproperty); }         set { setvalue(textboxwidthproperty, value); }     }      protected override void onpropertychanged(dependencypropertychangedeventargs e)     {         base.onpropertychanged(e);          if (e.property == foregroundproperty)             ensureunittext(invalidate: true);     }      protected override size measureoverride(size constraint)     {         var textboxwidth = this.textboxwidth;         var unit = ensureunittext(invalidate: true);         var padding = this.unitpadding;          if (unit != null)         {             var unitwidth = unit.width + padding.left + padding.right;             var unitheight = unit.height + padding.top + padding.bottom;              constraint = new size(                 constraint.width - unitwidth,                 math.max(constraint.height, unitheight));         }          var hasfixedtextboxwidth = !double.isnan(textboxwidth) &&                                    !double.isinfinity(textboxwidth);          if (hasfixedtextboxwidth)             constraint = new size(textboxwidth, constraint.height);          var basesize = base.measureoverride(constraint);         var basewidth = hasfixedtextboxwidth ? textboxwidth : basesize.width;          if (unit != null)         {             var unitwidth = unit.width + padding.left + padding.right;             var unitheight = unit.height + padding.top + padding.bottom;              return new size(                 basewidth + unitwidth,                 math.max(basesize.height, unitheight));         }          return new size(basewidth, basesize.height);     }      protected override size arrangeoverride(size arrangebounds)     {         var textsize = arrangebounds;         var unit = ensureunittext(invalidate: false);         var padding = this.unitpadding;          if (unit != null)         {             var unitwidth = unit.width + padding.left + padding.right;             var unitheight = unit.height + padding.top + padding.bottom;              textsize.width -= unitwidth;              _unittextbounds = new rect(                 textsize.width + padding.left,                 (arrangebounds.height - unitheight) / 2 + padding.top,                 textsize.width,                 textsize.height);         }          var basesize = base.arrangeoverride(textsize);          if (unit != null)         {             var unitwidth = unit.width + padding.left + padding.right;             var unitheight = unit.height + padding.top + padding.bottom;              return new size(                 basesize.width + unitwidth,                 math.max(basesize.height, unitheight));         }          return basesize;     }      protected override void onrender(drawingcontext drawingcontext)     {         base.onrender(drawingcontext);          var unittext = ensureunittext(invalidate: false);         if (unittext != null)             drawingcontext.drawtext(unittext, _unittextbounds.location);     }      private formattedtext ensureunittext(bool invalidate = false)     {         if (invalidate)             _unittext = null;          if (_unittext != null)             return _unittext;          var unit = this.unittext;          if (!string.isnullorempty(unit))         {             _unittext = new formattedtext(                 unit,                 cultureinfo.invariantculture,                 this.flowdirection,                 new typeface(                     this.fontfamily,                     this.fontstyle,                     this.fontweight,                     this.fontstretch),                 this.fontsize,                 this.foreground);         }          return _unittext;     } } 

the textboxwidth property lets set fixed width textbox. behavior of width remains unchanged, should: governs size of entire control, e.g., textbox and unit text.

no custom style or template necessary.


Comments

Popular posts from this blog

java - Oracle EBS .ClassNotFoundException: oracle.apps.fnd.formsClient.FormsLauncher.class ERROR -

c# - how to use buttonedit in devexpress gridcontrol -

nvd3.js - angularjs-nvd3-directives setting color in legend as well as in chart elements -