Set Table Row Background Color

I have been trying to find a way to set a table row’s background color depending on a setting in that row. The only thing I’ve seen about this is making a CSS file with style names and using a CellRenderer. This doesn’t work for me, since the color is user-selectable, and could be any color represented by RGB or hex value, so loading a CSS file with every possible color is not really practical.

I wouldn’t think setting a row’s color could be so complicated. Is there a better way to do this? Thanks.

What you need is dynamic style injections. I have made such a component, but haven’t had any time to package it as a proper add-on.

You can find the sources here:
VCSSInject.java
(client side code) and
CSSInject.java
(server side component).

You’re free to package them as an add-on with Apache License.

Currently the only pure Vaadin way to inject CSS in a Table is to wrap the cell contents within a CssLayout, which allows CSS injection.

Table table = new Table("Colorful Table");
table.addStyleName("colorful");
table.setPageLength(16);

// We wrap cell contents inside a CssLayout, which
// allows CSS injection.
table.addContainerProperty("Color", CssLayout.class, null);
        
for (int i=0; i<16; i++) {
    final int color = 255-i*16;
    
    // Get hexadecimal representation
    StringBuilder sb = new StringBuilder();
    new Formatter(sb).format("#%1$02x%1$02xff", color);
    final String colorcode = sb.toString();

    // Stylable wrapper for the cell content 
    CssLayout content = new CssLayout() {
        @Override
        public String getCss(Component c) {
            return "background: " + colorcode + ";";
        }
    };

    // The actual cell content
    Label label = new Label("Here's color " + colorcode);
    label.setSizeUndefined();
    content.addComponent(label);

    table.addItem(new Object[] {content}, new Integer(i));
}

See the
on-line example
.

You need some CSS trickery to disable the default padding in table cells.

I tried the above example, but it seems to require me to set the CssLayout object as the item’s ID, which is a problem because the ID is currently set to the associated row ID in the database, which I rely on for other functions including updating the table items. Is there a workaround for this?

This is how I’m adding rows. I subclass Table to encapsulate the functionality I need and the constructor contains this:


        this.addContainerProperty(CAPTION_PROPERTY, String.class, "(no document)");
        this.addContainerProperty(WAITINGON_PROPERTY, String.class, "");
        this.addContainerProperty(DUEDATE_PROPERTY, Date.class, null);
        this.addContainerProperty(STATUS_PROPERTY, String.class, "");
        this.addContainerProperty(DESCRIPTION_PROPERTY, String.class, "");
        this.addContainerProperty(DATEADDED_PROPERTY, Date.class, null);
        this.addContainerProperty(WORKFLOWNAME_PROPERTY, String.class, "");
        this.setItemCaptionMode(ITEM_CAPTION_MODE_PROPERTY);
        this.setItemCaptionPropertyId(CAPTION_PROPERTY);
        this.setColumnHeaders(new String[] { "Document Name", "Waiting On", "Due Date", "Status", "Description", "Date Added", "Workflow Name" });

Then, when an item needs to be added or updated, I call the updateDocument method in the table subclass, which does this (it’s kind of messy right now because of the CssLayout attempt:


    public void updateDocument(DocumentWorkflowSummary summary) {
        int id = summary.getDocument().getDocumentID();
        Item item;
        if (!this.containsId(id)) {
            item = this.addItem(id);
        } else {
            item = this.getItem(id);
        }

        if (summary.getWorkflowStep() != null) {
            final Color statusColor = summary.getWorkflowStep().getWorkflow().getStatus().getColor();
            System.out.println("TEMP COLOR: " + statusColor.toString());
            if (statusColor.getAlpha() > 0 && statusColor != Color.WHITE) {
                CssLayout content = new CssLayout() {
                    @Override
                    public String getCss(Component c) {
                        System.out.println("TEMP CSS COLOR: " + Integer.toHexString(statusColor.getRGB() & 0x00ffffff));
                        return "background: " + Integer.toHexString(statusColor.getRGB() & 0x00ffffff);
                    }
                };

                Label caption = new Label(summary.getDocument().getName());
                content.addComponent(caption);
                item.getItemProperty(CAPTION_PROPERTY).setValue(content);
            } else {
                item.getItemProperty(CAPTION_PROPERTY).setValue(summary.getDocument().getName());
            }

            item.getItemProperty(WAITINGON_PROPERTY).setValue(summary.getWaitingOn().toString());
            item.getItemProperty(DUEDATE_PROPERTY).setValue(summary.getDueDate());
            item.getItemProperty(STATUS_PROPERTY).setValue(summary.getWorkflowStep().getWorkflow().getStatus().getStatusName());
            item.getItemProperty(WORKFLOWNAME_PROPERTY).setValue(summary.getWorkflowStep().getWorkflow().getName());

        } else {
            item.getItemProperty(CAPTION_PROPERTY).setValue(summary.getDocument().getName());
        }

        item.getItemProperty(DESCRIPTION_PROPERTY).setValue(summary.getDocument().getDescription());
        item.getItemProperty(DATEADDED_PROPERTY).setValue(summary.getDocument().getDateAdded());
    }

It actually uses an Integer for the item ID, see the “[tt]
new Integer(i)
[/tt]”. The special [tt]
addItem()
[/tt] is a convenience method in Table that essentially does what you do with [tt]
getItemProperty()
[/tt] and [tt]
setValue()
[/tt] in your code.

Your code looks like it could work. It worries me a bit that you define the property class as String, but then put a CssLayout there. You should only put objects of one type in a column. I’m suprised that it doesn’t throw an error or something.

Notice that you could use GeneratedColumn for the colored column. That way you wouldn’t have to mess with the properties in the column. Either solution should work.

The properties were made strings as part of an example I saw of how to load data into a table. How would I use a GeneratedColumn to color a row? Do you have any example code? I Googled but could not find anything.

I don’t really like the idea of wrapping every cell in the row into a CssLayout because I do have Date objects in some of the columns that get formatted using the overridden formatPropertyValue method. Wrapping the Date in a Label inside of a CssLayout would take away the ability to format that date and would break sorting and all of that. Plus I keep hearing that I would need to do some CSS tricks for the cell spacing.

So if I can do this the way I have it, but use a GeneratedColumn to color the row instead of using a CssLayout hack, I would like to explore that route.

In case the GeneratedColumn thing doesn’t work out, how would I go about using this? I don’t know how to package addons or anything like that.

The easiest way would be to use the Eclipse plugin to package it. Just create a new Vaadin Widget, and replace the code in the stubs it creates with the CSSInject code I provided. Then use Eclipse’s Export and select Vaadin Directory Add-on.

Here’s a video how-to:
Creating and packaging Vaadin add-ons

UPDATE:

Oh, sod it, I just bit the bullet and packaged it myself :slight_smile:


CSSInject add-on

Not tested in anyway, I just wanted to get this out of my hands. If it doesn’t work out of the box (remember, you need to recompile the widgetset), I’ll try to find the time to fix it.

You rock Jouni! Thanks for doing that. I’m sure I’m not the only one looking for something like this. This will make any future styling problems much easier to tackle. I have not tried the add-on yet, but I will in the next day or two and I’ll be sure to give you feedback on it.

For anyone else that has had a similar problem that they need to solve, I will quickly go over something I tried that failed:

ATTEMPT:
I tried putting Labels in each cell, setting content mode to XHTML, then wrapping the text with a

tag with a style attribute. I also tried .

WHY IT FAILED:
This works, but looks bad. It messes up sorting, and doesn’t allow the columns to size properly to the content in the cell.

TEMPORARY SOLUTION:
Until I get a chance to use the new CSSInject add-on, the only solution I came up with was to create a custom theme, inheriting the reindeer theme, then making styles that set the background color for each of the 140 or so named colors. Then make a ColorUtil class that lists all the named colors as they are in the CSS file, along with the RGB code of each of the 140 or so colors. Pass the ColorUtil class a Color object, which finds the nearest named color. When found, it passes that style name to the table’s setCellStyleGenerator method.

It took a couple of hours to make the CSS file and the ColorUtil class and I’m sure I messed up a couple of the RGB codes along the way. Although, it works! It’s just not ideal, especially if you plan on allowing a user to select a color using a color wheel or something.

So, assuming the CSSInject works as expected, that add-on is the best solution to the problem.

Thanks again Jouni!

Hi,

thanks for that hint. This approach seems to work well within a generated column. I only have one issue. The color will not be set on initial table load. The color is visible as soon as a repaint action is triggered. So just sorting or collapsing the columns will make the colors visible. Do you know how to solve that issue? Is there a event that can to be triggered manually to force showing the colors? (request repaint, reset row cache don’t have any effect)

Thanks.

Regards
Karsten