如何处理SWT Shell(和Dialogs)?

时间:2011-12-21 20:37:34

标签: java swt eclipse-rcp dispose

处理SWT炮弹的正确方法是什么?我按照Dialog API文档中给出的模板创建了一些成功的Dialogs。 SWT API for Dialog says

  

用户定义对话框的基本模板通常看起来很像   像这样:

 public class MyDialog extends Dialog {
        Object result;

        public MyDialog (Shell parent, int style) {
                super (parent, style);
        }
        public MyDialog (Shell parent) {
                this (parent, 0); // your default style bits go here (not the Shell's style bits)
        }
        public Object open () {
                Shell parent = getParent();
                Shell shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
                shell.setText(getText());
                // Your code goes here (widget creation, set result, etc).
                shell.open();
                Display display = parent.getDisplay();
                while (!shell.isDisposed()) {
                        if (!display.readAndDispatch()) display.sleep();
                }
                return result;
        }
 }

我创建的对话框在Windows上有自己的任务栏图标,就像我期望的对话框一样。我现在想创建一些Shell,如果我理解正确在Windows上接收自己的任务栏条目?

与上述API文档中给出的说明相反,我还看到an article似乎暗示如API文档中所示的while循环是错误的方法。相反,本文建议使用close事件监听器来处理Shell。

处理SWT炮弹的正确方法是什么(当涉及主题时,也是对话)?

1 个答案:

答案 0 :(得分:10)

希望我能帮助解释这里发生的事情。

Dialog基本上只是一个方便的子类类,因为Shell本身不应该是子类。如果您愿意,可以根本不使用Dialog创建shell。在这种情况下,您的MyDialog类是其他人可以用来重复打开相同类型对话框的类。

只要shell打开,这段代码就会驱动SWT事件循环(单击shell上的关闭按钮会默认配置shell):

            while (!shell.isDisposed()) {
                    if (!display.readAndDispatch()) display.sleep();
            }

您必须定期致电Display.readAndDispatch以防止您的SWT应用程序“锁定”。基本上它会导致应用程序正确处理来自操作系统的所有传入事件(键盘和鼠标事件,重新绘制事件等)。 readAndDispatch基本上从应用程序的事件队列中获取一个事件并调用正确的侦听器。在Eclipse RCP应用程序中,工作台通常负责“抽取”事件循环,您不必乱用它。 Here's a little more info about the event loop.

在此上下文中“手动”抽取事件循环的目的是防止MyDialog.open在未处置shell时返回,但仍使应用程序“挂起”。如果你的MyDialog.open方法试图等待处理shell,但它没有引发事件循环,那么你的应用程序会“锁定”,因为没有事件循环运行,shell就没办法了被告知它应该被处置!

可以创建shell而不使用这种模式。这是一个非常简单的SWT应用程序的例子,它可以同时打开大量的shell并且只要其中至少有一个仍然打开就会一直运行(我省略了包声明和导入):

public class Shells {
    private static int numDisposals = 0;

    public static void main(String[] args) {
        Display d = Display.getDefault();

        for (int i = 0; i < 5; i++) {
            Shell s = new Shell(d);
            s.open();
            s.addDisposeListener(new DisposeListener() {
                @Override
                public void widgetDisposed(DisposeEvent arg0) {
                    numDisposals++;
                }
            });
        }

        while (numDisposals < 5) {
            while (!d.readAndDispatch()) {
                d.sleep();
            }
        }
    }
}

请注意,我在每个shell中添加DisposeListener,以便在shell关闭时执行某些操作。您还可以使用IShellListener直接监听close事件,甚至实际阻止它(例如,如果shell包含未保存的工作,您可能想要这样做)。这是对第一个启动5个shell并随机阻止你关​​闭它们的程序的烦人的修改:

public class Shells {
    private static Random r = new Random();
    private static int numDisposals = 0;

    public static void main(String[] args) {
        Display d = Display.getDefault();

        for (int i = 0; i < 5; i++) {
            Shell s = new Shell(d);
            s.open();
            s.addShellListener(new ShellAdapter() {
                @Override
                public void shellClosed(ShellEvent e) {
                    boolean close = r.nextBoolean();
                    if (close) {
                        System.out.println("Alright, shell closing.");
                    } else {
                        System.out.println("Try again.");
                    }
                    e.doit = close;
                }
            });
            s.addDisposeListener(new DisposeListener() {
                @Override
                public void widgetDisposed(DisposeEvent arg0) {
                    numDisposals++;
                }
            });
        }

        while (numDisposals < 5) {
            while (!d.readAndDispatch()) {
                d.sleep();
            }
        }
    }
}

希望这有助于使事情变得更加清晰!


编辑添加:我不完全确定为什么你没有为你的shell获取一个Windows任务栏项,但我怀疑它与你传递给shell的构造函数的样式标志有关。我的示例中的shell没有样式标志,它们都有一个任务栏图标。