none
Why can't I replace a (Designer provided) GUI control? RRS feed

  • Question

  • I have a RichTextBox on my form.  It's Designer provided (rather than dynamically provided) if that might make any difference.  Sometimes a subroutine is supposed to replace the RichTextBox.  So in that subroutine I    Dim newrtb as New RichTextBox

    Then I put some lines of text in newrtb using AppendText.  Finally, because the new text looks very similar to the text in the rtb that I want to replace, I add a last line of text:

    newrtb.AppendText("just to make sure it is working")

    Watching the Text property of newrtb I can see that all of the text is there.  So after adding all of the text I ... (note here that rtbPrices is the rtb I want to replace and, in case it matters, that it exists because of Designer generated code) ...

    newrtb.Location = rtbPrices.Location
    newrtb.ReadOnly = rtbPrices.ReadOnly
    newrtb.Width = rtbPrices.Width
    newrtb.Height = rtbPrices.Height
    rtbPrices = newrtb

    The Watch on rtbPrices shows that it has the right Text content. But after exiting the subroutine and rtb on the GUI still contains the previous content! So I didn't replace it - although I think that's what the last statement above should have done.

    I tried adding a Me.Controls.Add(rtbPrices) - although I didn't think that should be necessary - and it did no good. I also added a Me.Update and that didn't help either.

    Using the debugger I can see that rtbPrices has been changed on return from the subroutine.  But not according to the GUI.

    In desperation, and to be as thorough as possible before seeking your help, I added

    Me.Controls.Remove(rtbPrices)

    before the   rtbPrices = newrtb   statement, and that finally seems to have fixed the problem.  But I don't understand why I had to do that.  And since I have now put so much time into this I'd sure like to understand why that is necessary.

    Thanks,  Bob

     

    Thursday, December 13, 2012 7:21 PM

Answers

  • Assigning rtbPrices = newrtb just overwrites the rtbPrices variable, but does not change the reference within Me.Controls.

    Try the following:

    ' Create the control
    newrtb.Location = rtbPrices.Location
    newrtb.ReadOnly = rtbPrices.ReadOnly
    newrtb.Width = rtbPrices.Width
    newrtb.Height = rtbPrices.Height
    
    ' Prevent the form from performing layout
    Me.SuspendLayout()
    
    ' Remove the existing RTB
    Me.Controls.Remove(rtbPrices)
    
    ' Assign your variable
    rtbPrices = newrtb
    
    ' Now add the NEW rtb into the form:
    Me.Controls.Add(rtbPrices)
    
    ' Resume the layout
    Me.ResumeLayout()


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Thursday, December 13, 2012 7:29 PM
    Moderator
  • Thanks very much Reed.  But I am still missing something.  If I had an array and one of the elements was rtbPrices wouldn't that be changed?  I.E. wouldn't the rtbPrices element in the array point to the new stuff after I did the  rtbPrices = newrtb?

    Does Me.Controls code actually keep a pointer to a pointer and would that explain what I ran into?

    Thanks,  Bob

    No.  Each time you assign a variable to a reference type (a class), you're making a copy of that reference in the new variable.

    For example, say you have a class "Foo".  You could do something like:

    Dim a = new Foo("a")
    Dim b = new Foo("b")
    Dim c = new Foo("c")
    Dim d = new Foo("d")
    
    b = a
    a = c
    
    
    Console.WriteLine(a.Value) ' Prints "c"
    Console.WriteLine(b.Value) ' Prints "a"
    Console.WriteLine(c.Value) ' Prints "c" still
    Console.WriteLine(d.Value) ' Prints "d"
    
    ' Now overwrite the c variable with a copy of the reference in d
    c = d
    
    Console.WriteLine(a.Value) ' Prints "c" still - the a variable is unchanged, and still pointing to the object created with "c" now
    Console.WriteLine(c.Value) ' Prints "d" now, since we changed this reference
    
    

    In your case, when you originally do Me.Controls.Add(rtbPrices), you're adding a copy of the reference assigned in rtbPrices into the ControlCollection.  You can set rtbPrices to anything (including Nothing) and it won't change the reference in Me.Controls.

    That's why, in my code above, you need to remove the old reference from the collection, then assign it, then add the NEW reference into the collection.

    While this is a C# article, I recommend reading the MSDN article on reference types being passed by value: http://msdn.microsoft.com/en-us/library/s6938f28.aspx

    The same thing happens in VB.Net, but it's more explicit in C# (since C# forces ref/out for reference passing and doesn't ever infer it).  It shows what's happening here.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    • Marked as answer by eBob.com Friday, December 14, 2012 3:06 PM
    Thursday, December 13, 2012 8:16 PM
    Moderator
  • Thanks Reed.  I think that I am starting to get it.  I know it's important, but I have just never really dug into all of the implications of ByRef vs. ByVal argument passing.  I seem to usually get away with just not worrying about it. 

    I can't find explicit doc, but I gather from what you said that ControlCollection.Add expects a ByVal argument.  If it specified a ByRef argument would I still have had the problem I had?

    Thanks,  Bob

    Yes, because it's storing the reference.  Being ByRef would allow ControlCollection.Add to change your refernece in the caller, but wouldn't change the behavior in this case.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    • Marked as answer by eBob.com Friday, December 14, 2012 3:05 PM
    Thursday, December 13, 2012 9:44 PM
    Moderator

All replies

  • Assigning rtbPrices = newrtb just overwrites the rtbPrices variable, but does not change the reference within Me.Controls.

    Try the following:

    ' Create the control
    newrtb.Location = rtbPrices.Location
    newrtb.ReadOnly = rtbPrices.ReadOnly
    newrtb.Width = rtbPrices.Width
    newrtb.Height = rtbPrices.Height
    
    ' Prevent the form from performing layout
    Me.SuspendLayout()
    
    ' Remove the existing RTB
    Me.Controls.Remove(rtbPrices)
    
    ' Assign your variable
    rtbPrices = newrtb
    
    ' Now add the NEW rtb into the form:
    Me.Controls.Add(rtbPrices)
    
    ' Resume the layout
    Me.ResumeLayout()


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Thursday, December 13, 2012 7:29 PM
    Moderator
  • Thanks very much Reed.  But I am still missing something.  If I had an array and one of the elements was rtbPrices wouldn't that be changed?  I.E. wouldn't the rtbPrices element in the array point to the new stuff after I did the  rtbPrices = newrtb?

    Does Me.Controls code actually keep a pointer to a pointer and would that explain what I ran into?

    Thanks,  Bob

    Thursday, December 13, 2012 8:08 PM
  • Thanks very much Reed.  But I am still missing something.  If I had an array and one of the elements was rtbPrices wouldn't that be changed?  I.E. wouldn't the rtbPrices element in the array point to the new stuff after I did the  rtbPrices = newrtb?

    Does Me.Controls code actually keep a pointer to a pointer and would that explain what I ran into?

    Thanks,  Bob

    No.  Each time you assign a variable to a reference type (a class), you're making a copy of that reference in the new variable.

    For example, say you have a class "Foo".  You could do something like:

    Dim a = new Foo("a")
    Dim b = new Foo("b")
    Dim c = new Foo("c")
    Dim d = new Foo("d")
    
    b = a
    a = c
    
    
    Console.WriteLine(a.Value) ' Prints "c"
    Console.WriteLine(b.Value) ' Prints "a"
    Console.WriteLine(c.Value) ' Prints "c" still
    Console.WriteLine(d.Value) ' Prints "d"
    
    ' Now overwrite the c variable with a copy of the reference in d
    c = d
    
    Console.WriteLine(a.Value) ' Prints "c" still - the a variable is unchanged, and still pointing to the object created with "c" now
    Console.WriteLine(c.Value) ' Prints "d" now, since we changed this reference
    
    

    In your case, when you originally do Me.Controls.Add(rtbPrices), you're adding a copy of the reference assigned in rtbPrices into the ControlCollection.  You can set rtbPrices to anything (including Nothing) and it won't change the reference in Me.Controls.

    That's why, in my code above, you need to remove the old reference from the collection, then assign it, then add the NEW reference into the collection.

    While this is a C# article, I recommend reading the MSDN article on reference types being passed by value: http://msdn.microsoft.com/en-us/library/s6938f28.aspx

    The same thing happens in VB.Net, but it's more explicit in C# (since C# forces ref/out for reference passing and doesn't ever infer it).  It shows what's happening here.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    • Marked as answer by eBob.com Friday, December 14, 2012 3:06 PM
    Thursday, December 13, 2012 8:16 PM
    Moderator
  • Thanks Reed.  I think that I am starting to get it.  I know it's important, but I have just never really dug into all of the implications of ByRef vs. ByVal argument passing.  I seem to usually get away with just not worrying about it. 

    I can't find explicit doc, but I gather from what you said that ControlCollection.Add expects a ByVal argument.  If it specified a ByRef argument would I still have had the problem I had?

    Thanks,  Bob

    Thursday, December 13, 2012 9:31 PM
  • Thanks Reed.  I think that I am starting to get it.  I know it's important, but I have just never really dug into all of the implications of ByRef vs. ByVal argument passing.  I seem to usually get away with just not worrying about it. 

    I can't find explicit doc, but I gather from what you said that ControlCollection.Add expects a ByVal argument.  If it specified a ByRef argument would I still have had the problem I had?

    Thanks,  Bob

    Yes, because it's storing the reference.  Being ByRef would allow ControlCollection.Add to change your refernece in the caller, but wouldn't change the behavior in this case.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    • Marked as answer by eBob.com Friday, December 14, 2012 3:05 PM
    Thursday, December 13, 2012 9:44 PM
    Moderator