Dean Hiller
Dean Hiller

Reputation: 20210

TextBox.Text property not being set when I set it?

This is the weirdest thing as it works for me in some code in not other. The following code is in a class that subclasses TextBox (NOTE: I don't know if it matters but I subclass the Text property to set/get from a private field _realText)

In the below code, the first base.Text = this.RealText works fine!!! We also set it inside that method MaskData() as well and it works!!!! so why in the world does it not work in the if(!field.isSecure) section? (look at the logs for what I mean). I tried adding Invalidate(), Update() after base.Text=temp but that didn't help.

Code:

    private void SetupTextInBox()
    {
        if (isInManualMode)
        {
            this.ReadOnly = false;
            base.Text = this.RealText;
        }
        else
        {
            this.ReadOnly = true;
            if (!field.IsSecure)
            {
                string temp = this.RealText;
                log.Info("This field is not secure so show it. field=" + field.Variable + " real='" + temp+"'");
                base.Text = temp;
                log.Info("text value='" + base.Text+"'");
                return;
            }
            else
            {
                MaskData();
            }
        }
    }

Logs

2012-06-30 07:15:51,468 [1] INFO  AlpineAccess.Plugins.SecureTalkPlugin.SecureTextControl (null) - This field is not secure so show it. field=1.acc real='2222' 
2012-06-30 07:15:51,468 [1] INFO  AlpineAccess.Plugins.SecureTalkPlugin.SecureTextControl (null) - text value=''

EDIT: Note that this method is ALWAYS called from the same thread. It comes from server notifications telling us a tone on a phone somewhere else has been pressed, AND then that thread calls a BeginInvoke to put it in the GUI/controls thread or whatnot.

code just upstream from the above method is

    public void AppendDTMFDigit(string digit)
    {
        log.Info("specified="+field.MaxSpecified+" someone appending dtmf digit on field=" + field.Variable+" fieldMax="+field.Max+" len="+RealText.Length);
        if (field.MaxSpecified && this.RealText.Length >= field.Max)
            return; //shortcut out since we can't exceed max digits

        BeginInvoke(new MethodInvoker(delegate()
        {
            this.RealText = this.RealText + digit;
            log.Info("new realtext=" + this.RealText);
            SetupTextInBox();
        }
        )); 
    }

MORE INFO: If I change ALL my client code to stop using Text property and use RealText property, and then stop overriding the Text property, it then works fine. (Obviously I don't want that though as now I can't just change from my control to TextBox and back easily without changing lots of client code referring to the RealText property....ugh, may have to live with that though....seems like some kind of odd bug.

MORE INFO: got debugger stepping into it and this is very odd.

2 very odd things.

  1. It steps into the getter, not the setter???
  2. It steps into MY Text property instead of TextBox's Text Property.

grrrrr, why would that be...sounds like a major bug, right? I mean, base.Text should refer to superclass's base, correct? – Dean Hiller just now edit

Adding Text method property code

    public override string Text
    {
        get
        {
            return RealText;
        }
        set
        {
            if (value == null)
                throw new ArgumentException("Not allowed to set RealText to null.  Set to empty string instead");
            RealText = value;
        }
    }

Upvotes: 1

Views: 4890

Answers (2)

Charleh
Charleh

Reputation: 14002

Ok you've overridden Text - and you have THIS on one of your lines:

RealText = Text;

This will call the most derived 'Text' property (since you didn't specify base) and will call your overridden 'Text' on the current control

Which means you are setting RealText to itself

e.g.

 void SecureTextBox_TextChanged(object sender, EventArgs e)
 {
     if (isInManualMode) //make sure if in manual mode, we keep changes up to date in realText field
     RealText = Text; <---- **THIS CALLS THE OVERIDDEN 'Text' BELOW**

     ... snip

calls

public override string Text 
{
    get { return RealText; }
}

Which is a bit...nuts!

Did you mean to do:

 void SecureTextBox_TextChanged(object sender, EventArgs e)
 {
     if (isInManualMode) //make sure if in manual mode, we keep changes up to date in realText field
     RealText = base.Text; <--- THIS?

Check your code carefully, I bet this is one of your issues

Also it might be useful to change your naming conventions a little... e.g. I tend to use an underscore for private/protected fields

private string _realText;

instead of caps which is for properties

private string RealText;

Don't usually expose public fields, usually use properties but when I do I tend to use the same casing as I do for properties

This is so that it's easier to differentiate between properties and fields in your code and makes debugging a bit easier

Upvotes: 1

Dean Hiller
Dean Hiller

Reputation: 20210

Full source for this user control that doesn't work unless you comment out the Text property is here (You would have to use a RealText property and expose that one to clients instead because base.Text does not appear to work properly).

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using log4net;
using IntraNext.Win32.Config;

namespace AlpineAccess.Plugins.SecureTalkPlugin
{

    public partial class SecureTextBox : TextBox
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(SecureTextControl));

        private static readonly Color COLOR_FOCUSED = Color.Yellow;
        private static readonly Color COLOR_FOCUSED_MANUALMODE = Color.PaleGreen;

        private FocusEvent focusListener;
        private Field field;
        private bool isInManualMode;
        private EventHandler textChanged;
        private string RealText;

        public SecureTextBox()
        {
            InitializeComponent();
            RealText = "";
        }

        internal void Initialize(Field field, FocusEvent focusList, EventHandler textChanged)
        {
            this.focusListener = focusList;
            this.textChanged = textChanged;
            this.field = field;

            this.Enter += new EventHandler(SecureTextBox_Enter);
            this.Leave += new EventHandler(SecureTextBox_Leave);
            this.TextChanged += new EventHandler(SecureTextBox_TextChanged);

            MenuItem mnuItemNew = new MenuItem();
            mnuItemNew.Text = "&Clear";

            //THIS textbox HAS a context menu ALREADY BUT here we assign a new context menu to
            //our text box to replace the old one which had cut, paste etc. that we don't want
            //the agent using...
            this.ContextMenu = new ContextMenu();
            this.ContextMenu.MenuItems.Add(mnuItemNew);

            mnuItemNew.Click += new EventHandler(clear_Click);

            SwitchModes();
        }

        void SecureTextBox_TextChanged(object sender, EventArgs e)
        {
            if(isInManualMode) //make sure if in manual mode, we keep changes up to date in realText field
                RealText = Text;
            textChanged(sender, e);
        }

        void clear_Click(object sender, EventArgs e)
        {
            ClearAll();
        }

        internal void SetManualMode(bool inManual)
        {
            if (isInManualMode == inManual)
                return; //we don't care if there is no change so return;

            isInManualMode = inManual;
            SwitchModes();
        }

        void SecureTextBox_Leave(object sender, EventArgs e)
        {
            log.Info("exiting=" + field.Variable);
            focusListener(field.Variable, false, this.RealText);
            BeginInvoke(new MethodInvoker(delegate()
            {
                ChangeBackground();
            }
            ));            
        }

        void SecureTextBox_Enter(object sender, EventArgs e)
        {
            log.Info("entering=" + field.Variable );
            focusListener(field.Variable, true, this.RealText);
            BeginInvoke(new MethodInvoker(delegate()
            {
                ChangeBackground();
            }
            ));
        }

        private void SwitchModes()
        {
            SetupTextInBox();
            ChangeBackground();
        }

        private void SetupTextInBox()
        {
            if (isInManualMode)
            {
                this.ReadOnly = false;
                base.Text = RealText;
            }
            else if (!field.IsSecure)
            {
                this.ReadOnly = true;
                string temp = RealText;
                base.Text = temp;
                Invalidate();
                log.Info("txt=" + base.Text + " temp=" + temp);
            }
            else //not manual mode and IsSecure so mask it and make it readonly
            {
                this.ReadOnly = true;
                MaskData();
            }
        }

        private void MaskData()
        {
            log.Debug("mask=" + this.field.NumBeginDigitsToMaskSpecified + " num=" + field.NumBeginDigitsToMask + " txtLen=" + RealText.Length);
            int numDigitsToMask = RealText.Length;
            if (this.field.NumBeginDigitsToMaskSpecified && this.field.NumBeginDigitsToMask < RealText.Length)
            {
                int numDigits = this.field.NumBeginDigitsToMask;
                string maskedPart = "".PadLeft(numDigits, '●');
                string unmasked = RealText.Substring(numDigits);
                string full = maskedPart + unmasked;
                base.Text = full;
            }
            else
            {
                log.Debug("masking all digits");
                base.Text = "".PadLeft(RealText.Length, '●');
            }
        }

        private void ChangeBackground()
        {
            if (isInManualMode)
                SetManualModeColor();
            else
                SetNonManualModeColor();
        }

        private void SetNonManualModeColor()
        {
            if (this.Focused)
                this.BackColor = COLOR_FOCUSED;
            else
                this.BackColor = Control.DefaultBackColor;
        }

        private void SetManualModeColor()
        {
            if (this.Focused)
                this.BackColor = COLOR_FOCUSED_MANUALMODE;
            else
                this.BackColor = Control.DefaultBackColor;
        }

        public void AppendDTMFDigit(string digit)
        {
            log.Info("manualmode="+isInManualMode+" specified=" + field.MaxSpecified + " someone appending dtmf digit on field=" + field.Variable + " fieldMax=" + field.Max + " len=" + RealText.Length);

            if (isInManualMode)
                return;
            else if (field.MaxSpecified && RealText.Length >= field.Max)
                return; //shortcut out since we can't exceed max digits

            BeginInvoke(new MethodInvoker(delegate()
            {
                RealText = RealText + digit;
                SetupTextInBox();
            }
            )); 
        }

        internal void ClearAll()
        {
            log.Info("Cleared textbox for =" + field.Variable);
            base.Text = "";
            RealText = "";
            SetError("");
        }

        public override string Text
        {
            get
            {
                return RealText;
            }
            set
            {
                if (value == null)
                    throw new ArgumentException("Not allowed to set RealText to null.  Set to empty string instead");
                RealText = value;
            }
        }

        /**
         * Set to "" to clear the error or set anything to make valid
         */
        public void SetError(string error)
        {
            if (!this.IsHandleCreated)
                return;

            SecureTextBox box = this;
            //set to "" to clear the error
            BeginInvoke(new MethodInvoker(delegate()
            {
                errorProvider1.SetError(box, error);
            }));
        }
    }
}

Upvotes: 1

Related Questions