In the MSDN Visual Basic Power Packs forum, I had an embarrassing reply on a DataRepeater event handling question, in which the user asked why his event handling code did not work. Here is the original question:
“I want to be able to resize a textbox when a user clicks on a textbox within a datarepeater. So the textbox will start out as a single line, but would expand to be multiline to better allow the user to enter text. When the user's mouse leaves the textbox I want it to return to the original size. I tried changing the textbox.height, but nothing happens. Any help is appreciated.”
By looking at the question, I guessed that the user might forget to set the TextBox.MultiLine to true while trying to change the height. Without hesitation, I replied a sample code as below:
Code Sample 1
The user then replied back that the Multiline was not the problem. My code did not work! What is happening?
The DataRepeater control is very intuitive to use and is close to the WYSIWYG experience. However, we need to remember that at run time, the repeater items are cloned using the repeater item template. At design time, we are working on the repeater item template. At run time, several sets of new controls are created to serve as the display of the visible repeater items. Public properties of a control are copied during the cloning process, so do the event handlers. If Button1 is a button in the data repeater item template, code sample 2 below works perfectly.
Code Sample 2
Now let’s take a look on code sample 1, the event handler is cloned and so the code will be called when the mouse enters or leaves the TextBox. However, the code does not work as expected because TextBox1 refers to the template version of the TextBox, which is hidden at run time. The underline TextBox that triggers the event is the cloned version of the TextBox and in the context it is the sender object. So I modified code sample 1 to code sample 3, accessing TextBox1 through the sender object. Tested and worked.
Code Sample 3
I hope this example can help you better understand how the DataRepeater works. Keep in mind that the controls in the repeater item area we work on at design time are used for templates and we should not use them at run time unless you want to change the properties for all repeater items (please see Gavin’s blog: How to Change Appearance of all DataRepeater Items at Run Time). If you need to work on an individual control on a repeater item, use the sender object (as in code sample 1) and then get its parent and siblings if necessary. You can also get access to a DataRepeaterItem, which inherits from Panel control, and then you can access to all the controls in the control’s hierarchical structure. To illustrate this, I would like to change the original forum question to resize the TextBox whenever the current repeater item changes. Code sample 4 below is my solution.
Code Sample 4
In code sample 4, I use DataRepeater.CurrentItem property to get the current DataRepeaterItem and then I use Control.Controls property to return the cloned version of the TextBox1 in the current DataRepeaterItem. Notice that the cloned version of the TextBox is named as “TextBox1” as well, this is allowed at run time as long as it is named uniquely under the parent control (the DataRepeaterItem, in the context). Because there is no CurrentItemIndexChanging event, I use lastModifiedTextBox to remember the last modified TextBox and restore its state when necessary.
Lastly, I would like to point out again (as I have in my previous post) that the DataRepeater control applies a virtualization technique to display the data. So at run time, only a few DataRepeaterItems are created for display purpose. These DataRepeaterItems are reused by all data rows when they enter the visible range. So it is important that if you set a property on a control based on a data row. You may want to reset it appropriately when the data row is out of the visible range as the control will be reused by other data rows. Let me change the task again to only resize the TextBox1 for the first data row, and I will use DataRepeater.DrawItem event this time to get the DataRepeaterItem.
Code Sample 5
In code sample 5 above, if I omit the line to reset the txtBox.Multiline = False, you will see unexpected result after scrolling the data repeater up and down for a few times. See an illustration in picture 1 below.
In conclusion, writing event handler for a control in a DataRepeaterItem can be as straightforward as in a plain form like code sample 2. However, when individual control is needed, you need to make sure to get the correct run time control and remember to reset its property after the use.