Jframe 中的 2 个 Jdialogs 的 setModal 问题

2022-01-17 00:00:00 modal-dialog java swing jframe jdialog

当我设置我的第一个 JDialog 模态和第二个非模态时,我遇到了问题.

这是我正在尝试实现的功能:

  1. 点击测试对话框!"按钮,一个名为 Custom Dialog 的 JDialogMain 将打开.
  2. 如果在 Custom Dialog Main 中单击是"选项,则另一个JDialog 名为 Custom Dialog Search 将打开.
  3. 如果在自定义对话框搜索中单击是"选项,则自定义对话框主应该放在前面.
  4. 我应该能够选择任何 JDialog.例如,如果我选择自定义对话框搜索,其他对话框应该去反之亦然.

我面临的问题是,当我在自定义对话框主中单击是"时,自定义对话框搜索会显示在主对话框的后面.

发生这种情况是因为我将 自定义对话框搜索 设置为非模态.如果我使用此对话框模式,它会正确显示,但在我单击是"后 自定义对话框主 不会出现在前面.

我什至尝试将 CustomDialogSearch 的父级设置为 CustomDialog,但行为仍然不正确.

下面是我正在测试的示例代码.

import java.awt.event.ActionListener;导入 javax.swing.JFrame;导入 javax.swing.JButton;导入 java.awt.event.WindowAdapter;导入 java.awt.event.WindowEvent;导入 java.awt.event.ActionEvent;导入 java.awt.Dimension;公共类 TestTheDialog 实现 ActionListener {JFrame mainFrame = null;JButton myButton = null;公共TestTheDialog(){mainFrame = new JFrame("TestTheDialog Tester");mainFrame.addWindowListener(new WindowAdapter() {公共无效 windowClosing(WindowEvent e) {System.exit(0);}});myButton = new JButton("测试对话框!");myButton.addActionListener(this);mainFrame.setLocationRelativeTo(null);mainFrame.getContentPane().add(myButton);mainFrame.pack();mainFrame.setVisible(true);}公共无效actionPerformed(ActionEvent e){if(myButton == e.getSource()) {System.err.println("正在打开对话框.");CustomDialog myDialog = new CustomDialog(mainFrame, true, "Custom Dialog Main?");System.err.println("打开对话框后");如果(myDialog.getAnswer()){System.err.println("CustomDialog 中存储的答案是 'true' (即用户点击了是按钮.)");}别的 {System.err.println("CustomDialog 中存储的答案是 'false' (即用户没有点击任何按钮.)");}}}公共静态无效主要(字符串argv []){TestTheDialog tester = new TestTheDialog();}}导入 javax.swing.JDialog;导入 java.awt.event.ActionListener;导入 javax.swing.JPanel;导入 javax.swing.JFrame;导入 javax.swing.JLabel;导入 javax.swing.JButton;导入 java.awt.event.ActionEvent;公共类 CustomDialog 扩展 JDialog 实现 ActionListener {私人JPanel myPanel = null;私人 JButton yesButton = null;私人 JButton noButton = null;私人布尔答案=假;私有 JFrame 父框架;public boolean getAnswer() { 返回答案;}公共CustomDialog(JFrame框架,布尔模式,字符串myMessage){超级(框架,模态);父框架 = 框架;myPanel = 新的 JPanel();getContentPane().add(myPanel);myPanel.add(new JLabel(myMessage));yesButton = new JButton("是");yesButton.addActionListener(this);myPanel.add(yesButton);noButton = new JButton("否");noButton.addActionListener(this);myPanel.add(noButton);盒();setLocationRelativeTo(帧);设置可见(真);}公共无效actionPerformed(ActionEvent e){if(yesButton == e.getSource()) {CustomDialogSearch myDialog = new CustomDialogSearch(parentFrame, false, "CustomDialog Search?");System.err.println("用户选择是.");答案=真;myDialog.getAnswer();System.out.println("myDialog.getAnswer()="+myDialog.getAnswer());myDialog.show();如果(myDialog.getAnswer()==true){System.out.println("tofront");this.toFront();}//setVisible(false);}否则 if(noButton == e.getSource()) {System.err.println("用户选择了否.");答案=假;设置可见(假);}}}导入 javax.swing.JDialog;导入 java.awt.event.ActionListener;导入 javax.swing.JPanel;导入 javax.swing.JFrame;导入 javax.swing.JLabel;导入 javax.swing.JButton;导入 java.awt.event.ActionEvent;公共类 CustomDialogSearch 扩展 JDialog 实现 ActionListener {私人JPanel myPanel = null;私人 JButton yesButton = null;私人 JButton noButton = null;私人布尔答案=假;public boolean getAnswer() { 返回答案;}public CustomDialogSearch(JFrame frame, boolean modal, String myMessage) {超级(框架,模态);myPanel = 新的 JPanel();getContentPane().add(myPanel);myPanel.add(new JLabel(myMessage));yesButton = new JButton("是");yesButton.addActionListener(this);myPanel.add(yesButton);noButton = new JButton("否");noButton.addActionListener(this);myPanel.add(noButton);盒();setLocationRelativeTo(帧);设置可见(真);}公共无效actionPerformed(ActionEvent e){if(yesButton == e.getSource()) {System.err.println("搜索用户选择是.");答案=真;//setVisible(false);}否则 if(noButton == e.getSource()) {System.err.println("搜索用户选择了否.");答案=假;设置可见(假);}}}

解决方案

我什至尝试将 CustomDialogSearch 的父级设置为 CustomDialog行为仍然不正确.

我认为您在这里的方向是正确的,但您需要使用对话模式类型.例如:

  • 将Custom Dialog Main(父"对话框)的模态类型设置为Dialog.ModalityType.APPLICATION_MODAL.通过在此对话框可见时执行此操作,它将阻止除其子级之外的所有窗口.
  • 将自定义对话框搜索(子"对话框)的模态类型设置为Dialog.ModalityType.MODELESS.这样它就不会阻塞任何其他窗口,您可以从子窗口转到父窗口,反之亦然.

为了更好地理解,请查看 如何在对话框中使用模式 文章.

示例

这是我上面建议的关于使用模态的代码示例:

导入 java.awt.Dialog;导入 java.awt.Window;导入 java.awt.event.ActionEvent;导入 java.awt.event.ActionListener;导入 javax.swing.JButton;导入 javax.swing.JDialog;导入 javax.swing.JFrame;导入 javax.swing.SwingUtilities;公共类演示{私人无效createAndShowGUI(){JButton button = new JButton("创建父模式对话框");button.addActionListener(new ActionListener() {@覆盖公共无效actionPerformed(ActionEvent e){JButton 按钮 = (JButton)e.getSource();JFrame 所有者 = (JFrame)SwingUtilities.windowForComponent(button);Demo.this.createAndShowParentDialog(owner);}});JFrame frame = new JFrame("Demo");frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);frame.getContentPane().add(button);框架.pack();frame.setLocationRelativeTo(null);frame.setVisible(true);}私人无效createAndShowParentDialog(JFrame所有者){JButton button = new JButton("创建子非模态对话框");button.addActionListener(new ActionListener() {@覆盖公共无效actionPerformed(ActionEvent e){JButton 按钮 = (JButton)e.getSource();JDialog parent = (JDialog)SwingUtilities.windowForComponent(button);Demo.this.createAndShowChildrenDialog(parent);}});JDialog parentDialog = new JDialog(owner, "父对话框");parentDialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);parentDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);parentDialog.getContentPane().add(button);parentDialog.pack();parentDialog.setLocationRelativeTo(null);parentDialog.setVisible(true);}私人无效createAndShowChildrenDialog(JDialog父){JButton backButton = new JButton("返回父对话框");backButton.addActionListener(new ActionListener() {@覆盖公共无效actionPerformed(ActionEvent e){JButton 按钮 = (JButton)e.getSource();窗口对话框 = SwingUtilities.windowForComponent(button);对话框.getOwner().toFront();}});JDialog childDialog = new JDialog(parent, "子对话框");childDialog.setModalityType(Dialog.ModalityType.MODELESS);childDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);childDialog.getContentPane().add(backButton);childDialog.pack();childDialog.setLocationRelativeTo(null);childDialog.setVisible(true);}公共静态无效主要(字符串[]参数){SwingUtilities.invokeLater(new Runnable() {@覆盖公共无效运行(){新的 Demo().createAndShowGUI();}});}}

编辑

<块引用>

我可以选择父和子 JDialogs 的窗口,但是当我选择父JDialog窗口,子JDialog还在前面父 JDialog.

嗯,我现在对问题所在有了更好的理解.此行为取决于本机窗口系统如何处理焦点窗口和活动窗口.话虽如此,如果您调用例如 toFront() 它将尝试将窗口放在堆栈的顶部,但某些平台不允许拥有其他窗口的窗口出现在其子窗口的顶部.当您调用 toBack() 方法.有关详细信息,请参阅 javadocs.

我已经在 Windows 7 上测试了我的代码(32 位,如果它有任何区别)并且父对话框变得有焦点,但它的子对话框仍然显示(不集中)在顶部.如上所述,由窗口系统决定如何处理此问题.

I am facing issues when I set my first JDialog modal and the second one non-modal.

This is the functionality I am trying to implement:

  1. On click of "Test the dialog!" button, a JDialog with name Custom Dialog Main will open.
  2. If click "yes" option in Custom Dialog Main, another JDialog named Custom Dialog Search will open.
  3. If click "yes" option in Custom Dialog Search, then Custom Dialog Main should come front.
  4. And I should be able to select any JDialog. For example if I select Custom Dialog Search, the other dialog should go back and vice versa.

Problem I am facing is when I click "yes" in Custom Dialog Main, then Custom Dialog Search is displayed behind the main dialog.

This is happening because I am setting Custom Dialog Search non-modal. If I this dialog modal it is displayed correctly but after I click "yes" Custom Dialog Main doesn't come front.

I even tried to set CustomDialogSearch's parent to be CustomDialog the behaviour still not correct.

Below is the example code I am testing.

import java.awt.event.ActionListener;  
import javax.swing.JFrame;  
import javax.swing.JButton;  
import java.awt.event.WindowAdapter;  
import java.awt.event.WindowEvent;  
import java.awt.event.ActionEvent;  
import java.awt.Dimension;   

public class TestTheDialog implements ActionListener {  
    JFrame mainFrame = null;  
    JButton myButton = null;  

    public TestTheDialog() {  
        mainFrame = new JFrame("TestTheDialog Tester");  
        mainFrame.addWindowListener(new WindowAdapter() {  
                public void windowClosing(WindowEvent e) {System.exit(0);}  
            });  
        myButton = new JButton("Test the dialog!");  
        myButton.addActionListener(this);  
        mainFrame.setLocationRelativeTo(null);  
        mainFrame.getContentPane().add(myButton);  
        mainFrame.pack();  
        mainFrame.setVisible(true);  
    }  

    public void actionPerformed(ActionEvent e) {  
        if(myButton == e.getSource()) {  
            System.err.println("Opening dialog.");  
            CustomDialog myDialog = new CustomDialog(mainFrame, true, "Custom Dialog Main?");  
            System.err.println("After opening dialog.");  
            if(myDialog.getAnswer()) {  
                System.err.println("The answer stored in CustomDialog is 'true' (i.e. user clicked yes button.)");  
            }  
            else {  
                System.err.println("The answer stored in CustomDialog is 'false' (i.e. user clicked no button.)");  
            }  
        }  
    }  

    public static void main(String argv[]) {  

        TestTheDialog tester = new TestTheDialog();  
    }  
}  

import javax.swing.JDialog;   
import java.awt.event.ActionListener;  
import javax.swing.JPanel;  
import javax.swing.JFrame;  
import javax.swing.JLabel;  
import javax.swing.JButton;  
import java.awt.event.ActionEvent;  

public class CustomDialog extends JDialog implements ActionListener {  
    private JPanel myPanel = null;  
    private JButton yesButton = null;  
    private JButton noButton = null;  
    private boolean answer = false;  
    private JFrame parentFrame;  
    public boolean getAnswer() { return answer; }  

    public CustomDialog(JFrame frame, boolean modal, String myMessage) {  
        super(frame, modal);  
        parentFrame = frame;  
        myPanel = new JPanel();  
        getContentPane().add(myPanel);  
        myPanel.add(new JLabel(myMessage));  
        yesButton = new JButton("Yes");  
        yesButton.addActionListener(this);  
        myPanel.add(yesButton);   
        noButton = new JButton("No");  
        noButton.addActionListener(this);  
        myPanel.add(noButton);    
        pack();  
        setLocationRelativeTo(frame);  
        setVisible(true);  
    }  

    public void actionPerformed(ActionEvent e) {  
        if(yesButton == e.getSource()) {  
            CustomDialogSearch myDialog = new CustomDialogSearch(parentFrame, false, "CustomDialog Search?");  
            System.err.println("User chose yes.");  
            answer = true;  
            myDialog.getAnswer();  
            System.out.println("myDialog.getAnswer()="+myDialog.getAnswer());  
            myDialog.show();  

            if(myDialog.getAnswer()==true)  
            {  
                System.out.println("tofront");  
                this.toFront();  
            }  
            //setVisible(false);  
        }  
        else if(noButton == e.getSource()) {  
            System.err.println("User chose no.");  
            answer = false;  
            setVisible(false);  
        }  
    }  

}

import javax.swing.JDialog;   
import java.awt.event.ActionListener;  
import javax.swing.JPanel;  
import javax.swing.JFrame;  
import javax.swing.JLabel;  
import javax.swing.JButton;  
import java.awt.event.ActionEvent;  

public class CustomDialogSearch extends JDialog implements ActionListener {  
    private JPanel myPanel = null;  
    private JButton yesButton = null;  
    private JButton noButton = null;  
    private boolean answer = false;  
    public boolean getAnswer() { return answer; }  

    public CustomDialogSearch(JFrame frame, boolean modal, String myMessage) {  
        super(frame, modal);  
        myPanel = new JPanel();  
        getContentPane().add(myPanel);  
        myPanel.add(new JLabel(myMessage));  
        yesButton = new JButton("Yes");  
        yesButton.addActionListener(this);  
        myPanel.add(yesButton);   
        noButton = new JButton("No");  
        noButton.addActionListener(this);  
        myPanel.add(noButton);    
        pack();  
        setLocationRelativeTo(frame);  
        setVisible(true);  
    }  

    public void actionPerformed(ActionEvent e) {  
        if(yesButton == e.getSource()) {  
            System.err.println("Search User chose yes.");  
            answer = true;  
            //setVisible(false);  
        }  
        else if(noButton == e.getSource()) {  
            System.err.println("Search User chose no.");  
            answer = false;  
            setVisible(false);  
        }  
    }  

} 

解决方案

I even tried to set CustomDialogSearch's parent to be CustomDialog the behaviour still not correct.

I think you're in the right track here but you need to play with dialogs modality type. For instance:

  • Set the modality type of Custom Dialog Main ("parent" dialog) as Dialog.ModalityType.APPLICATION_MODAL. By doing this when this dialog is visible it will block all windows except its children.
  • Set the modality type of Custom Dialog Search ("child" dialog) as Dialog.ModalityType.MODELESS. This way it won't block any other window and you can go from the child to the parent and vice versa.

For a better understanding take a look to How to Use Modality in Dialogs article.

Example

Here is a code example about using modality as I've suggested above:

import java.awt.Dialog;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Demo {

    private void createAndShowGUI() {
        JButton button = new JButton("Create Parent modal dialog");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JButton button = (JButton)e.getSource();
                JFrame owner = (JFrame)SwingUtilities.windowForComponent(button);
                Demo.this.createAndShowParentDialog(owner);                
            }
        });

        JFrame frame = new JFrame("Demo");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(button);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void createAndShowParentDialog(JFrame owner) {
        JButton button = new JButton("Create Child non-modal dialog");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JButton button = (JButton)e.getSource();
                JDialog parent = (JDialog)SwingUtilities.windowForComponent(button);
                Demo.this.createAndShowChildrenDialog(parent);                
            }
        });

        JDialog parentDialog = new JDialog(owner, "Parent dialog");
        parentDialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
        parentDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        parentDialog.getContentPane().add(button);
        parentDialog.pack();
        parentDialog.setLocationRelativeTo(null);        
        parentDialog.setVisible(true);
    }

    private void createAndShowChildrenDialog(JDialog parent) {        
        JButton backButton = new JButton("Back to parent dialog");
        backButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JButton button = (JButton)e.getSource();
                Window dialog = SwingUtilities.windowForComponent(button);
                dialog.getOwner().toFront();
            }
        });

        JDialog childDialog = new JDialog(parent, "Child dialog");
        childDialog.setModalityType(Dialog.ModalityType.MODELESS);
        childDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        childDialog.getContentPane().add(backButton);
        childDialog.pack();
        childDialog.setLocationRelativeTo(null);        
        childDialog.setVisible(true);
    }


    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Demo().createAndShowGUI();
            }
        });
    }    
}

Edit

I can select the windows of parent and child JDialogs but when I select the parent JDialog window, the child JDialog is still in front of parent JDialog.

Well I have a better understanding now on what is the problem. This behaviour depends on how native windowing system handles focused and active windows. Having said this if you call for instance toFront() it will attempt to place the window at the top of the stack BUT some platforms do not allow windows which own other windows to appear on top of its childre. The same happens when you call toBack() method. See the javadocs for more details.

I've tested my code on Windows 7 (32 bits if it makes any difference) and parent dialog becomes focused but its children still showing (not focused) at the top. As mentioned above it's up to the windowing system decide how to handle this matter.

相关文章