Expandable Table Rows in JavaFX

Not so fully featured 

WPF tables offer a nice feature for showing a row "details" that is not supported in Java (Swing or FX). Someone recently asked me if it was possible to implement something similar in Java FX. After a lot of Google searches I realized there is nothing out there in the community to accomplish anything even remotely like this. You know what that means right? I HAD to do it. Uncharted territory is so much fun!


Included Files

package: codemonkeycorner.expandabletablerows

IExpandableTableRow - we need some extra properties to provide this functionality
AbstractExpandableTableRow - Our new Row (You have to provide the expanded content)
ExpandableTableRowSkin - How we actually layout the row / expanded content

package: codemonkeycorner.expandabletablerows.example

Person - example data
PersonRow (extends AbstractExpandableTableRow) - provides content for the details view
TestRunner - Creates an example table using the person data and row


Example in Action .....


Not quite so fast.....

To be honest I thought I had solved this issue really quickly without much hassle, right up until I hit the sort button on a column. Thats when I realized that the rows are reused when you sort, the data associated with the rows changes. This is great for performance but caused a real headache for the expanding rows. That meant I could not keep the expanded state of a row local. There had to be some external keeper (the data itself in my example) to store that data. That also meant every time the item changed I needed to update the layout as well for size and content. It IS all complete now however due this problem I am still not entirely happy with the results. My original solution was a bit more clear cut and had better separation of concerns.


Styling Java - FX vs Swing

How much of a difference is there in styling FX over Swing? Taking a particular look and feel and implementing it in one over the other, which one would be faster? 

I had to do some exercises for work to show some information on these topics. I thought I would share a snippet of that here to demonstrate the difference. I had a specific look and feel to implement, and so chose some of those components to show. Here is one example, a button with rounded corners (all names and colors have been changed to protect the innocent)




JavaFX - Button Style

/**
 *
 * @author Patricia Bradford, www.CodeMonkeyCorner.com
 */
.root {
    -gradientColor:rgb(205, 250, 7); 
    -forecolor:rgb(200,200,200);
    -backcolor:rgb(85,85,85);
}
.button{
    -fx-background-color:-backcolor;
    -fx-text-fill:-forecolor;
    -fx-background-radius: 8;
    -fx-background-insets: 1;
    -fx-border-color:-forecolor;
    -fx-border-radius: 8;
    -fx-border-width:1;
    -fx-padding: 3 15 3 15;
}
.button:hover{
    -fx-background-color: linear-gradient(from 0% 0% to 100% 100%, -gradientColor, derive(-gradientColor, 90%));
}
.button:pressed{
    -fx-text-fill:-backcolor;
    -fx-background-color:-forecolor;
    -fx-border-color:-backcolor;
}

Swing - ButtonUI

/**
 *
 * @author Patricia Bradford, www.CodeMonkeyCorner.com
 */
public class RoundedButtonUI extends BasicButtonUI {

    private static final float arcwidth = 16.0f;
    private static final float archeight = 16.0f;

    protected final Color fc = new Color(205, 250, 7);
    protected final Color ac = new Color(150, 229, 76);

    protected Shape shape;
    protected Shape border;
    protected Shape base;

    @Override
    protected void installDefaults(AbstractButton b) {
    UIManager.put("Button.background", new Color(85,85,85));
    UIManager.put("Button.foreground", new Color(200,200,200));
        super.installDefaults(b);
        b.setContentAreaFilled(false);
        b.setOpaque(false);
        b.setBorderPainted(false);
        initShape(b);
    }
    
    @Override
    public void paint(Graphics g, JComponent c) {
        Graphics2D g2 = (Graphics2D) g;
        AbstractButton b = (AbstractButton) c;
        ButtonModel model = b.getModel();
        initShape(b);
    //ContentArea
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_OFF);
        if (model.isArmed()) {
            g2.setColor(c.getBackground());
            g2.fill(shape);
        } else if (b.isRolloverEnabled() && model.isRollover()) {
            paintFocusAndRollover(g2, c, fc);
        } else if (b.hasFocus()) {
            paintFocusAndRollover(g2, c, fc);
        } else {
            g2.setColor(c.getBackground());
            g2.fill(shape);
        }
    //Border
        g2.setPaint(c.getForeground());
        g2.draw(shape);
        g2.setColor(c.getBackground());
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_OFF);
        super.paint(g2, c);
    }

    private void initShape(JComponent c) {
        if (!c.getBounds().equals(base)) {
            base = c.getBounds();
            shape = new RoundRectangle2D.Float(0, 0, c.getWidth() - 1, c.getHeight() - 1,
                    arcwidth, archeight);
        }
    }

    private void paintFocusAndRollover(Graphics2D g2, JComponent c, Color color) {
        g2.setPaint(new GradientPaint(0, 0, color, c.getWidth() - 1, c.getHeight() - 1,
                color.brighter(), true));
        g2.fill(shape);
    }

    @Override
    protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) {
        ButtonModel model = b.getModel();
        FontMetrics fm = SwingUtilities2.getFontMetrics(b, g);
        int mnemonicIndex = b.getDisplayedMnemonicIndex();

        /* Draw the Text */
        if(model.isEnabled()) {
            /*** paint the text normally */
            if(model.isPressed())
                g.setColor(b.getBackground());
            else
                g.setColor(b.getForeground());
            SwingUtilities2.drawStringUnderlineCharAt(b, g,text, mnemonicIndex,
                                          textRect.x + getTextShiftOffset(),
                                          textRect.y + fm.getAscent() + getTextShiftOffset());
        }
        else {
            /*** paint the text disabled ***/
            g.setColor(b.getBackground().brighter());
            SwingUtilities2.drawStringUnderlineCharAt(b, g,text, mnemonicIndex,
                                          textRect.x, textRect.y + fm.getAscent());
            g.setColor(b.getBackground().darker());
            SwingUtilities2.drawStringUnderlineCharAt(b, g,text, mnemonicIndex,
                                          textRect.x - 1, textRect.y + fm.getAscent() - 1);
        }
    }

    @Override
    protected void paintButtonPressed(Graphics g, AbstractButton c) {
        Graphics2D g2 = (Graphics2D) g;
        ButtonModel model = c.getModel();
    //ContentArea
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_OFF);
        g2.setColor(c.getForeground());
        g2.fill(shape);
    //Border
        g2.setPaint(c.getBackground());
        g2.draw(shape);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_OFF);

    }
}


I could spend a few paragraphs talking about how the FX version is simple and clear; easy to see what is going on. I could tell you that the swing version is not; that it requires either knowledge of Swings 2D rendering/painting/utilties or digging through the base UI element code to determine how its done. However, I think anyone who looks at the two versions can determine all they need to know all by themselves.....



Creating a Java Swing MDI

Window Z Order

I have seen quite a few requests for this functionality. It can be frustrating that swings implementation almost forces you to use a single window due to lack of "z order" control for multiple windows. In truth its not so much an MDI interface (since we can achieve that through the use of JInternalFrame), but more that we want to be able to have a window hierarchy to keep windows in a particular Z order.

Why?

Almost every single time I have seen this question asked, I have also seen people criticize the querier about it. I find it strange but I often see this happen when we do not know a solution. A very quick example would be you need a toolbar above your display. You obviously dont want your tools disappearing behind it when you click on the main window. You could accomplish this with JInternalFrame, however if you want to be able to drag the toolbar to say a different monitor that will not work. Now technically Java already provides this to a point, specifically with (J)Dialogs as children of a window. The problem arises when you need multiple layers of windows rather than just one. 

A (not perfect) Solution

I have a simple library to accomplish this, however it does have limitations. Specifically there will be "flicker" sometimes when clicking on a different window. This is greatly reduced if you use undecorated windows; the flicker you are seeing is related to the activation of windows that is happening behind the scenes. This will also work for JavaFX since you can use a JFXPanel inside of the windows.

Download

How to use the library

Using the IJavaSwingMDIManager interface you can create your Z layers and child windows. It is recommended that you use the ZParent and ZChild windows provided in the library but you can provide your own windows if you desire. A quick overview of some of the methods:

Creating a manager:

There are several calls to create a new manager (createManager(...)). They all use some variation of these parameters:
  • basewindow: You can provide the bottom most layer window or let the system create it for you
  • z-layers: You can specify how many layers to create
  • dialogCreator: You can specify your own methods to create the child and parent windows if you desire


Creating child windows

Automatically:
  • createChildWindow: creates a window on the z level that you specify
Manually
  • getZParent: Gets the parent window for a particular z level
  • registerChildren: Register listeners on a child window
To manually create a child you will need to create the JDialog with the owner set as the parent of the Z level you want. You will then need to register that child with the service.
JDialog myDialog = new JDialog(mdiService.getZParent(0));
mdiService.registerChildren(myDialog);

I recommend you use the automatic method; you can specify the dialog creator if you just need to change the way the dialogs look or otherwise need access to the windows.

Creating a layered workspace:



    public static void main(String[] args)
    {
        JFrame frame = new JFrame();
        frame.setSize(new Dimension(800,800));
        frame.setTitle("Base Window");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        
        IJavaSwingMDIManager mdiInterface = new WindowManager(frame,3);
        
        JDialog dialog = mdiInterface.createChildWindow(0);
        dialog.getContentPane().add(new JLabel("Child of Z 0"));
        dialog.pack();
        dialog.setVisible(true);
        
        dialog = mdiInterface.createChildWindow(1);
        dialog.getContentPane().add(new JLabel("Child of Z 1"));
        dialog.pack();
        dialog.setVisible(true);
        
        dialog = mdiInterface.createChildWindow(1);
        dialog.getContentPane().add(new JLabel("Child of Z 1"));
        dialog.pack();
        dialog.setVisible(true);
        
        dialog = mdiInterface.createChildWindow(2);
        dialog.getContentPane().add(new JLabel("Child of Z 2"));
        dialog.pack();
        dialog.setVisible(true);
    }


Children of a higher z order cannot go below children of a lower z order; children of the same z order can.




Enjoy!


Patricia Bradford

WPF Dependency Properties vs INotifyPropertyChanged

Distinguishing between wants and needs 


If you are doing WPF development you will not get very far before running into this decision. Should you use DependencyObject(and DependencyProperty) or should you implement INotifyPropertyChanged to support binding. This question boils down into two really. When do you "need" to use one or the other, and when might you "want" to choose one over the other.


So when do we need  to use DependencyObject?

<MyControl MyDependencyProperty="{Binding PropertyPath}"  />

The only time you must use a DependencyObject is when you want someone to be able to make it the target of a binding expression. Typically (though not always) this is only the case if you have created a custom control with properties that you want to be bindable rather than as a view model. This is the one case where you cannot use INotifyPropertyChanged.

So when do we need  to use INotifyPropertyChanged?


  • If your object/property needs to implement a custom Equals/Hashcode.
DependencyObject seals both Equals and GetHashCode methods, so if you need to override you must use INotifyPropertyChanged.

  • If your object/property needs to be Serializable
DependencyObject is not marked as Serializable. DependencyProperty fields do not serialize correctly even if you mark them as Serializable. DependencyProperty values are not stored local to the object (if they are stored at all).

When might I want  to use one over the other?

Performance (memory and speed) - DependencyObject wins

  • Binding works faster with DependencyObject rather than an object that implements INotifyPropertyChanged.

  • DependencyObject uses less memory than a CLR(Common Language Runtime) object.
This can be a significant difference if objects are commonly using the default values. DependencyObject uses a static field for the default, and only creates storage space for the DependencyProperty if a value is set. This is the reason a DependencyObject is not serializable. 

However ....
It should be noted that in most cases you would never notice the difference. Only in cases with a very large property set, or with more real-time operations would you need the performance difference.

Multi-threaded - CLRs win

  • If you are using a background thread to create your objects you might want to use CLRs instead. 
DependencyObjects have thread affinity (they can only be accessed from the thread they were created on). Binding to a DependencyObject that you have created on a background thread will cause an exception. 

However ....
You could dispatch your object creation to the UI thread. Keep in mind though if you are constantly having to bounce thread calls to create your objects and set values then you may be losing much of the benefit of using your background thread for processing.

Control over notification - CLRs win

  • You get to decide when to send notification of changes. 
This can be useful if for instance you wanted to batch multiple changes into one UI operation.

However ....
You do get other very useful mechanisms built into the DependencyObject/DependencyProperty system. Validate and Coerce callbacks are very useful in certain situations.

In conclusion....

There is no best to use for all cases. Instead you should pick the one that suits your needs. Hopefully this post has helped you to determine which one that will be!


JavaFX Clock / Circle Layout

In a circle .....

A common request I see is for a layout that positions its children in a circle. CircleLayout.java (5.5KB) accomplishes this by extending JavaFX's Pane layout. Here is a quick walkthrough of its highlights.

Important Properties   

    /**
     * Stop Angle: The angle to layout the last child.
     */
    public DoubleProperty stopAngleProperty();
    public double getStopAngle();
    public void setStopAngle(double value);

    /**
     * Start Angle: The angle to layout the first child. Default is 0 (top center)
     */
    public DoubleProperty startAngleProperty();
    public double getStartAngle();
    public void setStartAngle(double value);
    
    /**
     * Inner radius: Layout will position the children in the outer ring
     * of the circle. (Halfway between the inner radius and outer radius)
     */
    public DoubleProperty innerRadiusProperty();
    public double getInnerRadius();
    public void setInnerRadius(double value);
    
    /**
     * Gets the outer radius of the circle. This is either the width or height 
     * whichever is smaller.
     * @return radius
     */
    public double getOuterRadius();

These allow you to layout the children in an arc around the circle starting and ending at any point along the circle.
For instance if i wanted to create a clock I might do something like this...

   CircleLayout layout = new CircleLayout(100);
   layout.getChildren().addAll(
               new Label("12"),new Label("1"),new Label("2"),
               new Label("3"),new Label("4"),new Label("5"),
               new Label("6"),new Label("7"),new Label("8"),
               new Label("9"),new Label("10"),new Label("11"));

Which looks like .....


Or you could also produce the same results with this
   CircleLayout layout = new CircleLayout(100);
   layout.setStartAngle(30);
   for(int i =1;i<13;i++)
   {
       layout.getChildren().add(new Label(i+""));
   }
By default, If you do not set a stop angle, the layout will do a full circle. However it does support layout in a specific arc such as
   CircleLayout layout = new CircleLayout(100);
   layout.setStartAngle(30);
   layout.setStopAngle(120);
   for(int i =1;i<13;i++)
   {
       layout.getChildren().add(new Label(i+""));
   }
Which produces this:


The layout also dynamically updates for certain changes such as:
Child size changes
Children added/removed
Layout size changes
Stop/Start angle changed
Inner radius changed

Enjoy!

New Blog and What is a code monkey?

New Blog

Well I have been intending to create this site for quite some time, however as usual free time is at a minimum. Ill give credit to a friend of mine for getting his up first (see renderloupe.com) -- and therefore motivating me to get this one up! I had a time trying to decide how to host/create the site as well as decisions on the domain name. My aforementioned friend went with wordpress which I almost did but then I found BlogEngine.Net. Ah Ha! Now i get more control, I can learn some aspx (web languages are becoming a necessary skill set), and get my blog up! 

So what shall the first post be about? Future ramblings will involve programming, mostly c# and java. Some projects of mine I would like to highlight, some wisdom (or foolishness) I have aquired over the years. However for this first post I think we need to define.... 

What is a code monkey? 

If you have not ever heard the song check it out here. There are quite a few youtube videos made around it.

The term is usually used as a derogatory label although it can be used to refer to anyone who writes code for a living. Some common meanings behind the term ....
  1. Someone who writes code so simple  a monkey could do it.
  2. Someone who only  writes code. (No design, requirements, etc)
  3. Someone who writes a lot  of code. 
As you might expect junior programmers are referred to often as code monkeys. It can also be used to deny responsibility.... "Hey i don't make the decisions, I am just a code monkey", "I am a code monkey, I just do what I am told" etc 

In my case however, I tend to use the term as a humbug. Ok so maybe I should define that as well since most people tend to think of Ebenezer and relate the word to an exclamation of grumpiness. A humbug however is
  • A joke, jest or hoax, something designed to deceive and/or mislead
A lot of people both inside and outside the field tend to think of what <insert code monkey term here (programmers/software engineers/developers)> do is pretty simple. That any kid sitting at home can "program a computer". See this rant. Being the mildly facetious person that I am, I took the term and ran with it. 

A "code monkey" is actually about as far from what I do as is possible. Yes I can "code". In fact according to some of my peers I can code at a rather insane rate. However that has way less to do with the simplicity of the code and more to do with designing ahead of time what I am going to write with a huge emphasis on reuse and generic code. Sometimes I write little helper applications to do mundane tasks for me. Sure it takes longer to write the program then just doing it the first time, but once i have to repeat this task 4 or 5 .... hundred times it makes a huge difference.

This made me stand out a bit as a new programmer, I was a "code monkey extraordinaire". When what i was doing was not very code monkeyish at all! Nowadays however I am a senior member of a rather unique team. While I still code quite a bit my claims to fame now are usually for architecture design, problem detection/solving, quick adaptation to new languages/technologies, and being able to take whatever the designers(UX) hand me and make it a reality. 

Yet I still go by the code monkey label, my own personal humbug.