StrataFrame Forum

Subclassing DevEx.TextEdit

http://forum.strataframe.net/Topic25852.aspx

By Charles R Hankey - 2/9/2010

I would like to subclass the Microfour.Strataframe.UI.Windows.Forms.DevEx.TextEdit so I don't have to set all the properties each time to get currency box behavior when I drop one on a form (this form will have > 30 ) I will be using this to bind to character fields as well.



I am still a little uncertain as to how masking works in .NET.



I am trying to add a new component, and set the inherits, then set the properties I want in the properties sheet. I get a warning that fProperties has a the exact property in the base class and should be marked Shadows but I'm out of my depth on exactly how to set this up.



I'd be open to any alternative suggestions as to how to create the textbox that will behave right to left, 2 decimals, show with normal backcolor when disabled or readonly etc. The DevEx.TextEdit ( part of the DevExpress freebie in the XtraEditors ) might be the easiest to adapt for my purposes but if anyone has built something else they'd like to share I'd certainly like to see it.



Assume others have been down this path before so I'll look forward to suggestions.



TIA
By Trent L. Taylor - 2/9/2010

You have two options.  Implement the Shadows property and create your own internal privates to set the values on the parent class or create totally new properties that you set and in turn sets the parents base properties.

Private _MyText As String = String.Empty
Public Shadows Property Text As String
    Get
         '-- Make sure that the base property is set when being evaluated
         If Not MyBase.Text.Equals(_MyText) Then MyBase.Text = _MyText
       
         Return _MyText
    End get
    Set(Byval value As String)
          _MyText = value
          MyBase.Text = _MyText
    End Set
End Property

The second option is basically the same, but is slightly harder to implement dynamically as the base class will not automatically evaluate the property, thus requiring you to set each property regardless of the serialized state.

Hope this helps Smile  Also, the above code was quick and dirty and not tested, but it will be close. Wink

By Charles R Hankey - 2/9/2010

First, in case I didn't say so before, I created this by using a component class.



I think I get what you're saying but the fProperties seems to encompass all the dx specific properties of TextEdit, storing them in the repository.



The component library builds, but when I drop the component on a form all the props I set in the subclassed component are ignored, then there is a problem reopening the component in the designer.



The warning I get is this :



WithEvents variable 'fProperties' implicitly defines '_fProperties', which conflicts with a member of the same name in class 'dxTxtC'.






I tried this ( both the commented and uncommented version ) with exactly the same result



Public Class dxTxtC

Inherits MicroFour.StrataFrame.UI.Windows.Forms.DevEx.TextEdit



'Private _myfProperties As DevExpress.XtraEditors.Repository.RepositoryItemTextEdit



'Public Shadows Property myfProperties() As DevExpress.XtraEditors.Repository.RepositoryItemTextEdit



' Get

' '-- Make sure that the base property is set when being evaluated

' If Not MyBase.fProperties.Equals(_myfProperties) Then MyBase.fProperties = _myfProperties

' Return _myfProperties

' End Get

' Set(ByVal value As DevExpress.XtraEditors.Repository.RepositoryItemTextEdit)

' _myfProperties = value

' MyBase.fProperties = Me._myfProperties

' End Set

'End Property



End Class





Also tried this naming the props _fProperties and fProperties()



In all cases the designer part of the control looks like this :



_

Private Sub InitializeComponent()

Me.fProperties = New DevExpress.XtraEditors.Repository.RepositoryItemTextEdit

CType(Me.fProperties, System.ComponentModel.ISupportInitialize).BeginInit()

Me.SuspendLayout()

'

'fProperties

'

Me.fProperties.Appearance.Options.UseTextOptions = True

Me.fProperties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far

Me.fProperties.AppearanceDisabled.BackColor = System.Drawing.Color.White

Me.fProperties.AppearanceDisabled.ForeColor = System.Drawing.Color.Black

Me.fProperties.AppearanceDisabled.Options.UseBackColor = True

Me.fProperties.AppearanceDisabled.Options.UseForeColor = True

Me.fProperties.AppearanceDisabled.Options.UseTextOptions = True

Me.fProperties.AppearanceDisabled.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far

Me.fProperties.AppearanceFocused.Options.UseTextOptions = True

Me.fProperties.AppearanceFocused.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far

Me.fProperties.AppearanceReadOnly.BackColor = System.Drawing.Color.FromArgb(CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer))

Me.fProperties.AppearanceReadOnly.ForeColor = System.Drawing.Color.Black

Me.fProperties.AppearanceReadOnly.Options.UseBackColor = True

Me.fProperties.AppearanceReadOnly.Options.UseForeColor = True

Me.fProperties.AppearanceReadOnly.Options.UseTextOptions = True

Me.fProperties.AppearanceReadOnly.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far

Me.fProperties.DisplayFormat.FormatType = DevExpress.Utils.FormatType.Numeric

Me.fProperties.EditFormat.FormatType = DevExpress.Utils.FormatType.Numeric

Me.fProperties.Name = "fProperties"

'

'dxTextEdit

'

Me.BusinessObjectEvaluated = True

CType(Me.fProperties, System.ComponentModel.ISupportInitialize).EndInit()

Me.ResumeLayout(False)




I'm obviously missing a basic concept here. Should I be just using a the SF wrapper as a model and creating my own wrapper to remove one layer of abstraction?



Since this involves the SF wrapper for the DevEx control, I'd really appreciate it if you could just tell me specifically, for subclassing this particular SF control, what the best approach to take is.



Since DevEx just released all these controls for free, this might be really useful for a lot of us who haven't been using the DevEx stuff up til now.



TIA










By Charles R Hankey - 2/9/2010

Okay, I tried stealing the code from the SF source code for the TextEdit wrapper, discovering it is just a class, not a component, so it may well be that my fProperties problem was mostly because I was using a component so there was a design file.



But now there is the question of how to change defaults for properties in my subclass (the whole purpose of doing the subclass to start with )



While my rip-off of the SF wrapper fine - subclassing directly from the DevEx control just as the framework does - builds fine and works as part of my own UI dll , I have discovered that the property sheet allows you to set properties but they do not persist into the built class. Same with subclassing the MaskedTextbox.



Looks like the properties must be over ridden ( shadowed ? ) in the code itself to get new defaults.



Interesting.



Also guessing I'm going to find the DevEx control is going to work differently from the SF MaskedTextbox, since the devEx control uses a repository which seems to be basically a data dictionary approach.



Surely a whole lot of other folks have already dealt with this??? Suggestions ?




By Bill Cunnien - 2/10/2010

Hi Charles,



I've extended a TextEdit control. I did not like the gray coloring for disabled and readonly controls (difficult to read). Also, I wanted the control to reflect any binding length limits. So here is what I did:



private void TextEdit_Layout(object sender, LayoutEventArgs e)

{

if ((BusinessObject != null) && (BindingField!=String.Empty))

{

Properties.AppearanceDisabled.BackColor = Color.FromName("Info");

Properties.AppearanceDisabled.ForeColor = Color.FromName("InfoText");

Properties.AppearanceReadOnly.BackColor = Color.FromName("Info");

Properties.AppearanceReadOnly.ForeColor = Color.FromName("InfoText");

}

}



private void TextEdit_Enter(object sender, EventArgs e)

{

if (((MicroFour.StrataFrame.Business.BusinessLayer)BusinessObject).FieldDbTypes[BindingField] == DbType.String)

{

Properties.MaxLength = ((MicroFour.StrataFrame.Business.BusinessLayer)BusinessObject).FieldLengths[BindingField];

}

}




I am pretty sure that I started with an empty component. I have seen that fProperties issue. It's been a while since I designed this control, so I do not remember what I did to work around it.



Have a great day!

Bill
By Charles R Hankey - 2/10/2010

Thanks, Bill.



I'll need to chew this over. I'm assuming this is code inside the inherited class, so I'll approach it that way and see what happens.


By Bill Cunnien - 2/10/2010

Yup...it's in there...



public partial class TextEdit : MicroFour.StrataFrame.UI.Windows.Forms.DevEx.TextEdit



Enjoy!
By Greg McGuffey - 2/10/2010

I'm not entirely following this thread (I haven't chewed on the code much) and I don't use DevX controls, but a couple of thoughts.



First if all you want to do is set some defaults, I believe I'd just try setting them in the constructor of the sub classed control. This might not work, but I think it will. If it didn't I'd likely try overriding the create control method. Any properties you set on designer would then override them.



Second, if that didn't work or wasn't what you wanted, then I'd use shadowing to replace certain properties with my own. This can get tricky when dealing with inheritance and interfaces, but is definitely doable.



Here's some sample code for the first method (just typed in):



Public Class dxTxtC

Inherits MicroFour.StrataFrame.UI.Windows.Forms.DevEx.TextEdit



  Public Sub New()

    '-- Set any defaults

    Me.BackColor = Color.Black

    Me.Mask = "9999.99"

  End Sub



End Class




That should be the entire class! Of course I might be missing what the issue is or some other issue your having but this sort of thing is pretty easy.
By Charles R Hankey - 2/10/2010

If you do that in a subclass New() do you need to call the baseclass constructor first ?
By Bill Cunnien - 2/10/2010

I use trial and error a lot--and the KISS method. I did try placing the property settings in the constructor, but it did not work. Of course, I may have been doing something wrong. It does work in the layout method (a method called during the GUI rendering phase). I get a little pragmatic when it comes to some of this stuff. I kind of wonder what shadowing or interfacing or even overriding would do that my approach does not do. I am open to the possibilities.



Here is the entire class that includes the code posted earlier:



using System;

using System.Data;

using System.Drawing;

using System.Windows.Forms;



namespace Aspire.SubclassedControls.DevEx

{

public partial class TextEdit : MicroFour.StrataFrame.UI.Windows.Forms.DevEx.TextEdit

{

public TextEdit()

{

InitializeComponent();

}



private void TextEdit_Layout(object sender, LayoutEventArgs e)

{

if ((BusinessObject != null) && (BindingField!=String.Empty))

{

Properties.AppearanceDisabled.BackColor = Color.FromName("Info");

Properties.AppearanceDisabled.ForeColor = Color.FromName("InfoText");

Properties.AppearanceReadOnly.BackColor = Color.FromName("Info");

Properties.AppearanceReadOnly.ForeColor = Color.FromName("InfoText");

}

}



private void TextEdit_Enter(object sender, EventArgs e)

{

if (((MicroFour.StrataFrame.Business.BusinessLayer)BusinessObject).FieldDbTypes[BindingField] == DbType.String)

{

Properties.MaxLength = ((MicroFour.StrataFrame.Business.BusinessLayer)BusinessObject).FieldLengths[BindingField];

}

}

}

}

By Greg McGuffey - 2/10/2010

Yeah, I'd call the base class constructor in most cases.



BTW, this does work when I try it with a SF textbox (I set backcolor, Text and IgnoreManageReadonlyState). There was some issues with some properties not immediately showing up, but they were there. Now, where this could get exiting is if the DevX control is messing with stuff after the control is constructed, which it sounds like it is, hence the need to figure out where you can finally manipulate properties without DevX stomping on those changes.



Anyway, it sounds like in this case, you have to use the layout event. Ermm
By Bill Cunnien - 2/10/2010

I just made a minor change to the class...the properties for the back and fore colors were not showing up in the designer, so I did replaced the TextEdit_Layout method with this:



protected override void OnLayout(LayoutEventArgs levent)

{

base.OnLayout(levent);

if ((BusinessObject != null) && (BindingField != String.Empty))

{

Properties.AppearanceDisabled.BackColor = Color.FromName("Info");

Properties.AppearanceDisabled.ForeColor = Color.FromName("InfoText");

Properties.AppearanceReadOnly.BackColor = Color.FromName("Info");

Properties.AppearanceReadOnly.ForeColor = Color.FromName("InfoText");

}

}




That works nicely.
By Bill Cunnien - 2/10/2010

...the DevX control is messing with stuff after the control is constructed...




Yup...I think it has to do with the support for application themes.
By Bill Cunnien - 2/10/2010

I played around with my code a little more. I could not get the properties to behave in the constructor. The BusinessObject is always null at that point. So, I tweaked things and found a better method to perform the rituals. Here is the refined code:





using System;

using System.Data;

using System.Drawing;

using System.Windows.Forms;



namespace Aspire.SubclassedControls.DevEx

{

public partial class TextEdit : MicroFour.StrataFrame.UI.Windows.Forms.DevEx.TextEdit

{

public TextEdit()

{

InitializeComponent();

}



private void TextEdit_BindingContextChanged(object sender, EventArgs e)

{

if ((BusinessObject != null) && (BindingField != String.Empty))

{

Properties.AppearanceDisabled.BackColor = Color.FromName("Info");

Properties.AppearanceDisabled.ForeColor = Color.FromName("InfoText");

Properties.AppearanceReadOnly.BackColor = Color.FromName("Info");

Properties.AppearanceReadOnly.ForeColor = Color.FromName("InfoText");

if (((MicroFour.StrataFrame.Business.BusinessLayer)BusinessObject).FieldDbTypes[BindingField] == DbType.AnsiString)

Properties.MaxLength = ((MicroFour.StrataFrame.Business.BusinessLayer)BusinessObject).FieldLengths[BindingField];

}

}

}

}





It is just a tad cleaner and places the changes at a point where it is better handled...someday my code may be changing the binding (doubt it, but one never knows). Doing it this way I won't have to think about the sub-classed TextEdit control.
By Bill Cunnien - 2/10/2010

Dude.



Just saw it.



AutoAdjustMaxLength property on the SF-extended DevEx TextEdit control.



Testing now.


By Bill Cunnien - 2/10/2010

Nope...it is NOT on the TextEdit control. Alas!

The control was the MicroFour.StrataFrame.UI.Windows.Forms.Textbox.



Got myself all worked up for nothing.



SF, do you think you could add that little nugget into the extended TextEdit control?



Thanks,

Bill
By Trent L. Taylor - 2/10/2010

There is no reason for us to do it when you guys are making the change right now your self. I really don't mind adding it, but no reason to wait on us. Just open the SF source code to see our logic there and then transpose that over to TextEdit control. Smile
By Bill Cunnien - 2/10/2010

Heh heh...you betcha! I am opening the source code, as we speak. I'll be making the change for my own extended control. Easy-peasy. Just thinking about that future developer... Cool
By Charles R Hankey - 2/12/2010

Thanks to Bill and Greg, here's the finished dxTextEdit.vb I was trying to get.



This subclasses the Strataframe DevEx wrapper for the DevExpress TextEdit control which is in the XtraEditors 9.3 now available free from DevExpress. In this case I have set default properties to show the textbox normally when disabled, to treat its display and editing as numeric ( though in my usage I am binding to a character field ) and to right align the numbers, both for display and editing. I will probably further refine this as I learn more about how I want to mask it, but this will demonstrate how to simply take advantage of these controls and subclass variations to save some time setting a lot of properties ( this control is being used 50+ times on a form )



From what Greg said the C# implementation may be different and I will leave that to those better qualified.



Note: I have this control in a UI project of my own subclasses of controls. You need to reference



DevExpress.Data.v9.3

DevExpress.Utils.v9.3

DevExpress.Xtra.Editors.v9.3



in your project.





Imports System

Imports System.Data

Imports System.Drawing

Imports System.Windows.Forms





Public Class dxTextEditC

Inherits MicroFour.StrataFrame.UI.Windows.Forms.DevEx.TextEdit



Public Sub New()



Me.initializecomponent()



End Sub



Private Sub initializecomponent()



Me.BindingFormat = "c"

Me.Properties.Appearance.Options.UseTextOptions = True

Me.Properties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far

Me.Properties.AppearanceDisabled.BackColor = System.Drawing.Color.White

Me.Properties.AppearanceDisabled.ForeColor = System.Drawing.Color.Black

Me.Properties.AppearanceDisabled.Options.UseBackColor = True

Me.Properties.AppearanceDisabled.Options.UseForeColor = True

Me.Properties.AppearanceDisabled.Options.UseTextOptions = True

Me.Properties.AppearanceDisabled.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far

Me.Properties.DisplayFormat.FormatType = DevExpress.Utils.FormatType.Numeric

Me.Properties.EditFormat.FormatType = DevExpress.Utils.FormatType.Numeric



End Sub



End Class
By Charles R Hankey - 2/12/2010

(addendum )



Here's the properties I added to get the TextEdit control working just the way I want it for 2 decimal numbers :





Me.Properties.EditFormat.FormatString = "000,000,000.00"

Me.Properties.Mask.MaskType = DevExpress.XtraEditors.Mask.MaskType.Numeric

Me.Properties.Mask.ShowPlaceHolders = False

Me.Properties.Mask.UseMaskAsDisplayFormat = True





BTW - the subclassed control is created from a humble Class, not a component or user control or DevExControl or whatever else might look tempting in the templates Smile



You will probalby need to close all forms you may have open and rebuild both the UI Class library and the project using the control in order to see what you need in the toolbox.




By Bill Cunnien - 2/12/2010

I just tried this approach (add a constructor with an initialization method) and the control at design-time does not set the properties according to the specifications within the init method. How are you getting this thing to work? I went back to the drawing board on my control (mostly to try out some new ideas), but have gotten back to the same place that I was earlier in the week...setting these properties in a layout method or attaching them to an event (e.g. OnBindingContextChanged).
By Charles R Hankey - 2/12/2010

I'm going to let Greg or Trent step in on this one. Not sure if this works differently in C#.



Looks pretty solid in VB. The code I posted is the entirety of the code in my dxTextEditC.vb class.



When dropped on a form from the toolbox I see all the properties set by default ( including being bolded ) as if I had manually set them as dropping the SF control on.



Are you subclassing the SF wrapper or the Dx control directly?
By Charles R Hankey - 2/12/2010

I noticed your class is defined as partial. Is there some other part to it? What happens if you just translate my code into C# and paste directly into a class.cs ?






By Charles R Hankey - 2/12/2010

Okay, I tested this and it works :

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Drawing;
using System.Windows.Forms;


namespace SFCsharpTest
{

    public class dxTextEditC : MicroFour.StrataFrame.UI.Windows.Forms.DevEx.TextEdit
    {

        public dxTextEditC()
        {

            this.initializecomponent();

        }

        private void initializecomponent()
        {

            this.BindingFormat = "c";
            this.Properties.Appearance.Options.UseTextOptions = true;
            this.Properties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far;
            this.Properties.AppearanceDisabled.BackColor = System.Drawing.Color.White;
            this.Properties.AppearanceDisabled.ForeColor = System.Drawing.Color.Black;
            this.Properties.AppearanceDisabled.Options.UseBackColor = true;
            this.Properties.AppearanceDisabled.Options.UseForeColor = true;
            this.Properties.AppearanceDisabled.Options.UseTextOptions = true;
            this.Properties.AppearanceDisabled.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far;
            this.Properties.DisplayFormat.FormatType = DevExpress.Utils.FormatType.Numeric;
            this.Properties.EditFormat.FormatType = DevExpress.Utils.FormatType.Numeric;

        }

    }
}

I'm attaching a C# project that demonstrates it. 

By Bill Cunnien - 2/12/2010

Thanks for working this out with me. Here is the code that I am using:





using MicroFour.StrataFrame.Business;

using System;

using System.Data;

using System.Drawing;



namespace Aspire.SubclassedControls.DevEx

{

public class TextEdit : MicroFour.StrataFrame.UI.Windows.Forms.DevEx.TextEdit

{

public TextEdit()

{

InitializeControl();

}



private void InitializeControl()

{

if ((BusinessObject != null) && (BindingField != String.Empty))

{

Properties.AppearanceDisabled.BackColor = Color.FromName("Info");

Properties.AppearanceDisabled.ForeColor = Color.FromName("InfoText");

Properties.AppearanceReadOnly.BackColor = Color.FromName("Info");

Properties.AppearanceReadOnly.ForeColor = Color.FromName("InfoText");

if (((BusinessLayer)BusinessObject).FieldDbTypes[BindingField] == DbType.AnsiString)

Properties.MaxLength = ((BusinessLayer)BusinessObject).FieldLengths[BindingField];

}

}

}

}





Looks quite similar to your's, eh?
By Bill Cunnien - 2/12/2010

btw, how do you get your posted code to have the indentation...I knew once how to do it. I've forgotten.
By Charles R Hankey - 2/12/2010

I'm calling initializecomponent and you're calling initializecontrol ?



I just copied the code from my class in vs and pasted between the codesnippet ifcodes. Get it indented in VS is an IDE options thing.
By Bill Cunnien - 2/12/2010

I pulled the appearance properties out of the if-statement. That should take care of that piece of the puzzle. Silly me.



As far as the copy/paste routine....yup, that is what I do. I lose all of the indentation, though.



Perhaps, it a Google Chrome thing.
By Bill Cunnien - 2/12/2010

After all of the fun, I have the following functioning code. It is not much different than where I started before attempting to recreate the SF code.



using MicroFour.StrataFrame.Business;

using System;

using System.Data;

using System.Drawing;



namespace Aspire.SubclassedControls.DevEx

{

public class TextEdit : MicroFour.StrataFrame.UI.Windows.Forms.DevEx.TextEdit

{

public TextEdit()

{

InitializeControl();

}



private void InitializeControl()

{

Properties.AppearanceDisabled.BackColor = Color.FromName("Info");

Properties.AppearanceDisabled.ForeColor = Color.FromName("InfoText");

Properties.AppearanceReadOnly.BackColor = Color.FromName("Info");

Properties.AppearanceReadOnly.ForeColor = Color.FromName("InfoText");

}



protected override void OnBindingContextChanged(EventArgs e)

{

base.OnBindingContextChanged(e);

if ((BusinessObject != null) && (BindingField != String.Empty))

{

if (((BusinessLayer)BusinessObject).FieldDbTypes[BindingField] == DbType.AnsiString)

Properties.MaxLength = ((BusinessLayer)BusinessObject).FieldLengths[BindingField];

}

}

}

}

By Ger Cannoll - 4/1/2011

I am trying to SubClass the DevEx TextEdit control and use the AutoAdjustMaxLength property thats in the SF TextBox.

If this is now included as standard, could someone point me in the right direction, if not , some pointers to where the code is in the SF Source Code, or if somebody has already done it in C#, that would be very useful 
By Edhy Rijo - 4/1/2011

Hi Gerard,

The code is in the TextBox.vb class in the MicroFour StrataFrame UI project's source code.  Take a look at the AutoAdjustMaxLength property and the UpdateMaxLength() method.  You can copy that to the TextEdit class in the SF DevExpress Wrapper source, then recompile and all should be good.
By Ger Cannoll - 4/4/2011

Iseem to have this fully working now..many thanks for all contributions.

I did run into one little issue. When I amneded the SF Source Code for the Inherited textEdit, I 'noticed'  that there was not a MaxLength property. So. ok I wrote the code to 'Create' a new Maxlength property in the subclassed TexEdit control. Of course this made no difference when Iran the code . I then went back and noticed that ther was a Maxlength property already there , but it was in a 'Property' property. Just for lurkers may save a bit of time !!
By Edhy Rijo - 4/5/2011

Hi Gerard,
Thanks for the clarification.  So the property name in DevExpress is Property.MaxLentgh then.
By Ger Cannoll - 4/5/2011

Hi Edhy.

Yes. Most of properties are at the root (e.g. .Font  , .MaximumSize etc.)  but there are a good number then in a section called Properties , and one of these is MaxLength