5

I know that there's a SwingUtilities.updateComponentTreeUI(Component c) method but it doesn't work perfectly. For example, I have a JFileChooser and the current look and feel is Windows, then I change the look and feel to Nimbus with SwingUtilities.updateComponentTreeUI(mainWindow), and the main window's style is changed correctly, but when I show the file chooser with the JFileChooser.showOpenDialog(Component parent) method, it's still in Windows look and feel. The same happens if I show a popup dialog with the JPopupMenu.show(Component invoker, int x, int y) method.

Any solution to this issue?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Zhao Yi
  • 2,335
  • 7
  • 23
  • 27
  • To be clear, you also adjusted the current L&F with [`UIManager.setLookAndFeel()`](http://docs.oracle.com/javase/7/docs/api/javax/swing/UIManager.html#setLookAndFeel%28java.lang.String%29)? – Duncan Jones Apr 23 '13 at 09:13

2 Answers2

7

Assuming that value is the class name of the new look-and-feel, here is the snippet to update all windows and sub-components:

public static void updateLAF(String value) {
    if (UIManager.getLookAndFeel().getClass().getName().equals(value)) {
        return;
    }
    try {
        UIManager.setLookAndFeel(value);
        for (Frame frame : Frame.getFrames()) {
            updateLAFRecursively(frame);
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (UnsupportedLookAndFeelException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public static void updateLAFRecursively(Window window) {
    for (Window childWindow : window.getOwnedWindows()) {
        updateLAFRecursively(childWindow);
    }
    SwingUtilities.updateComponentTreeUI(window);
}
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • Which will not help if you cache a Swing component which is not part of the Swing hierarchy in your own code. – Robin Apr 23 '13 at 09:27
  • @Robin Indeed, nice catch for the cached components. Yet, if it is embedded in another component which is the hierarchy, another option is to override `updateUI`, call `super.updateUI` and also forward the call to the file-chooser. – Guillaume Polet Apr 23 '13 at 09:28
  • 1
    I know this thanks to experience. Already spend some time on this problem in my own code. Typically for a file chooser it is not contained in the hierarchy, but only opened in a dialog when needed – Robin Apr 23 '13 at 09:46
  • `Window[] wins = Window.getWindows();` instead of` Window window : frame.getOwnedWindows()` could be works for all containers that could be `isDisplayable()`, `isDisplayable(`) returns containers with removed `Rootpane` or if `DecorationsTyp`e is changed programatically – mKorbel Apr 23 '13 at 10:11
  • Updating all windows returned by `Window.getWindows()` doesn't affect my file chooser that has not yet displayed in a dialog. – Zhao Yi Apr 24 '13 at 01:55
  • @ZhaoYi Please also refer to Robin's comments: components that are cached and not part of the component hierarchy will not be updated. Yet, if your component is cached within another component which is part of the hierarchy, you could simply override `updateUI`, call `super.updateUI` and then forward the call to your cached component `updateUI()` method. – Guillaume Polet Apr 24 '13 at 07:18
  • One problem is that some components are created from 3rd party libraries, and I'm unable to explicitly update its UI. And do you know how to add a listener to `UIManager` as pointed out by Robin? – Zhao Yi Apr 24 '13 at 07:43
  • @ZhaoYi Probably with `UIManager.addPropertyChangeListener` and watch for the change of property `"lookAndFeel"`. – Guillaume Polet Apr 24 '13 at 07:59
  • It doesn't help if I can't get all references to components. – Zhao Yi Apr 24 '13 at 08:44
  • @ZhaoYi How can't you get references to some components but yet you manage to display those components? If you are absolutely unable to reach those components, then there is not much you can do except check that, once displayed, the components use the proper L&F and if not call updateUI()/updateComponentTreeUI. – Guillaume Polet Apr 24 '13 at 08:49
  • Sure that's possible, e.g. a popup up menu created by a 3rd library, and is shown by `JPopupMenu.show`. In this case, how could I get the reference to that popup menu? – Zhao Yi Apr 24 '13 at 08:55
6

Calling SwingUtilities.updateComponentTreeUI(mainWindow) will only update the Swing components in the Swing hierarchy under mainWindow.

If you store the JFileChooser somewhere in your code (e.g. in a field of a class) without showing the JFileChooser the chooser will not be updated by the SwingUtilities.updateComponentTreeUI(mainWindow) call. You can work around this by adding a listener to the UIManager yourself and call SwingUtilities.updateComponentTreeUI(myStoredFileChooser) from that listener when the look-and-feel is changed.

Make sure you do not create a memory leak with this, e.g. let that listener only have a WeakReference to the JFileChooser (as the lifetime of the UIManager equals the lifetime of the JVM)

Robin
  • 36,233
  • 5
  • 47
  • 99
  • How to add a listener to `UIManager`? – Zhao Yi Apr 24 '13 at 01:59
  • Great job on highlighting the `WeakReference`. – Duncan Jones Apr 24 '13 at 06:42
  • @ZhaoYi did you even bother to open the javadoc of the `UIManager` class. In that case I am sure you would have found the [`addPropertyChangeListener`](http://docs.oracle.com/javase/7/docs/api/javax/swing/UIManager.html#addPropertyChangeListener(java.beans.PropertyChangeListener)) method – Robin Apr 24 '13 at 08:04
  • As I've said below, some components may be created by 3rd party libs, thus I'm unable to get their references. – Zhao Yi Apr 24 '13 at 08:43