Creating Keyboard Shortcuts in a Flash Form

Posted By : todd sharp Posted At : June 29, 2006 9:12 PM Posted In: Flash Forms, ActionScript, ColdFusion

3

Here's a way to create keyboard shortcuts for your flash forms. You can pretty much map any key sequence to perform a specified function. In the example, I'm showing a quick way to jump between tabs in a tab navigator with CTRL-1, CTRL-2 & CTRL-3. Note: Don't try the 1,2 & 3 in your num pad - I'm guessing they're mapped differently.

Check out the example.

Here's the code:

<cfform format="flash" onload="initS();">
<cfformitem type="script">
function initS(){
var myListener = Object();
myListener.onKeyDown = myOnKeyDown;
Key.addListener( myListener );

Accessibility.updateProperties();
}


function myOnKeyDown() {
    if (Key.isDown(Key.CONTROL) && Key.getCode() == 49) // 49 is key code for 1
    {
        _root.testTabNav.selectedIndex = 0;
    }
        if (Key.isDown(Key.CONTROL) && Key.getCode() == 50)
    {
        _root.testTabNav.selectedIndex = 1;
    }
        if (Key.isDown(Key.CONTROL) && Key.getCode() == 51)
    {
        _root.testTabNav.selectedIndex = 2;
    }
}
</cfformitem>
    <cfformgroup type="tabnavigator" id="testTabNav">
        <cfformgroup type="page" label="tab1">    
        </cfformgroup>
        <cfformgroup type="page" label="tab2">
        </cfformgroup>
        <cfformgroup type="page" label="tab3">
        </cfformgroup>
    </cfformgroup>
    <cfformitem type="text">press ctrl 1 for tab 1, ctrl2 for tab 2, ctrl 3 for tab 3</cfformitem>
</cfform>

Flash Shared Object Viewer

Posted By : todd sharp Posted At : June 29, 2006 12:26 PM Posted In: Flash Forms, ActionScript

0

Just found a pretty cool little application for viewing and editing shared objects. It's called SolVE and it was created by Darron Schall. It's a pretty helpful little tool, check it out!

CFGRID - Custom Validation with Listeners

Posted By : todd sharp Posted At : June 26, 2006 9:13 PM Posted In: Flash Forms, ActionScript, ColdFusion

3

One of the downsides of working with an editable CFGRID in a Flash form is the inability to use validation on the individual grid columns. Of course, server side validation should always be done and should catch any bad data - but it never hurts to throw in some client side validation. The technique below allows you to create custom validation rules for individual columns giving you the extra security of some client side catches. Ray Camden has a good post showing a simliar method for grid validation, but this technique allows for a little more flexibility and does not require looping over the entire grid to check for valid inputs. In this method a few listeners are employed which catch some built in events that the grid broadcasts (specifically the cellFocusOut and cellEdit events). This gives us the ability to immediately catch the invalid inputs and do things like blank out the cell, disable controls when we catch the bad data, reset the focus to the invalid cell, round decimals to integers, and endless additional possibilities.

Here's an example.

The code is below.

<cfform format="flash" name="testForm" onload="initListeners(testGrid);">
<cfformitem type="script">
function initListeners(dg:mx.controls.DataGrid){

var listenerObject:Object={};
var listenerObject2:Object={};

    listenerObject.cellFocusOut = function(eventObject){
        var col:Number = eventObject.columnIndex;
        var row:Number = eventObject.itemIndex;
        var colName = dg.getColumnAt(col).columnName;
        var txt = dg.getItemAt(row)[colName];
        var testBtn = testBtn;
    
        if(isNaN(txt)){
            {
            var myClickHandler = function (evt){
                if (evt.detail == mx.controls.Alert.OK){
                    dg.editField(row, colName, '');
                    dg.focusedCell ={columnIndex:col, itemIndex:row};
                    testBtn.enabled = false;
                    }
                }
        
            alert('Must be a number.', 'Warning', mx.controls.Alert.OK, myClickHandler);
            }    
        }
        
        if(! isNaN(txt)){
        testBtn.enabled = true;
        txt = Math.round(txt);
        dg.editField(row, colName, txt);
        }
    }
    
    listenerObject2.cellEdit = function(eventObject){
        var col:Number = eventObject.columnIndex;
        var row:Number = eventObject.itemIndex;
        var colName = dg.getColumnAt(col).columnName;
        var txt = dg.getItemAt(row)[colName];
            
            if(txt != eventObject.oldValue)
            {
            dg.editField(row, 'status', 'updated');
            }
    }
dg.addEventListener("cellFocusOut", listenerObject);
dg.addEventListener("cellEdit", listenerObject2);
}
</cfformitem>
<cfgrid name="testGrid" selectmode="edit">
    <cfgridcolumn name="col1" header="Enter a Number" select="yes">
    <cfgridcolumn name="col2" header="Enter a Number" select="yes">
    <cfgridcolumn name="status" header="Status" select="no">
    <cfgridrow data="1,2">
    <cfgridrow data="1,2">
    <cfgridrow data="1,2">
    <cfgridrow data="1,2">
    <cfgridrow data="1,2">
</cfgrid>
<cfinput type="button" name="testBtn" value="Submit" onclick="alert('You submitted the form');">

</cfform>

Flash Form - Disabling Accordion Pages

Posted By : todd sharp Posted At : June 26, 2006 10:48 AM Posted In: Flash Forms, ActionScript, ColdFusion

0

I just stumbled across this post that shows how to dynamically disable an accordion page in Flex. Like most good Flex tips, it works perfect in Flash Forms too. Check it out.

Flash Form Tab Navigator Tips

Posted By : todd sharp Posted At : June 16, 2006 1:37 PM Posted In: Flash Forms, ActionScript, ColdFusion

5

I'm not quite sure how useful this will be since it's mostly undocumented stuff and you really can't do much with it, but it's still pretty cool. Ever wish you could add or delete tabs in a tab navigator in a Flash form? What about dynamically enabling/disabling (or hiding/showing) them on the fly? Use at your own risk since they'll likely be locked down in future versions of CF.

Oh yeah, see this post for a more technical explanation as to why you shouldn't use these features.

See a demo.

<cfform format="flash" name="myForm">
<cfformitem type="script">
function makeTabs(howMany){
    for(var i = 0; i<howMany; i++){
    var numExistingTabs = tabNav.numChildren;
    var numPlusOne = numExistingTabs + 1;
    var tabLabel = 'Tab ' + numPlusOne;
    _root.tabNav.createTab(mx.containers.Canvas, "", tabLabel);
    //update the del ete select list

    _root.delTab.addItemAt(numExistingTabs, numPlusOne,numPlusOne);
    }
}

function delIt(delMe){
var numExistingTabs = tabNav.numChildren;
    _root.tabNav.destroyChildAt(delMe);

    for(var i = 0; i<numExistingTabs; i++){
    var j = i+1;
    _root.tabNav[i].label = 'Tab ' + j;
    _root.delTab.removeAll();
    }

    //reset the variable
    numExistingTabs = tabNav.numChildren;
    
    //rebuild the select list
    for(var x = 0; x<numExistingTabs; x++){
    var y = x+1
    _root.delTab.addItemAt(x, y,y);
    }
    
}
</cfformitem>
<cfoutput>

    <cfformgroup type="horizontal">
        <cfselect name="howMany" label="How many tabs do you want to create?">
            <cfloop from="1" to="20" index="i">
                <option value="#i#">#i#</option>
            </cfloop>
        </cfselect>
        <cfinput type="button" name="makeEm" value="Make Em"
onclick="makeTabs(howMany.selectedItem.data);">

    </cfformgroup>
    
    <cfformgroup type="horizontal">
        <cfselect name="delTab" label="Which tab do you want to get rid of?">
            <cfloop from="1" to="3" index="i">
                <option value="#i#">#i#</option>
            </cfloop>
        </cfselect>
        <cfinput type="button" name="delEm" value="{'Del'+'ete It'}" onclick="delIt(delTab.selectedItem.data - 1);">
    </cfformgroup>
    
</cfoutput>

<cfformgroup type="horizontal">
    <cfinput type="radio" name="disEn1" onclick="_root.tabNav['tabBar']._tab0.enabled = false;" label="Disable Tab 1" value="dis1">
    <cfinput type="radio" name="disEn1" onclick="_root.tabNav['tabBar']._tab0.enabled = true;" label="Enable Tab 1" value="en1">
    <cfinput type="radio" name="hideShow1" onclick="_root.tabNav['tabBar']._tab0.visible = false;" label="Hide Tab 1" value="hide1">
    <cfinput type="radio" name="hideShow1" onclick="_root.tabNav['tabBar']._tab0.visible = true;" label="Show Tab 1" value="show1">
</cfformgroup>

<cfformgroup type="tabnavigator" id="tabNav">
    <cfformgroup label="Tab 1" type="page" name="tab1">
    </cfformgroup>
    <cfformgroup label="Tab 2" type="page" name="tab2">
    </cfformgroup>
    <cfformgroup label="Tab 3" type="page" name="tab3">
    </cfformgroup>
</cfformgroup>

</cfform>

CFGRID Cell Renderer - ComboBox (Select)

Posted By : todd sharp Posted At : June 12, 2006 8:32 PM Posted In: Flash Forms, ActionScript, ColdFusion

23

As promised in my last post, here is an example of creating a custom cell renderer for a select in a cfgrid. To be fair, the code isn't my own (though I can't seem to find where I originally found it. This example is purely to show that the custom renderer can be applied to a cfgrid within a Flash Form and can easily be customized to meet your individual needs.

The dataprovider is hardcoded in this example, but I imagine it could easily be dynamic with a little Flash remoting. The cool thing is that existing grid data item is pre-selected if it exists in the select's dataprovider. In the example, you'll also see that when the selected item changes, the data is populated in the appropriate grid cell.

Here's the code - first the cfm page:

<cfform format="flash" name="test" onload="setCellRenderer();">
<cfformitem type="script">

function setCellRenderer(){
testGrid.getColumnAt(2).cellRenderer = ComboBoxCell;
}
</cfformitem>
<cfformitem type="spacer"/>
<cfformgroup type="horizontal">
<cfgrid name="testGrid" width="260" selectmode="edit">
<cfgridcolumn name="dataCol" header="data col" width="100">
                <cfgridrow data="data,low">
                <cfgridrow data="data,medium">
                <cfgridrow data="data">
<cfgridcolumn name="sel" header="select" width="200">
</cfgrid>
         <cfinput type="button" name="testbtn" value="Show Value in Grid" onclick="alert(testGrid.selectedItem.sel);">
    </cfformgroup>
</cfform>

Now the ComboBoxCell.as - residing in the same directory as the cfm page:

import mx.core.UIComponent
import mx.controls.ComboBox

class ComboBoxCell extends UIComponent
{
    var combo : MovieClip;
    var owner : MovieClip;    // The row that contains the cell.
    var listOwner : MovieClip; // the reference we receive to the list
    var getCellIndex : Function; // the function we receive from the list
    var    getDataLabel : Function; // the function we receive from the list
    private static var COMBOBOX_DATA_PROVIDER : Array = [{label:"", data:""},
                                        {label: "unrated", data: "unrated"},
                                        {label: "low", data: "low"},
                                        {label: "medium", data: "medium"},
                                        {label: "high", data: "high"}];
    function ComboBoxCellRenderer()
    {
    }

    function createChildren(Void) : Void
    {        
        //Creates a ComboBox object and listen to changes
        combo = createObject("ComboBox", "Combo", 0, {styleName:this, owner:this});
        combo.addEventListener("change", this);
        combo.dataProvider = COMBOBOX_DATA_PROVIDER;
        size();
    }

    // note that setSize is implemented by UIComponent
    //and calls size(), after setting
    // __width and __height
    function size(Void) : Void
    {
        
    var h = __height;
var w = __width;
combo.setSize(w - 2, Math.max(h, listOwner.rowHeight - 2));    
    }

public function setValue(str:String, item:Object, sel:Boolean) : Void
    {
        /* Sets the ComboBox to the correspoinding
        cell data from the list owner's data provider
        if the cell data matches
        with any items available for the ComboBox. */

        
        var drawCombo:Boolean = true;
        if (item[getDataLabel()]!=undefined)
        {
            /* For each item's data in the ComboBox, verify if it matches
            the assigned data for the cell this ComboBox is in.
            Set the selectedIndex of the ComboBox to what matches. */

            for(var i:Number = 0; i < combo.length; i++)
            {
                if( combo.getItemAt(i).data == item[getDataLabel()] )
                {
                    combo.selectedIndex = i;
                    break;
                }
                if ( i == combo.length - 1 )
                {
                    // There was no matching data, the ComboBox should not be shown.
                    //drawCombo = false;
                    // The original function hid the combobox if no data, i leave it in
                }
            }
        }
        else
        {
            // There was no data, set the combobox to blank
            combo.selectedIndex = 0;
        }
        
        combo._visible = drawCombo;
    }

    function getPreferredHeight(Void) : Number
    {
        return owner.__height;
    }

    function getPreferredWidth(Void) : Number
    {
        return owner.__width;
    }
    // This re-builds the dataProvider, the selected item
    // as the first in the array
    function reorder(datos:Array, choice:String):Array {
        var index:Number = 0
        var newArray = new Array()
        for(var i=0; i<datos.length; i++){        
            if(datos[i].label!=choice){
                index++
                newArray[index] = datos[i]
            } else newArray[0] = datos[i]
        }    
        return newArray
    }


    
    public function change()
    {
        // Handler for the ComboBox change event.
        
        // Set the listOwner's data to the currently selected item's data of the combo box.
        listOwner.dataProvider.editField(getCellIndex().itemIndex, getDataLabel(), combo.selectedItem.data);    
        listOwner.selectedIndex = getCellIndex().itemIndex;
    }

}

Live Example.

CFGRID Cell Render - Enhancing the CheckBox

Posted By : todd sharp Posted At : June 10, 2006 12:34 PM Posted In: Flash Forms, ActionScript, ColdFusion

1

Jari asks:

"Would it be possible to use an mxml custom cell renderer to add on a cfgrid a checkbox, which would fire the onclick events."

The short answer is "kinda" - I'm guessing it could be done with mxml (as I did in this post but i'm not quite sure how to tackle it that way since I've yet to dig too deep into Flex. However, there is an easier way to go get the cell renderer to do what Jari is looking for using Actionscript.

Using the type="boolean" attribute of the cfgridcolumn presents us with a handy checkbox in the grid column. The only problem with this checkbox is that checking/unchecking it does not fire any events within the grid. To get around this restriction, we simply have to modify the custom cell renderer that is used to create the checkbox.

The cfm page looks like this:

<cfform format="flash" name="testForm" onload="setCellRenderer();">
<cfformitem type="script">

function setCellRenderer(){
testGrid.getColumnAt(2).cellRenderer = CustomCheckCellRenderer;
}

function test(){
alert('This function is in the cfm page and is called from CustomCheckCellRenderer.');
}

</cfformitem>
<cfformgroup type="horizontal">
     <cfgrid name="testGrid" width="260">
        <cfgridcolumn name="dataCol" header="data col">
            <cfgridrow data="hi">
            <cfgridrow data="hi2">
            <cfgridrow data="hi3">
        <cfgridcolumn name="check" header="check box">
     </cfgrid>
     <cfinput type="text" name="testText" label="Text input populated by CustomCheckCellRenderer">
</cfformgroup>
</cfform>

The only thing I've done special here is call the setCellRenderer function onload of the form which sets the cell renderer of the second column of the grid to be CustomCheckCellRenderer.

The code for CustomCheckCellRenderer.as follows (saved within the same directory of the cfm page):

import mx.core.UIComponent
import mx.controls.CheckBox

class CustomCheckCellRenderer extends UIComponent
{

    var check : MovieClip;
    var listOwner : MovieClip; // the reference we receive to the list
    var getCellIndex : Function; // the function we receive from the list
    var    getDataLabel : Function; // the function we receive from the list
    var init = false;

    function CheckCellRenderer()
    {
    }

    function createChildren(Void) : Void
    {
        check = createClassObject(CheckBox, "check", 0, {styleName:this, owner:this});
        check.addEventListener("click", this);
        size();
    }

    // note that setSize is implemented by UIComponent and calls size(), after setting
    // __width and __height
    function size(Void) : Void
    {
        check.setSize(20, layoutHeight);
        check._x = (layoutWidth-20)/2;
        check._y = (layoutHeight-16)/2;
    }

    function getPreferredHeight(Void) : Number
    {
        return 16;
    }

    function getPreferredWidth(Void) : Number
    {
        return 20;
    }

    function setValue(str:String, item:Object, sel:Boolean) : Void
    {
//width = 0 is the same as hidden
var w = 0;
if( listOwner.__columns[getCellIndex().columnIndex] != undefined && listOwner.__columns[getCellIndex().columnIndex]['width'] != undefined )
{
     w = listOwner.__columns[getCellIndex().columnIndex]['width'];
     }
// set visible if display is true (width >
0)
check.visible = false;
if( item != undefined && w > 0 )
{
check.visible = true;
}

if( item != undefined )
{
check.selected = false;
if( item[getDataLabel()] == 1 || item[getDataLabel()] == "true" || item[getDataLabel()] == "yes" )
{
check.selected = true;
listOwner.dataProvider[getCellIndex().itemIndex][getDataLabel()] = "true";
init=true;
}
else
{
if( !init && item[getDataLabel()].length == 1)
{
init=true;
this.click();
}
else
{
init=true;
}

listOwner.dataProvider[getCellIndex().itemIndex][getDataLabel()] = "false";
}
}
    }

    function click()
    {
        //the original click() function edits the grid to be true/false
        //depending on the check value

        listOwner.editField(getCellIndex().itemIndex, getDataLabel(), check.selected);
        
        //I've added the following code to show
//how to extend the click function
        
        //set the selectedIndex of the grid -
//this is usefull if you are
        //looking to do anything with the gridrow

        //declare a variable for the current gridrow

        var curRow = getCellIndex().itemIndex;
        listOwner.selectedIndex = curRow;
        
    
        
        //set up a msg
        var msg = 'You have selected the checkbox in row '+curRow+'. The current value is '+check.selected;
        
        //show a msg
        mx.controls.Alert.show(msg);
        
        //populate something in a text box
        _root.testText.text = _root.testGrid.selectedItem.dataCol;
        
        //call a function in the cfm page
        _root.test();
    }

}

You can view a live example here.

In my next post, I'll show how to modify this concept to create a custom renderer for a select in a grid.

Creating a Read Only Flash Form Textarea

Posted By : todd sharp Posted At : February 11, 2006 12:09 PM Posted In: Flash Forms, ActionScript, ColdFusion

0

I'm always the last to figure these kind of things out, but I thought I'd share with the 5 people out there who haven't figured this out yet like me.

Ever want to make a text area read only (maybe based on permissions, or for any other reason)? In HTML we had the "readonly" attribute, but there is no "readonly" attribute for cftextarea. You could disable the control, but then users couldn't copy the text or even scroll if the text was longer than the given height. So what to do? Actionscript to the rescue!

The trick is to set the textarea's editable attribute to "false." Put some actionscript in the onfocus of the textarea (or do it onload, or wherever the hell you feel like putting it) kinda like this:

<cftextarea name="myText" onfocus="if(adminLoggedIn.selected == false){myText.editable = false;}"/>

Multiline Header in a CFGRID

Posted By : todd sharp Posted At : January 5, 2006 11:38 AM Posted In: Flash Forms, ActionScript, ColdFusion

0

Here's a simple technique for creating a multiline header in a Flash Form. It's not terribly dynamic, but it works in a pinch.

Here's the code:

<cfform format="flash" name="wordwrapHeader" onload="setHeadHeight(wordwrapHeaderGrid);">
    <cfformitem type="script">
    function setHeadHeight(dg){
    dg.variableRowHeight = true;
    dg.setHeaderHeight(30);
    for(var i:Number = 0; i<dg.__columns.length; i++)
    {
    dg.getColumnAt(i).wordWrap = true;
    }
    dg.getColumnAt(1).headerText = "This Header is\nReally Long";
    dg.getColumnAt(2).headerText = "Short Header";
    }
    </cfformitem>

    <cfgrid name="wordwrapHeaderGrid" rowheaders="no" width="200">
        <cfgridcolumn name="column1" width="100">
        <cfgridcolumn name="column2" width="100">
        <cfgridrow data="This Text is Really Long But the Column isnt so wide.,This Text is Short.">
    </cfgrid>

</cfform>


And here's what it looks like:

Multiline header in a flash form.

The trick is to set the header height equal to the height of the "tallest" header in your grid. I've found that 15px per line works good. So if your tallest header is 4 lines, go with 60. The other trick is to use Actionscript's new line designation (\n) in your header text to force a new line. As I said it's not very dynamic but it works.

Genesis 1.0 (Query Evalution Tool) Released

Posted By : todd sharp Posted At : January 4, 2006 8:28 PM Posted In: SQL, Flash Forms, ActionScript, ColdFusion

1

I'm happy to announce the release of Genesis 1.0 (Query Evaluation Tool built on Flash Forms). There are a ton of updates in this release thanks to some really great feedback from some of the folks who downloaded the initial release.

Some highlights of this release:

  • Added Stored Procedures, Views, and Indexes to the Object Browser (MSSQLSERVER only). Stored procs and views can be scripted to the query window by selecting the proc or view and right clicking in the query window. Currently Genesis supports returning a single resultset when calling a stored proc.
  • Added clear query button.
  • Added copy to clipboard button to copy the current results to the client's clipboard.
  • Added query logging.
  • Made results grid editable to allow users to copy single cell results to clipboard.
  • Added selective querying. When working in the query window if a user would like to run a single sub-query they can highlight just the code they would like to run and click Execute Query.
  • Misc other fixes throughout.

If you have a previous version of Genesis make sure you don't overwrite the savedQueries and savedResults directories when migrating to this version (if would like to keep existing stored queries and results that is).

For the moment I had to discontinue Object Browser support for MySQL db's. I'll probably re-support them in the future, but I ran out of time and some of the changes that I made in this release weren't working with MySQL.

Thanks again to all who sent in feedback. Keep it coming!