Resize DataGrid When Shown - apache-flex

I'm trying to write my flex application so that it restores the user's settings when it starts up. The application has 3 data grids (only one is visible [on screen] at any time) and I simply save the state of the grids if the user ever does any resize events.
Saving the state works perfectly, the problem comes when restoring. It seems that only the visible data grid gets restored properly. On all the other grids, the widths are all wrong and do not match the settings I restored from.
The problem is that, when the application starts, the data grids are not sized correctly. Each data grid takes up the full screen, but initially they are only given a very small size (ie. 200 width)
Is there some way to force the data grid to resize properly when it is created? Or is it possible to know when the data grid is "maximized" I don't want to hook up to the resize event - if possible - because I would get that quite frequently, and I only want to restore the settings once.
Here's the code I'm using to restore the column widths:
var grid:DataGrid = ...;
... for each column ...
... parse width into width (ie. width is now "320")
// If it's in the config, it's visible
col.visible = true;
col.width = width;
... After loop ...
grid.invalidateSize();
grid.invalidateDisplayList();
Edit:
Here's the info you guys requested:
The settings are saved into a database (the flex proejct is connected to JBoss). I simply save a CSV type thing with the column's name and it's width. I know this works because the one data grid that is visible when the application starts gets sized correctly.
Yes, the grids use relative sizes (ie. 100%) -- I can't really change that because I want the grid to take up all available space in its container -- and I want the user to be able to resize their browser and still have it look right.
Each grid is contained in its own Hbox
The data grids are created when the application starts -- you can only see one of them though. They are in a tab navigator type thing, but it's creation policy is set to "all" so they can be populated with data (the population takes some time, and typically the user sits on the front grid for a little while.)
I can't put my resize stuff in the creationComplete handler because I end up with the same problem. The grid's size is like 50 right after it's created (it should be like 1000, 100% of the page width) -- and then I go to set the sizes of the columns (which are obviously > 50) and the resize does nothing

A couple thoughts:
Resize during CreationComplete(): When are your datagrids being created? Can you declare a method like resizeDataGrid() when the creationComplete() event fires for the datagrid? If you are using datagrids in, say, a TabNavigator, the default creation policy is to delay component creation until the user uses the component (e.g. makes the tab active).
Fixed Sizes: Are your datagrids using a fixed size, or a relative size? Relative sizing (e.g. width='50%') of a datagrid or datagrid columns is buggy in Flex, and the best workaround is to set a pixel width of both the datagrid and the datagrid columns
Storing User Preferences: How are you storing your user preferences? Using a SharedObject to store datagrid widths is probably the best option.
Spark Containers: How are the datagrids contained? For example, do they each sit within a VGroup, or are they not contained at all? I've found that using Spark layout tools (like a VGroup) to contain your datagrids improves datagrid layout issues.

What we did was saved off the column width on initialization and then reset the widths on rerender of the grid, this will occur on the first draw of the grid and subsequent resizes.
the only problem with this was that it requires columns to be resizable but does not allow the user to resize
// saves column width - this is intended to save width percent which
// will only be available on initialization, after that I beleive widths
// will be fixed size
private function saveOffColumnWidths(columns:ArrayCollection):void
{
for each(var column:TDMSEDataGridColumn in columns)
{
originalColWidths.push(column.width);
}
}
// resets the column widths - the saved widths are assumed to be width percentages
private function resetColumnWidths():void
{
dataGrid.columns[0].width=20; // set the check col seperately
for (var pos:int=0; pos<dataGrid.columnCount-1;pos++ )
{
dataGrid.columns[pos+1].width = (dataGrid.width - 20) * originalColWidths[pos];
}
}
protected function dataGrid_renderHandler(event:Event):void
{
resetColumnWidths();
}

Related

Flex DataGrid columns won't resize

I am having serious problems trying to resize columns in a DataGrid. I've been at it for over a day now and am at my wit's end with a headache to boot.
Essentially, I have a TabNavigator component with NavigatorContent children inside. In each one of the NavigatorContent children, I have a DataGrid to which I'm setting the width to 100% (this is needed to be able to handle resizing of the browser window). I am using the excellent filterable DataGrid from Iwo Banas as the DataGrid in each tab.
Now, I am making visible/invisible some columns in each of the DataGrids and this is working fine. However, I find that the column widths are not being set correctly. Whenever I set the column widths (using this code), all of the columns seem to be set to the correct width except for the ones that I have recently made visible and the column immediately to the left of these. The ones recently made visible are very small (though I set their width to 30) and the one to the left of these columns is very large (though I've also set its width to 30).
I think it's something to do with the life cycle of the DataGrids because the first DataGrid behaves fine. It's when I click on the other tabs that I find that the widths of the other DataGrids have not been set correctly.
However, if I "see" one of those problem DataGrids (i.e. it appears on the screen) and the code which resizes the columns runs again, the columns are correctly sized.
I have tried a number of things recommended all over the internet including the questions listed below but to no avail.
This is the code I'm using to resize the columns (taken from this answer)
public static function resizeColumn(col:DataGridColumn, size:int):void
{
var owner:* = col.mx_internal::owner
col.mx_internal::owner = null;
col.width = size;
col.mx_internal::owner = owner;
}
Any and all help would be greatly appreciated.
I have already looked at the following answers.
Flex DataGrid column width: one column to rule them all?
Flex 3 DataGrid Column Width Problem
Flex DataGrid Column Width (<-- this answer got me closest)
Unable to change the column width dynamically in flex datagrid
So, after a huge amount of head wrecking, I finally found a solution (at least to my problem, not sure if it would help anyone else but thought I'd post it here anyway as you never know what could help someone else).
I decided that since calling the resize code when the DataGrid is on screen works then I needed to just do that. So I thought of putting the resize code for the relevant DataGrid depending on the tab clicked. Not that straightforward as there didn't seem to be a straightforward way to implement a click handler for the tabs. I did a quick Google and found this solution.
Essentially, you add a click handler for each tab button by cycling through the children of the tab navigator, getting the button and adding an event listener.
Then, you do what you need to do in the handler.
Code example (slightly different to one from website):
protected function tbGridArea_creationCompleteHandler(event:FlexEvent):void
{
for ( var i:int=0; i < tbGridArea.getChildren().length; i++ )
{
var tab:Object = tbGridArea.getTabAt(i);
tab.addEventListener( FlexEvent.BUTTON_DOWN,myTabClickHandler );
}
}
private function myTabClickHandler(event:Event):void {
switch(event.currentTarget.label) {
// do whatever you need to do here
}
}

Is it possible to have Dojo/Dijit DataGrid autoheight functionality based on the parent div size instead of a number of rows?

I have a datagrid that is updated periodically and the number of rows inside it grows steadily over time. It is inside of a parent div with a height of 60% of the screen.
If I set autoheight to, say, 5 rows, the table works properly. When a sixth row is added, a scrollbar appears within the datagrid and I can scroll up/down and the headers remain fixed at the top and visible. Unfortunately, once I have a lot of data, this is a waste of space -- I have 60% of the screen's height to work with, but only 5 rows are being shown at a time.
If I set autoheight to false, the scrollbar that appears is attached to the parent div. Scrolling up/down allows me to see the data, but the headers at the top of the grid scroll out of view.
Is there a way to ask the datagrid to show as many rows as it can fit and provide a scrollbar for the rest?
---- EDIT ----
Setting autoheight to false would be exactly what I want if the datagrid would resize itself along with the parent when I resize my browser. Is there a good way to achieve that?
I think you're looking for grid.resize(); If you look at the file "Grid.js.uncompressed.js" in the dojox nightly files here: http://archive.dojotoolkit.org/nightly/dojotoolkit/dojox/grid/ You can see exactly what it does. The dataGrid should inherit this method, if you're using it. One way to use it is to resize the containing div based on the window's height, and then resize the grid inside:
function changeHeight() {
document.getElementById("div Id in which the dojo grid is there").style.height ="your desired height";
dijit.byId('Id of the dojo grid').resize();
dijit.byId('Id of the dojo grid').update();
}
Another option method is do do something like this:
function resizeGrid() {
// do whatever you need here, e.g.:
myGrid.resize();
myGrid.update();
}
dojo.addOnLoad(function() {
dojo.connect(window, "onresize", resizeGrid);
});

Auto-sizing and positioning in Flex

I am working on a flex app that uses XML templates to dynamically create DisplayObjects. These templates define different layouts that can be used for each page of content in the app (ie , 2 columns, 3 columns etc etc). The administrator can select from one of these and populate each area with their content.
The templates add one of 3 types of DisplayObject - HBox, VBox or a third component - LibraryContentContainer (an mxml component that is defined as part of the app) - which is effectively a canvas element with a TextArea inside.
The problem that I am getting is that I need each of these areas to automatically resize to fit the length of the content but don't seem to be able to find an effective way to do so.
In the LibraryContentContainer, when the value of the TextArea is set, I am calling .validateNow() on the LibraryContentContainer. I then set the height property on both the TextArea and LibraryContentContainer to match the textHeight property of the TextArea.
In the following example, this is the LibraryContentContainer, viewer is the TextArea and the value property of the TextArea is bound to this.__Value. v is the variable containing the content for the textarea
this.__Value = v;
this.validateNow();
this.viewer.height = this.viewer.textHeight;
this.height = this.viewer.height;
This works to a degree in that the TextArea grows or shrinks depending on the length of content, but it's still not great - sometimes there are still vertical scrollbars even tho the size of the TextArea has grown.
Anyone got any ideas?
Thanks
Adam
I think the problem lies not with your dynamically added components, but with the component they're being added to. How is the height of this component being determined? If you set verticalScrollPolicy and horizontalScrollPolicy on this container to off, do your scrollbars disappear? If that's the case, then you'll need to look at how this component is sized rather than your hbox, vbox, or whatever it is you're adding.

When using a container powered by itemrenderers, is there a way to make the rendering not stall?

I'm developing on Flex 4. I have a datagrid container and custom itemrenderer with a text field, a button and a few boxes. Every time some data is displayed, the app stalls for a second or two before rendering completely. Is there any way to make it render more fluidly or render one after another...?
Is your renderer setting percentage-based dimensional information in a commitProperties() or updateDisplayList() override? For example, if you set a percentWidth and percentHeight, you want to do that only once, so do that in the constructor.
If you do it in another method that gets called over and over, your whole grid will be resizing and rerendering each cell until every cell has been satisfactorily sized and measured. So if your grid is 10 columns by 20 rows, that means it has to render completely at least 200 times before it's happy with its cell dimensions.

Making a Flex DataGrid scroll smoothly

I've noticed that the default behaviour for a DataGrid's vertical scroll bar is to scroll one row at a time. This is all well and good when the rows are all uniform and small (e.g. displaying a single line of text), but gets really ugly as soon as you have rows with variable heights.
I'm curious, is there a way to make DataGrid scrolling "smooth"? For instance, is there a way to have the DataGrid scroll by a set number of pixels, lines of text, etc. rather than scrolling one row at a time?
So far, the only solution I've managed to come up with is to place the DataGrid in a Canvas and have the Canvas do the scrolling instead of the DataGrid. The issue with this approach, though, is that as soon as the Canvas scrolls far enough, the DataGrid headers scroll off-screen. Ideally, I'd like to get the smooth-scrolling nature of the Canvas, but also keep the DataGrid headers visible. Is that possible?
The way that ItemRenderer's work in Flex 3 makes smooth scrolling difficult to achieve. Basically Flex recycles item renderers scrolled off of the top of the list as the display objects used for new data at the bottom of the list. Adobe's implementation of most list components in Flex 3 creates and adds these items as they come on to the screen rather than just off the screen, so they "pop in" and smooth scrolling isn't available. I'm not sure why they couldn't have done it in a similar manner for items +/- one position above or below the current scroll pane, but they didn't, and we're stuck with sticky scrolling by default.
Work-arounds do exist, though the one you've noted (dropping the datagrid into a canvas) negates the display-object saving intention of item renderers and incurs a performance cost. This will be fixed for most list-based Flex components in Flex 4, though it won't be fixed immediately for DataGrid. The DataGrid / AdvancedDataGrid component is maintained by a separate team based in India, last time I heard, and so it tends to be a bit behind the rest of the SDK.
I'd recommend trying something similar to this implementation of a smooth-scrolling list by Alex Harui. I'm not sure exactly how well it'd work for DataGrid or AdvancedDataGrid, but this is the most intuitive technique I can think of for making the list scroll correctly.
Try this... It's still based on Alex's code that was mentioned above. His should still be a great start for removing the snap-to-row behavior. Original source:
http://blogs.adobe.com/aharui/2008/03/smooth_scrolling_list.html
Alex's original some code for smooth vertical scrolling but that was not an issue I had with the DataGrid. It was smooth scrolling horizontally that I needed. I am using the DataGrid in an unorthodox manner for analyzing plain text reports output by our database (great way of providing visual feedback on a document). The code below allows content to go off screen and the user can scroll without that snap-to-column behavior.
You can adapt this to use the same math routines for vertical scrolling and then it will make scrolling possible and ignore the snap to row behavior. In particular switch the usage of the listContent.move method to move the contents vertically and use a inverse of the rounded pixel value you calculate from the vertical scroll bar (as opposed to my using the horizontal).
This method is bit simpler than Alex's method from the link above - a lot less code so try adapting and see how it works.
override protected function scrollHandler(event:Event):void
{
// Override the default scroll behavior to provide smooth horizontal scrolling and not the usual "snap-to-column" behavior
var scrEvt:ScrollEvent = event as ScrollEvent;
if(scrEvt.direction == ScrollEventDirection.HORIZONTAL) {
// Get individual components of a scroll bar for measuring and get a horizontal position to use
var scrDownArrow:DisplayObject = horizontalScrollBar.getChildAt(3);
var sctThumb:DisplayObject = horizontalScrollBar.getChildAt(2);
// I replaced maxHorizontalScrollPosition in Alex's code with "1300" to fix my exact application. In other situations you may finding using some property or different value is more appropriate. Don't rely on my choice.
var hPos:Number = Math.round((sctThumb.y - scrDownArrow.height) / (scrDownArrow.y - sctThumb.height - scrDownArrow.height) * 1300);
// Inverse the position to scroll the content to the left for large reports
listContent.move(hPos * -1, listContent.y);
}
// Go ahead and use the default handler for vertical scrolling
else {
super.scrollHandler(event);
}
}

Resources