<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Digital Primates&#187; flex-actionscript</title>
	<atom:link href="http://www.digitalprimates.net/author/category/flex-actionscript/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.digitalprimates.net</link>
	<description>Flex Development and Consulting</description>
	<lastBuildDate>Thu, 12 Apr 2012 15:26:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>Making a custom datagrid scrollbar? I hope the width=16&#8230;</title>
		<link>http://www.digitalprimates.net/author/strict-inequality/2011/02/02/making-a-custom-datagrid-scrollbar-i-hope-the-width16/</link>
		<comments>http://www.digitalprimates.net/author/strict-inequality/2011/02/02/making-a-custom-datagrid-scrollbar-i-hope-the-width16/#comments</comments>
		<pubDate>Wed, 02 Feb 2011 22:29:16 +0000</pubDate>
		<dc:creator>strict-inequality</dc:creator>
				<category><![CDATA[flex-actionscript]]></category>

		<guid isPermaLink="false">http://www.digitalprimates.net/strict-inequality/?p=55</guid>
		<description><![CDATA[Flex has a lot of great facilities for styling and skinning, including custom scrollbars. Unfortunately, I&#8217;ve discovered that Flex really wants your vertical scrollbars to be 16 pixels wide. When it isn&#8217;t, Flex can make some strange layout decisions when &#8230; <a href="http://www.digitalprimates.net/author/strict-inequality/2011/02/02/making-a-custom-datagrid-scrollbar-i-hope-the-width16/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[Flex has a lot of great facilities for styling and skinning, including custom scrollbars. Unfortunately, I&#8217;ve discovered that Flex really wants your vertical scrollbars to be 16 pixels wide. When it isn&#8217;t, Flex can make some strange layout decisions when first putting it on screen.

<span id="more-680"></span>

for example, check out this (very fancy) datagrid:

<a href="http://www.digitalprimates.net/wp-content/uploads/2011/02/start.png"><img class="alignnone size-full wp-image-56" title="start" src="http://www.digitalprimates.net/wp-content/uploads/2011/02/start.png" alt="" width="499" height="196" /></a>

I made an 8-pixel wide vertical scrollbar, complete with a lovely red and purple coloring, and used it in my datagrid. Obviously, there&#8217;s a couple things wrong. First, there&#8217;s an ugly white gap between my content and my fancy scrollbar. Second, it looks like the content in my 2nd column has a different left padding than the content in my 1st column. Finally, the header area above the scrollbar didn&#8217;t render correctly.

To see what kind of scrolling behavior it had, I clicked on my &#8220;down arrow&#8221;:

<a href="http://www.digitalprimates.net/wp-content/uploads/2011/02/scroll_down.png"><img class="alignnone size-full wp-image-57" title="scroll_down" src="http://www.digitalprimates.net/wp-content/uploads/2011/02/scroll_down.png" alt="" width="499" height="196" /></a>

A couple things sorted themselves out: the 8-pixel white gap is gone, the header looks right, and the content in the last row (Roger McBob) looks proper. As a final test, I sorted the 2nd column and got this:

<a href="http://www.digitalprimates.net/wp-content/uploads/2011/02/sorted.png"><img class="alignnone size-full wp-image-58" title="sorted" src="http://www.digitalprimates.net/wp-content/uploads/2011/02/sorted.png" alt="" width="499" height="196" /></a>

Now all the content looks properly placed. So what the heck is going on here?

This bad behavior can be traced back to ScrollControlBase.as, in setScrollBarProperties() and createVScrollBar().

setScrollBarProperties() is going to be invoked from DataGrid&#8217;s updateDisplayList(). What we&#8217;re really interested in is whether the content area (for the first and last names) has changed size, which is tracked in the class variable scrollAreaChanged. If that scroll area has changed, then setScrollBarProperties() is going to call updateDisplayList().

What&#8217;s that? Aren&#8217;t we already in a call to updateDisplayList(), and isn&#8217;t that call going to re-execute DataGrid.updateDisplayList() and get us right back in here?

Yes. Yes it will.

The other thing that setScrollBarProperties() is doing is deciding whether or not it needs to create or destroy scrollbars. For the situation I&#8217;ve created, it will need to create a vertical scrollbar. And it does so with a call to createVScrollBar(). That new scrollbar, before its measure() can get called, is going to report a width of 16, which is the default. And that value will be used to determine not only the overall content area (c.f. that ugly white gap), but also how the content therein is placed (c.f. the 2nd column content being off a few pixels). It&#8217;s going to do all this before we can actually measure the scrollbar, even when we re-enter the method. It&#8217;s not until we start interacting with the control (scrolling, sorting) that we get back into setScrollBarProperties() after the scrollbar has finally been measured. As we can see, it then starts laying out properly.

It&#8217;s great that the framework handles getting us back into that method, but I can&#8217;t help thinking that it would be better served through an invalidation cycle, rather than a recursive call. If that happened, we&#8217;d go through a proper measure cycle and the content area would be sized correctly without having to touch the control.

Funnily enough, in createVScrollBar() (I&#8217;m looking at the Flex SDK 3.5 here), there&#8217;s this code:
<p style="padding-left: 30px;">//need verticalScrollBar.validateNow()?</p>
Given the way the code is written now, I&#8217;d say the answer is yes. That would force a measure and our datagrid would render correctly the first time. Maybe that should have been explored more before simply commenting it out.]]></content:encoded>
			<wfw:commentRss>http://www.digitalprimates.net/author/strict-inequality/2011/02/02/making-a-custom-datagrid-scrollbar-i-hope-the-width16/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Datagrid item editors and dynamic behavior (part 3/3)</title>
		<link>http://www.digitalprimates.net/author/strict-inequality/2008/03/12/datagrid-item-editors-and-dynamic-behavior-part-33/</link>
		<comments>http://www.digitalprimates.net/author/strict-inequality/2008/03/12/datagrid-item-editors-and-dynamic-behavior-part-33/#comments</comments>
		<pubDate>Wed, 12 Mar 2008 04:37:57 +0000</pubDate>
		<dc:creator>strict-inequality</dc:creator>
				<category><![CDATA[flex-actionscript]]></category>

		<guid isPermaLink="false">http://173.236.48.58/~digit329/strict-inequality/?p=13</guid>
		<description><![CDATA[we&#8217;re ready to wrap up our functionality now. in part 1, we defined our problem (validated error reporting in a datagrid cell, plus dynamic validation characteristics) and made a first attempt at a solution. in part 2, we improved our &#8230; <a href="http://www.digitalprimates.net/author/strict-inequality/2008/03/12/datagrid-item-editors-and-dynamic-behavior-part-33/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[we&#8217;re ready to wrap up our functionality now.

in <a href="http://173.236.48.58/~digit329/strict-inequality/?p=9">part 1</a>, we defined our problem (validated error reporting in a datagrid cell, plus dynamic validation characteristics) and made a first attempt at a solution.

in <a href="http://173.236.48.58/~digit329/strict-inequality/?p=11">part 2</a>, we improved our error handling in the cells.

now, in part 3, we&#8217;ll expand our validation logic beyond valid numbers to range checking based on other XML data in the row. this will fulfill our task of creating item editors with dynamic behavior.
<span id="more-679"></span>
first, we&#8217;ll change our WeightEditor to define the ranges and accept a 2nd parameter in its validate() method. this parameter specifies the weight class against which we want to validate. its value will come from the row, and our custom ITEM_EDIT_END handler will be able to access it.

here&#8217;s our new method signature:
<code>public function validate(value:String,
weightClass:String) : String</code>

in our handler, the old code looked like this:
<code>var editor:WeightEditor2 = WeightEditor2
(weigh_in.itemEditorInstance);</code>

<code>var userEnteredValue:String = editor.text;

</code>

<code>var errorMsg:String = editor.validate
(userEnteredValue);</code>
and the new code is this:
<code>var weightClass:String = event.itemRenderer.data.weightClass;
var editor:WeightEditor3 = WeightEditor3(weigh_in.itemEditorInstance);
var userEnteredValue:String = editor.text;
var errorMsg:String = editor.validate(userEnteredValue, weightClass);
</code>
note that our handler doesn&#8217;t do anything with the weightClass value except hand it to the validation method. our itemEditor and handler are fairly coupled to each other, but at least we&#8217;ve kept the validation logic in the editor and not spread it across both places.

the handler logic is the same: if we fail our (now improved) validation, we&#8217;ll prevent the datagrid from moving out of the cell and allow the user to enter another value.

here&#8217;s the code for the new item editor, WeightEditor3.mxml:

<code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;mx:TextInput restrict="1234567890,." xmlns:mx="<a href="http://www.adobe.com/2006/mxml" target="_blank">http://www.adobe.com/2006/mxml</a>"&gt;
&lt;mx:Script&gt;
&lt;![CDATA[
import mx.formatters.NumberFormatter;
import mx.events.ValidationResultEvent;
import mx.validators.NumberValidator;
protected static const A_LOW:Number = 0;
protected static const A_HIGH:Number = 99;
protected static const B_LOW:Number = 100;
protected static const B_HIGH:Number = 135;
protected static const C_LOW:Number = 136;
protected static const C_HIGH:Number = 169;
protected static const D_LOW:Number = 170;
protected static const D_HIGH:Number = 210;
/**
* This method is invoked when the user enters a value in the weight column
* of the weigh_in datagrid. Because we're allowing the user to handle validation,
* we'll always return what the user typed in, right or wrong. By sending back a
* value which will eventually fail validation, that allows us to keep the value
* in the field when the errortip is drawn around it.
*/
override public function get data() : Object
{
var value:String = text;
<em> // no need to validate here, since we don't act upon it. but if we did</em> <em> // need to validate to make some kind of decision, then we'll end up</em> <em> // validating twice.</em>
return value;
}
/**
* This is our validation method. We pass validation if the user has
* entered a non-negative decimal. This is a public function so that we can
* re-use this validation outside of the editor.
*
* @param value         The user-entered value
* @param weightClass   From the XML, the weight class of this particular wrestler
*
* @return null if we pass validation, otherwise we return the error string
*/
public function validate(value:String, weightClass:String) : String
{
var validator:NumberValidator = new NumberValidator();
validator.allowNegative = false;
validator.required = false;
var result:ValidationResultEvent = validator.validate(value);
if (result.type != ValidationResultEvent.VALID)
return result.message;
var errorMsg:String = validateWeightClass(value, weightClass);
return errorMsg;
}
/**
* This is a validation helper method. It checks to ensure the entered weight
* is within range. The weight can be equal on the low end but must be lower than
* the high end.
*
* @param value         The user-entered value
* @param weightClass   From the XML, the weight class of this particular wrestler
*
* @return null if we pass validation, otherwise we return the error string
*/
protected function validateWeightClass(value:String, weightClass:String) : String
{
var lowWeight:Number = 0;
var highWeight:Number = 1000;
switch (weightClass)
{
case 'A' :
lowWeight = A_LOW;
highWeight = A_HIGH;
break;
case 'B' :
lowWeight = B_LOW;
highWeight = B_HIGH;
break;
case 'C' :
lowWeight = C_LOW;
highWeight = C_HIGH;
break;
case 'D' :
lowWeight = D_LOW;
highWeight = D_HIGH;
break;
default:
break;
}
var weight:Number = Number(value);
if ((weight &gt;= lowWeight) &amp;&amp; (weight &lt; highWeight))
return null;
var errorMsg:String = "This wrestler's weight is out of his weight class. His weight must be between "
+ lowWeight + ' and ' + highWeight + ' pounds.';
return errorMsg;
}
]]&gt;
&lt;/mx:Script&gt;
&lt;/mx:TextInput&gt;</code>

click on the image to run the final app. test the new validation by entering valid numbers which are outside the weight range.

<a href="http://blogs.digitalprimates.net/strictinequality/samples/wrestling/wrestling3.html">
<img src="http://blogs.digitalprimates.net/strictinequality/images/blog/wrestling.jpg" alt="" />
</a>]]></content:encoded>
			<wfw:commentRss>http://www.digitalprimates.net/author/strict-inequality/2008/03/12/datagrid-item-editors-and-dynamic-behavior-part-33/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Datagrid item editors and dynamic behavior (part 2/3)</title>
		<link>http://www.digitalprimates.net/author/strict-inequality/2008/03/03/datagrid-item-editors-and-dynamic-behavior-part-23/</link>
		<comments>http://www.digitalprimates.net/author/strict-inequality/2008/03/03/datagrid-item-editors-and-dynamic-behavior-part-23/#comments</comments>
		<pubDate>Mon, 03 Mar 2008 19:09:52 +0000</pubDate>
		<dc:creator>strict-inequality</dc:creator>
				<category><![CDATA[flex-actionscript]]></category>

		<guid isPermaLink="false">http://173.236.48.58/~digit329/strict-inequality/?p=11</guid>
		<description><![CDATA[picking up from where we left off in part one, now we want to to improve our user notification when they enter a bad value in our datagrid. previously, we jumped out of the cell and left it blank. ideally, &#8230; <a href="http://www.digitalprimates.net/author/strict-inequality/2008/03/03/datagrid-item-editors-and-dynamic-behavior-part-23/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[picking up from where we left off in <a href="http://173.236.48.58/~digit329/strict-inequality/?p=9">part one</a>, now we want to to improve our user notification when they enter a bad value in our datagrid.

previously, we jumped out of the cell and left it blank. ideally, we want our cell, whose itemEditor is a derived class of TextInput, to behave like a TextInput with bad data. i.e. we would like a red border and an errortip.<span id="more-678"></span>to get this behavior, we must have an understanding of how DataGrid works with regard to item editors and how to short-circuit its default behavior. by default, once you leave a datagrid cell, the DataGrid destroys the item editor and looks for the next cell to edit. this is why we got the &#8220;leave the cell&#8221; behavior in our first iteration.

when a user leaves a datagrid cell, which dispatches the ITEM_EDIT_END event, the DataGrid invokes its handler method: itemEditorItemEditEndHandler. if the user has not canceled the edit, that method pulls the data out of the item editor (based on the value of editorDataField), puts the value in the cell, and under normal circumstances destroys the itemEditor.

what we want to do is call our own handler when we get the ITEM_EDIT_END event. we do this by specifying a value for the itemEditEnd property on our DataGrid, like this:

<code>&lt;mx:DataGrid id="weigh_in" editable="true" dataProvider="{wrestlers}" itemEditEnd="processEditedItem(event)" &gt;</code>

when our handler is called, we need to decide whether or not the value is good. if it is, we&#8217;ll simply exit and let the framework handle the rest (putting the value into the cell, destroying the item editor, and finding the next cell to edit).

if the value is bad, however, we need to prevent that default behavior, by calling the preventDefault() method on the DataGridEvent passed to our handler:

<code>event.preventDefault();</code>

though the DataGrid&#8217;s ITEM_EDIT_END handler is still invoked, this prevents the DataGrid from leaving the cell. however, it still destroys the item editor, so we need to be sure we&#8217;re done with the item editor before that happens.

a moment ago, i said that we needed to take action if our value is no good. how do we know if it&#8217;s good or not? what would be ideal is if our item editor could somehow indicate that to the framework so that our event could tell us (a &#8216;valid&#8217; flag, perhaps?), but there&#8217;s no framework support for that.

so i did something rather ugly but necessary: i made the validate() method in our itemEditor public so that it could be invoked from our custom handler. and then i did something even uglier: i overloaded the return of that validation method to return the error string if it&#8217;s in error, and a null otherwise.

in general, i&#8217;m not at all a fan of overloading a return type for such double meaning, but it seemed to be the most straightforward way of getting done what was needed without making multiple changes to the framework to support a more elegant solution.

so our approach in the handler is to grab a reference to our itemEditor and invoke its validation method from there. that will give us the information we need to determine if we need to stay in our cell our move on to the next.

here is our handler:

<code>protected function processEditedItem(event:DataGridEvent) : void
{
if (event.reason == DataGridEventReason.CANCELLED)
return;
if (event.dataField != 'weight')
return;
<em> // we need a reference to our style of item editor so we can call</em> <em> // its validation method.</em>
var editor:WeightEditor2 = WeightEditor2(weigh_in.itemEditorInstance);
var userEnteredValue:String = editor.text;
var errorMsg:String = editor.validate(userEnteredValue);
<em> // if we didn't validate, we'll set our errorString and call preventDefault()</em> <em> // so the datagrid knows to keep us in the same cell</em>
if (errorMsg)
{
editor.errorString = errorMsg;
event.preventDefault();
}
}</code>
we want to bug out straightaway if the user has canceled the edit (by hitting escape) or if our handler was called for a column in which we&#8217;re not interested. in both cases, the framework will handle the work.

otherwise, we&#8217;ll grab a reference to our itemEditor, call its validation method with the data the user entered, and if we get back an error string from the validator, we&#8217;ll use that as the error tip and call preventDefault.

here&#8217;s the code:

wresting2.mxml

<code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;mx:Application xmlns:mx="<a href="http://www.adobe.com/2006/mxml" target="_blank">http://www.adobe.com/2006/mxml</a>" layout="vertical" &gt;
&lt;mx:Script&gt;
&lt;![CDATA[
import mx.events.DataGridEventReason;
import mx.events.DataGridEvent;
import mx.formatters.NumberFormatter;
/**
* Our label function. It formats entered weights as proper numbers.
*
* @param item      The user-entered weight
* @param column   The column in which the weight was entered
*
* @return   A formatted weight
*/
protected function decimalLabel(item:Object, column:DataGridColumn) : String
{
var formatter:NumberFormatter = new NumberFormatter();
var weight:String = item.weight;
var formattedNumber:String = formatter.format(weight);
return formattedNumber;
}
/**
* This is our method for processing ITEM_EDIT_END events on our
* weight column. We'll grab weight and validate.
*
* If the weight is a valid number, we'll let the framework handle the
* rest of the process, such as finding the next cell to edit and calling
* our label function.
*
* If the weight is not a valid number, we'll set our errorTip to the error
* message we got back from validation, and ensure the framework does not
* continue processing so that the user will stay in the same cell and can
* enter a new value.
*
* @param event      Our ITEM_EDIT_END event.
*/
protected function processEditedItem(event:DataGridEvent) : void
{
if (event.reason == DataGridEventReason.CANCELLED)
return;
if (event.dataField != 'weight')
return;
<em>// we need a reference to our style of item editor so we can call</em> <em> // its validation method.</em>
var editor:WeightEditor2 = WeightEditor2(weigh_in.itemEditorInstance);
var userEnteredValue:String = editor.text;
var errorMsg:String = editor.validate(userEnteredValue);
<em>// if we didn't validate, we'll set our errorString and call preventDefault()</em> <em> // so the datagrid knows to keep us in the same cell</em>
if (errorMsg)
{
editor.errorString = errorMsg;
event.preventDefault();
}
}
]]&gt;
&lt;/mx:Script&gt;
&lt;mx:XMLList id="wrestlers" xmlns=""&gt;
&lt;wrestler&gt;
&lt;name&gt; Bob Smith &lt;/name&gt;
&lt;weightClass&gt; A &lt;/weightClass&gt;
&lt;weight&gt; &lt;/weight&gt;
&lt;/wrestler&gt;
&lt;wrestler&gt;
&lt;name&gt; Tim McLarry &lt;/name&gt;
&lt;weightClass&gt; B &lt;/weightClass&gt;
&lt;weight&gt; &lt;/weight&gt;
&lt;/wrestler&gt;
&lt;wrestler&gt;
&lt;name&gt; Joe Doug &lt;/name&gt;
&lt;weightClass&gt; C &lt;/weightClass&gt;
&lt;weight&gt; &lt;/weight&gt;
&lt;/wrestler&gt;
&lt;wrestler&gt;
&lt;name&gt; Larry McTim &lt;/name&gt;
&lt;weightClass&gt; D &lt;/weightClass&gt;
&lt;weight&gt; &lt;/weight&gt;
&lt;/wrestler&gt;
&lt;/mx:XMLList&gt;
&lt;mx:DataGrid id="weigh_in" editable="true" dataProvider="{wrestlers}" itemEditEnd="processEditedItem(event)" &gt;
&lt;mx:columns&gt;
&lt;mx:DataGridColumn id="wrestler_name" headerText="Name" editable="false" dataField="name" /&gt;
&lt;mx:DataGridColumn id="weight_class" headerText="Class" editable="false" dataField="weightClass" /&gt;
&lt;mx:DataGridColumn id="weight" headerText="Weight" editable="true" dataField="weight"
itemEditor="WeightEditor2" editorDataField="data" labelFunction="decimalLabel" /&gt;
&lt;/mx:columns&gt;
&lt;/mx:DataGrid&gt;
&lt;mx:Form &gt;
&lt;mx:FormHeading label="Weight Classes" /&gt;
&lt;mx:FormItem label="A" &gt;
&lt;mx:Label text="0-99 lbs." /&gt;
&lt;/mx:FormItem&gt;
&lt;mx:FormItem label="B" &gt;
&lt;mx:Label text="100-135 lbs." /&gt;
&lt;/mx:FormItem&gt;
&lt;mx:FormItem label="C" &gt;
&lt;mx:Label text="136-169 lbs." /&gt;
&lt;/mx:FormItem&gt;
&lt;mx:FormItem label="D" &gt;
&lt;mx:Label text="170-210 lbs." /&gt;
&lt;/mx:FormItem&gt;
&lt;/mx:Form&gt;
&lt;/mx:Application&gt;</code>

WeightEditor2.mxml:

<code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;mx:TextInput restrict="1234567890,." xmlns:mx="<a href="http://www.adobe.com/2006/mxml" target="_blank">http://www.adobe.com/2006/mxml</a>"&gt;
&lt;mx:Script&gt;
&lt;![CDATA[
import mx.formatters.NumberFormatter;
import mx.events.ValidationResultEvent;
import mx.validators.NumberValidator;
/**
* This method is invoked when the user enters a value in the weight column
* of the weigh_in datagrid. Because we're allowing the user to handle validation,
* we'll always return what the user typed in, right or wrong. By sending back a
* value which will eventually fail validation, that allows us to keep the value
* in the field when the errortip is drawn around it.
*/
override public function get data() : Object
{
var value:String = text;
<em> // no need to validate here, since we don't act upon it. but if we did</em> <em> // need to validate to make some kind of decision, then we'll end up</em> <em> // validating twice.</em>
return value;
}
/**
* This is our validation method. We pass validation if the user has
* entered a non-negative decimal. This is a public function so that we can
* re-use this validation outside of the editor.
*
* @param value   The user-entered value
* @return null if we pass validation, otherwise we return the error string
*/
public function validate(value:String) : String
{
var validator:NumberValidator = new NumberValidator();
validator.allowNegative = false;
validator.required = false;
var result:ValidationResultEvent = validator.validate(value);
if (result.type != ValidationResultEvent.VALID)
return result.message;
return null;
}
]]&gt;
&lt;/mx:Script&gt;
&lt;/mx:TextInput&gt;</code>

you&#8217;ll notice that i introduced a label function on the weight column. that&#8217;s there to handle formatting numbers. for example, if the user entered 000122, we&#8217;ll display that as 122.

for part 3, all we&#8217;ve got left is to change our validation so that we&#8217;ll flag an error if the entered weight is out-of-range of the weight class.

click on the image to run the new app. to see the error behavior, enter a non-number such as 5.5.5.

<a href="http://blogs.digitalprimates.net/strictinequality/samples/wrestling/wrestling2.html">
<img src="http://blogs.digitalprimates.net/strictinequality/images/blog/wrestling.jpg" alt="" />
</a>

<em>NOTE: there is a bug in flex2 DataGrid so that the behavior of the last cell is incorrect. if you enter a bad value in the last cell of the weight column and tab out, the value will disappear. if you hit enter, though, we&#8217;ll stay in the cell as expected. it seems this is fixed in flex 3.</em>]]></content:encoded>
			<wfw:commentRss>http://www.digitalprimates.net/author/strict-inequality/2008/03/03/datagrid-item-editors-and-dynamic-behavior-part-23/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Datagrid item editors and dynamic behavior (part 1/3)</title>
		<link>http://www.digitalprimates.net/author/strict-inequality/2008/02/27/datagrid-item-editors-and-dynamic-behavior-part-13/</link>
		<comments>http://www.digitalprimates.net/author/strict-inequality/2008/02/27/datagrid-item-editors-and-dynamic-behavior-part-13/#comments</comments>
		<pubDate>Wed, 27 Feb 2008 02:56:38 +0000</pubDate>
		<dc:creator>strict-inequality</dc:creator>
				<category><![CDATA[flex-actionscript]]></category>

		<guid isPermaLink="false">http://173.236.48.58/~digit329/strict-inequality/?p=9</guid>
		<description><![CDATA[though a lot of validation functionality is available in mxml through validators, i&#8217;ve always found it curious that Datagrid seems to be a bit short-changed. for example, in mxml, i can create a TextInput and give it the ability to &#8230; <a href="http://www.digitalprimates.net/author/strict-inequality/2008/02/27/datagrid-item-editors-and-dynamic-behavior-part-13/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[though a lot of validation functionality is available in mxml through validators, i&#8217;ve always found it curious that Datagrid seems to be a bit short-changed.

for example, in mxml, i can create a TextInput and give it the ability to accept ranges of numbers, through both the restrict property and assigning it a NumberValidator, but how do we do the same in a Datagrid cell?

further, what happens when we want to dynamically process cell contents based on other data in the same row?

Adobe has a primer here:
<a href="http://www.adobe.com/devnet/flex/quickstart/creating_item_editors/ ">http://www.adobe.com/devnet/flex/quickstart/creating_item_editors/</a>

&#8230; with help on creating different kinds of item editors. but whenever they give an example of a cell into which you&#8217;re meant to enter numbers, they use a NumericStepper. and that&#8217;s great, but maybe i don&#8217;t always want to use a NumericStepper in my interface. maybe i want to use a TextInput component, just like the rest of the cells. further, we still haven&#8217;t addressed the kinds of dynamic behavior i mentioned above.

so what&#8217;s the solution?<span id="more-677"></span>
using the approach on that Adobe page called &#8216;Using a component as an item editor&#8217;, and by delving into a little bit of ActionScript, we can make ourselves a TextInput item editor that can do everything we need.

i&#8217;m dividing this topic into 3 posts so we can build to our eventual solution.

let&#8217;s take an example:

say you&#8217;re building an app for your local high school&#8217;s wrestling team. you&#8217;ve already written the part of the app to pre-register wrestlers and now you&#8217;re working on the screen for weigh-in at the meet.

when wrestlers pre-register, they enter their names and respective weight classes. on the day of the meet, you want your weigh-in screen to flag an error to the user that a wrestler is out of his weight class.

let&#8217;s take a first stab at some prototype code. here&#8217;s the mxml you&#8217;ve written for the weigh-in app (called wrestling.mxml):

<code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" &gt;
&lt;mx:XMLList id="wrestlers" xmlns=""&gt;
&lt;wrestler&gt;
&lt;name&gt; Bob Smith &lt;/name&gt;
&lt;weightClass&gt; A &lt;/weightClass&gt;
&lt;weight&gt; &lt;/weight&gt;
&lt;/wrestler&gt;
&lt;wrestler&gt;
&lt;name&gt; Tim McLarry &lt;/name&gt;
&lt;weightClass&gt; B &lt;/weightClass&gt;
&lt;weight&gt; &lt;/weight&gt;
&lt;/wrestler&gt;
&lt;wrestler&gt;
&lt;name&gt; Joe Doug &lt;/name&gt;
&lt;weightClass&gt; C &lt;/weightClass&gt;
&lt;weight&gt; &lt;/weight&gt;
&lt;/wrestler&gt;
&lt;wrestler&gt;
&lt;name&gt; Larry McTim &lt;/name&gt;
&lt;weightClass&gt; D &lt;/weightClass&gt;
&lt;weight&gt; &lt;/weight&gt;
&lt;/wrestler&gt;
&lt;/mx:XMLList&gt;
&lt;mx:DataGrid id="weigh_in" editable="true" dataProvider="{wrestlers}" &gt;
&lt;mx:columns&gt;
&lt;mx:DataGridColumn id="wrestler_name" headerText="Name" editable="false" dataField="name" /&gt;
&lt;mx:DataGridColumn id="weight_class" headerText="Class" editable="false" dataField="weightClass" /&gt;
&lt;mx:DataGridColumn id="weight" headerText="Weight" editable="true" dataField="weight"
itemEditor="WeightEditor" editorDataField="data" /&gt;
&lt;/mx:columns&gt;
&lt;/mx:DataGrid&gt;
&lt;mx:Form &gt;
&lt;mx:FormHeading label="Weight Classes" /&gt;
&lt;mx:FormItem label="A" &gt;
&lt;mx:Label text="0-99 lbs." /&gt;
&lt;/mx:FormItem&gt;
&lt;mx:FormItem label="B" &gt;
&lt;mx:Label text="100-135 lbs." /&gt;
&lt;/mx:FormItem&gt;
&lt;mx:FormItem label="C" &gt;
&lt;mx:Label text="136-169 lbs." /&gt;
&lt;/mx:FormItem&gt;
&lt;mx:FormItem label="D" &gt;
&lt;mx:Label text="107-210 lbs." /&gt;
&lt;/mx:FormItem&gt;
&lt;/mx:Form&gt;
&lt;/mx:Application&gt;</code>

pretty straightfoward. we&#8217;ve got a Datagrid with 3 columns, some test XML to fill in the name and weight class, and a small form letting us know the weight class ranges (which i made up, and likely has no bearing on any realworld scenario).

we&#8217;ve also referenced a WeightEditor.mxml, which is this code:

<code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;mx:TextInput restrict="1234567890,." xmlns:mx="<a href="http://www.adobe.com/2006/mxml" target="_blank">http://www.adobe.com/2006/mxml</a>"&gt;
&lt;mx:Script&gt;
&lt;![CDATA[
import mx.events.ValidationResultEvent;
import mx.validators.NumberValidator;
/**
* This method is invoked when the user enters a value in the weight column
* of the weigh_in datagrid. We'll validate the user-entered value as a number
* and return a blank if it's not a number. Otherwise, we'll return what the
* user entered.
*/
override public function get data() : Object
{
var value:String = text;
if (!this.validate(value))
return '';
return value;
}
/**
* This is our validation method. We pass validation if the user has
* entered a non-negative decimal.
*
* @param value   The user-entered value
* @return true if we pass validation, false otherwise
*/
protected function validate(value:String) : Boolean
{
var validator:NumberValidator = new NumberValidator();
validator.allowNegative = false;
validator.required = false;
var result:ValidationResultEvent = validator.validate(value);
if (result.type != ValidationResultEvent.VALID)
return false;
return true;
}
]]&gt;
&lt;/mx:Script&gt;
&lt;/mx:TextInput&gt;</code>

this code is more interesting. first, note that we&#8217;ve derived this item editor from TextInput, so it should behave more like the other cells than, say, a NumericStepper would. second, you&#8217;ll notice that we set the restrict property straightaway, so we see an immediate benefit that the user can enter only numbers, periods and commas in this field.

we override the data method here to return what the user entered if it&#8217;s a number. if it&#8217;s not a number (e.g. 5.5.5..5), we&#8217;ll return nothing.

why is the method called &#8216;data&#8217;? because that&#8217;s the value we specified in the editorDataField in the weight column in the wrestling.mxml file. it doesn&#8217;t have to be called &#8216;data&#8217;, you can call it whatever you&#8217;d like. but because &#8216;data&#8217; exists and is considered the default, we have to specify that we&#8217;re overriding it.

this is a decent first step, but doesn&#8217;t exhibit terribly useful behavior. first, if the user enters something that isn&#8217;t a number, the cell erases itself. good for avoiding keeping bad values around, not so good for the user experience. this is something we want to keep in mind for our next iteration.

also note that we haven&#8217;t honored, at all, the weight class. we&#8217;ll also fix this in a later iteration, and it would be nice if we could provide some meaningful user feedback for what went wrong.

we&#8217;ll address user notification in part 2.

<a href="http://blogs.digitalprimates.net/strictinequality/samples/wrestling/wrestling.html">
<img src="http://blogs.digitalprimates.net/strictinequality/images/blog/wrestling.jpg" alt="" />
</a>

(click on the image to run the app)]]></content:encoded>
			<wfw:commentRss>http://www.digitalprimates.net/author/strict-inequality/2008/02/27/datagrid-item-editors-and-dynamic-behavior-part-13/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

