質問

I am trying to use a FormItem as an ItemRenderer within a DataGroup - the purpose being to dynamically create a Form based upon an XMLlist as a data provider. The first part is a success in that the form items are successfully rendered, however my problem is that despite used the FormLayout in the DataGroup, my FormItem Labels are not aligning up with eachother.

<?xml version="1.0" encoding="utf-8"?>
<s:Application 
xmlns:fx="http://ns.adobe.com/mxml/2009" 
xmlns:s="library://ns.adobe.com/flex/spark" 
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600"
>
<fx:Declarations>
    <fx:XMLList id="_formModel" >
            <control description="Weeks 1 Form Item"  id="087B4626-B95E-731F-ACC7-80ED7373E083" label="One" />
            <control description="Weeks 2 Form Item"  id="AC546361-D9C5-E9F3-5F90-80EFA52EF54D" label="Second"  />
            <control description="Weeks 3 Form Item"  id="9EE2AAA4-B9C3-68C0-D9B5-80F0374F940E" label="Three" />
            <control description="Weeks 4 Form Item"  id="3CCEABD6-12F9-E511-0C77-80F0A86902D5" label="The Fourth"  />      
    </fx:XMLList> 
    <s:XMLListCollection id="_controls" source="{_formModel}"/>
</fx:Declarations>

<s:Scroller width="100%" height="100%">
    <s:DataGroup width="100%" height="100%" dataProvider="{_controls}"  >
        <s:layout>
            <s:FormLayout/>
        </s:layout>
        <s:itemRenderer>
            <fx:Component>
                <s:ItemRenderer>
                    <s:FormItem label="{data.@label}" width="100%"  >
                        <s:TextInput  width="100%"  text="{data.@description}"/>
                    </s:FormItem>
                </s:ItemRenderer>
            </fx:Component>
        </s:itemRenderer>  
    </s:DataGroup>
</s:Scroller>
</s:Application>

Can anyone tell me how to align the form item labels whilst still using them within a Data Group as the purpose of the exercise is to create a form Dynamically based upon data fed into a DataGroup.

Thanks in advance.

役に立ちましたか?

解決

Interesting question: I had some fun solving it and it turned out to be easier then I anticipated.

The problem

The issue is that FormItems must be direct children of the container that has the FormLayout, however in this case the FormItem is a child of ItemRenderer which in turn is a child of the DataGroup.

The solution

To fix the issue, you can create a subclass of FormItem that also implements the IItemRenderer interface. That way the DataGroup will know what to do with the items, and the FormItems will be direct children of the FormLayout.

The code

First create the custom item renderer:

public class FormItemRenderer extends FormItem implements IItemRenderer {

    private var _data:Object;
    [Bindable("dataChange")]
    public function get data():Object { return _data; }
    public function set data(value:Object):void {
        _data = value;
        label = value ? value.@label : null;

        if (hasEventListener(FlexEvent.DATA_CHANGE))
            dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
    }

    private var _itemIndex:int;
    public function get itemIndex():int { return _itemIndex; }
    public function set itemIndex(value:int):void {
        if (value == _itemIndex) return;

        _itemIndex = value;
        invalidateDisplayList();
    }

    private var _selected:Boolean = false;
    public function get selected():Boolean { return _selected; }
    public function set selected(value:Boolean):void {
        if (value != _selected) {
            _selected = value;
            invalidateDisplayList();
        }
    }

    public function get dragging():Boolean { return false; }
    public function set dragging(value:Boolean):void { }

    public function get showsCaret():Boolean { return false; }
    public function set showsCaret(value:Boolean):void { }

}

I only implemented the bare minimum to get this to work; you can add what you need later.
Also note the second line of the data setter where we pass on the label attribute of the XML node to the FormItem's label property. That's not a very generic way to do it, but I guess you can figure out a solution for that later, if need be.

Now use the custom renderer:

<s:DataGroup dataProvider="{_controls}">
    <s:layout>
        <s:FormLayout/>
    </s:layout>
    <s:itemRenderer>
        <fx:Component>
            <r:FormItemRenderer>
                <s:TextInput width="100%" text="{data.@description}"/>
            </r:FormItemRenderer>
        </fx:Component>
    </s:itemRenderer>
</s:DataGroup>

Notes

You can use this solution with a DataGroup out of the box, but if you want to use it with a List (or any other component extending ListBase) there's one additional thing to consider:
FormLayout doesn't support layout virtualization, which is turned on by default for Lists (wouldn't make sense for normal use cases anyway). So you'll have to explicitly set useVirtualLayout to false to avoid NullPointerExceptions.

<s:List dataProvider="{_controls}" useVirtualLayout="false">
    ...
</s:List/>
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top