Flex: horizontal scrolling banner

27 10 2008

OK, so I’m not advocating advertising, but we all deal with it from time to time.

Below is a simple solution to get some container (in this case a HBox) to scroll horizontally across the screen.

Note the clipContent on the absolute container (in this case the Application container). This prevents the container from putting up scrollbars.

The duration sets the speed of movement, below I’ve set it to approximately 15ms per pixel.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application resize="onResized(event)" creationComplete="onCrtComplete()" xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" clipContent="false">
    <mx:Script>
        <![CDATA[

            import mx.effects.easing.Linear;

            private function onCrtComplete():void
            {
                startScroll();
            }

            private function startScroll():void
            {
                moved.duration = 15 * this.width + (15 * hbox.width);

                moved.xTo = -hbox.width;  

                hbox.x = this.width;
                hbox.y = this.height - hbox.height;
                moved.play();
            } 

            private function onResized(evt:Event):void
            {
                if (!this.initialized)
                {
                    return;
                }
                moved.stop();

                startScroll();
            }

        ]]>
    </mx:Script>

    <mx:Move id="moved" target="{hbox}" repeatCount="0" easingFunction="mx.effects.easing.Linear.easeIn" />

    <mx:HBox id="hbox" backgroundColor="#5F3013" fontSize="12" fontFamily="courier" color="#86DF2D" >
        <mx:Label text="this is" />
        <mx:Label text="shameless" fontWeight="bold" />
        <mx:Label text="advertising" />
    </mx:HBox>

</mx:Application>

Advertisements




our internet community (or why I hate the experts exchange)

13 10 2008

When you get down to it, the Internet has been evolving in some very curious ways.

One of the best things about it’s current stage I believe, is the co-operative nature of so much of it. From open source software that can do just about anything, to the free web applications that proliferate our lives, the Net gives back as much it takes.

For me professionally, the help that comes from other developers who’ve posted responses to issues, bugs and common problems has been invaluable. At this stage though, I also need to credit Google – and any number of other search engines – for providing me with relevant enough content that I rarely need to visit even the first five results, let alone venture to the next ten.

It was for these reasons, along with having a repository of findings for myself, that I started this blog in the first place. As the hits and comments steadily build, I feel a certain sense of joy. Certainly part of it is pride, but I also feel a strong sense of satisfaction in being able to help others liked they’ve helped me.

With this in mind, I feel a strong sense of loathing when my searches reveal the Experts Exchange in amongst the top ten. They are a subscription-based help site who I’m sure you’ve encountered before. I have seen various posts lambasting them for fooling Google results by allowing the bots to scan responses.

What we’ve created in the Internet community is wonderful. We help others, they help us. Simple. No money, no reasons, just helping each other out. It’s like a virtual honesty box, and it’s working beautifully.

Who needs the experts exchange and their paid “experts” when we have our own expert: you.





Flex: Dynamic, bindable value objects

10 10 2008

What a mouthful. Dynamic, bindable value objects.

Well, practically these puppies are pretty useful.

Lemme give you a scenario:

You are writing an Employee database that you wish to use for various Companies. Say that each Company wants custom fields for employees (on top of the essential fields). Sure, you could add another column to the Employee table “CustomFields” which is XML data and put the fields in there. However, my preferred method is to have two extra tables – one a list of available fields for the company, and the other an assignment of values for these custom fields to an employee record.

eg:

Example Database Schema

At this point I will clarify that I use .NET 3.5 and LINQ to pull out this data. That means that my Employee object will automatically be given the Company property and the EmployeeCustomValues[] property. Please see my other articles on Flex and .NET 3.5.

Now, to get this into Flex.

I’m using FluorineFx’s most recent revision (r33) posted on Sep 30 in their Google SVN (at time of writing, the most recent version for DL on the FluorineFx website is v1.0.0.13, which won’t handle dynamic objects for our purposes).

I also tried with WebORB 3.5 but was getting exceptions in sending this data back to .NET and still haven’t heard back from them regarding dynamic value objects.

Getting this data into the Employee.as file isn’t that hard. You write a normal value object, but you make it dynamic and you will need to extend Proxy to make it bindable. See this solution here. (I’ve also read this solution in the Flex 3 Cookbook).

Now the fun part, in Employee.as, I now need to also implement some more methods of Proxy, because I want to use these value objects inside Datagrids. First off I need to get all the properties within this object for enumeration. This means, in the Employee constructor, I’ll need to get all the strong properties and add them to my list. I do this using the describe type utility.

[Bindable(event=“propertyChange”)]

[RemoteClass(alias=“JustinJMoses.Examples.DynamicBindableVO.Employee”)]

dynamic public class Employee extends Proxy implements IEventDispatcher

{

private var _evtDispatcher:EventDispatcher;

private var _properties:Array;

public function Employee():void

{

_evtDispatcher = new EventDispatcher(this);

_properties = new Array();

var classInfo:XML = flash.utils.describeType(this);

for each (var a:XML in classInfo..accessor)

{

addProperty(a.@name.toString());

}

}

….

Then, as I add dynamic properties in the getProperty() and setProperty() I will also append them to my _properties private array. This means that enumerating my object will now yield both my strong properties and my dynamic ones:

//because this class override Proxy, it must implement the iteration over this object’s properties

override flash_proxy function nextNameIndex (index:int):int {

if (index < _properties.length) {

return index + 1;

} else {

return 0;

}

}

//as above

override flash_proxy function nextName(index:int):String {

return _properties[index – 1];

}

override flash_proxy function nextValue(index:int):*

{

return this[_properties[index – 1]];

}

But there’s still a bit more. The callProperty() method is also needed for all these toString() calls that happen around the shop. My implmentation ins’t great I confess, but I found that without it, the rows in my Datagrids weren’t selectable as the UID created for each row is a toString() of the datarow’s item.

//this implementation of toString is required as a datagrid’s list items require it

override flash_proxy function callProperty(name:*, … rest):*

{

if (name.localName == “toString”)

{

var str:String = “”;

for each (var field:String in this)

{

if (field != null)

str += field.toString();

}

return str;

}

}

 

Now, as I mentioned, this dynamic value object – Employee – will only get sent back to .NET successfully in the most recent revision of FluorineFx, but this should be remedied soon.

Remember, in the implementation of get and setProperty() in Employee, you still have to manage the storage of your values into the array collections sent from .NET.

I’ve written out an abstract DynamicProperties class and my Employee solution below.

DynamicProperties.as

[Bindable(event="propertyChange")]
    dynamic public class DynamicProperties extends Proxy implements IEventDispatcher
    {

        private var _evtDispatcher:EventDispatcher;

        private var _properties:Array;

        public function DynamicProperties():void
        {
            _evtDispatcher = new EventDispatcher(this);

            _properties = new Array();

            var classInfo:XML = flash.utils.describeType(this);

             for each (var a:XML in classInfo..accessor)
             {
                addProperty(a.@name.toString());
            } 

            //if you want to listen to property changes
            //this.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onChanged,false,0,true);
        }

        override flash_proxy function getProperty(name:*):*
        {
            return getDynamicProperty(name);

        }

        public function addProperty(name:String):void
        {
            _properties.push(name);
        }

        protected function getDynamicProperty(name:*):*
        {
            //abstract
            throw new Error("Abstract");
        }

        //this override allows us to dispatch a binding event when change
        override flash_proxy function setProperty(name:*, value:*):void
        {
            var oldVal:* = setDynamicProperty(name, value);

            var evt:Event = PropertyChangeEvent.createUpdateEvent( this, name, oldVal, value );

            dispatchEvent( evt );
        }

        /**
        * Abstract.
        *
        * This function will set the property of a dynamic
        */
        protected function setDynamicProperty(name:*, value:*):*
        {
            throw new Error("Abstract");
        }

        //because this class override Proxy, it must implement the iteration over this object's properties
        override flash_proxy function nextNameIndex (index:int):int {

             if (index < _properties.length) {
                 return index + 1;
             } else {
                 return 0;
             }
         }

         //as above
         override flash_proxy function nextName(index:int):String {
             return _properties[index - 1];
         }

         override flash_proxy function nextValue(index:int):*
         {
             return this[_properties[index - 1]];
         }

         //this implementation of toString is required as a datagrid's list items require it
         override flash_proxy function callProperty(name:*, ... rest):*
         {
             if (name.localName == "toString")
             {
                 var str:String = "";

                 for each (var field:String in this)
                 {
                     if (field != null)
                         str += field.toString();
                 }

                 return str;
             }
         }

        // IEventDispatcher implementation.
        public function addEventListener( type:String,
                                        listener:Function,
                                        useCapture:Boolean = false,
                                        priority:int = 0,
                                        useWeakReference:Boolean = false):void
        {
            _evtDispatcher.addEventListener( type, listener, useCapture,
                                               priority, useWeakReference );
        }
        // IEventDispatcher implementation.
        public function removeEventListener( type:String,
                                            listener:Function,
                                            useCapture:Boolean = false ):void
        {

           _evtDispatcher.removeEventListener( type, listener, useCapture );
        }
        // IEventDispatcher implementation.
        public function dispatchEvent( evt:Event ):Boolean
        {
            return _evtDispatcher.dispatchEvent( evt );
        }
        // IEventDispatcher implementation.
        public function hasEventListener( type:String ):Boolean
        {
            return _evtDispatcher.hasEventListener( type );
        }
        // IEventDispatcher implementation.
        public function willTrigger( type:String ):Boolean
        {
            return _evtDispatcher.willTrigger( type );
        }

    }

And my final Employee.as file

[Bindable]
    [RemoteClass(alias="JustinJMoses.Examples.DynamicBindableVO.Employee")]
    dynamic public class Employee extends DynamicProperties implements IValueObject
    {

        public var EmployeeID:int = 0;

        public var CompanyID:int = 0;

        /**
         * These are the two implementations of the dynamic properties class.
         *
         * They allow dynamic properties to be getted/setted via some implementation.
         */

        override protected function getDynamicProperty(name:*):*
        {
            for each (var value:EmployeeCustomValue in this.EmployeeCustomValues)
            {
                if (value.EmployeeCustomField.FieldName == name.localName)
                {
                    return value.FieldValue;
                }
            }

            return null;
        }

        override protected function setDynamicProperty(name:*, value:*):*
        {
            var oldVal:*; 

            for each (var fieldValue:EmployeeCustomValue in this.EmployeeCustomValues)
            {
                if (fieldValue.EmployeeCustomField.FieldName == name.localName)
                {
                    oldVal = fieldValue.FieldValue;
                    fieldValue.FieldValue = value;

                    return oldVal;
                }
            }

            //can't find this value for the object, so find the field object and create a new associated value
            var allFields:ICollectionView = MyModelLocator.getInstance().dynamicEmployeeFields;

            if (allFields.length < 1)
            {
                throw new MyError(MyError.EMPLOYEE_CUSTOM_FIELDS_NOT_LOADED);
            }

            //search through available field names
            for each (var field:EmployeeCustomField in allFields)
            {
                if (field.FieldName == name.localName)
                {
                    var employeeCustomValue:EmployeeCustomValue = new  EmployeeCustomValue();
                    employeeCustomValue.Employee = this;
                    employeeCustomValue.EmployeeCustomField = field;
                    employeeCustomValue.FieldValue = value;
                    this.EmployeeCustomValues.addItem(employeeCustomValue);
                    return null;
                }

            }

            throw new MyError(MyError.NO_EMPLOYEE_FIELD);

        }

        public function Employee():void
        {
            EmployeeCustomValues = new ArrayCollection();
        }

        private var _employeeCustomValues:ArrayCollection;

        public function get EmployeeCustomValues():ArrayCollection
        {
            return _employeeCustomValues;
        }

        public function set EmployeeCustomValues(val:ArrayCollection):void
        {
            _employeeCustomValues = val;

            for each (var field:EmployeeCustomValue in EmployeeCustomValues)
            {
                //set the property (so it shows up)
                this[field.EmployeeCustomField.FieldName] = field.FieldValue;

                //add the property to the internal list
                this.addProperty(field.EmployeeCustomField.FieldName);
            }

        }

    }