繁体   English   中英

如何使用 C# 在 Windows Forms 中隐藏另一个用户控件的用户控件

[英]How can I hide a usercontrol from another usercontrol in Windows Forms using C#

我有一个内置于 C# 和 Windows Forms 的应用程序。 我打开了一个基本面板,其中有几个使用不同按钮的用户控件。

例如,我有 PC 按钮和笔记本电脑和平板电脑; 对于每个按钮,我创建了 2 个用户控件,一个占据容器的(码头属性)顶部,另一个占据中心(码头填充)。

问题在于,我有一个 panelContainer,它是显示某些内容的主面板。 当我按下主页按钮时,我希望它打开 2 个用户控件并隐藏其他按钮,其他按钮也应该如此。

创建的每个 UserControl 都占用该 panelContainer,但我无法隐藏其他 UserControl。

这是创建 panelContainer 以显示 UserControl 的代码:

static Form1 _obj;

    public static Form1 Instance
    {
        get
        {
            if (_obj == null)
            {
                _obj = new Form1();
            }
            return _obj;
        }
    }

    public Panel PnlContainer
    {
        get { return panelContainer; }
        set { panelContainer = value; }
    }

这是在 panelContainer 中打开 2 个 userControl 的 Button PC

private void guna2Button4_Click(object sender, EventArgs e)
{
        // here the 2 userControls are created and added to the panelContainer
        if (!Form1.Instance.PnlContainer.Controls.ContainsKey("UserControlSistemePC"))
        {
            UserControlSistemePC pc = new UserControlSistemePC();
            pc.Dock = DockStyle.Fill;
            Form1.Instance.PnlContainer.Controls.Add(pc);
            UserControlPCsiPeriferice_Menu pcMenu = new UserControlPCsiPeriferice_Menu();
            pcMenu.Dock = DockStyle.Top;
            Form1.Instance.PnlContainer.Controls.Add(pcMenu);
        }
        Form1.Instance.PnlContainer.Controls["UserControlPCsiPeriferice_Menu"].BringToFront();
        Form1.Instance.PnlContainer.Controls["UserControlSistemePC"].BringToFront();
        Form1.Instance.BackButton.Visible = true;
       
    }

这是在 panelContainer 中打开 2 个 userControl 的 Button 笔记本电脑

private void buttonLaptop_Click(object sender, EventArgs e)
{
        // here the 2 userControls are created and added to the panelContainer
        if (!Form1.Instance.PnlContainer.Controls.ContainsKey("UserControlLaptop"))
        {
            UserControlLaptop laptop = new UserControlLaptop();
            laptop.Dock = DockStyle.Fill;
            Form1.Instance.PnlContainer.Controls.Add(laptop);
            UserControlLaptop_Menu laptopMenu = new UserControlLaptop_Menu();
            laptopMenu.Dock = DockStyle.Top;
            Form1.Instance.PnlContainer.Controls.Add(laptopMenu);
        }
        Form1.Instance.PnlContainer.Controls["UserControlLaptop_Menu"].BringToFront();
        Form1.Instance.PnlContainer.Controls["UserControlLaptop"].BringToFront();
        Form1.Instance.BackButton.Visible = true;
    }

这是在 panelContainer 中打开 2 个 userControl 的 Button Tablet

private void buttonTablete_Click(object sender, EventArgs e)
{
        // here the 2 userControls are created and added to the panelContainer
        if (!Form1.Instance.PnlContainer.Controls.ContainsKey("UserControlTablete"))
        {
            UserControlTablete tablete = new UserControlTablete();
            tablete.Dock = DockStyle.Fill;
            Form1.Instance.PnlContainer.Controls.Add(tablete);
            UserControlTablete_Menu tableteMenu = new UserControlTablete_Menu();
            tableteMenu.Dock = DockStyle.Top;
            Form1.Instance.PnlContainer.Controls.Add(tableteMenu);
        }
        Form1.Instance.PnlContainer.Controls["UserControlTablete_Menu"].BringToFront();
        Form1.Instance.PnlContainer.Controls["UserControlTablete"].BringToFront();
        Form1.Instance.BackButton.Visible = true;

    }

如您所见,每个单独的按钮都会打开 2 个 USerControls 并显示它们,但我的问题是如何隐藏其他按钮。 我已经尝试过这两种方法,但它们都不起作用,而且我对这两种方法都有同样的错误。

Form1.Instance.PnlContainer.Controls["UserControlTablete"].Hide();//.Visible = false
panelContainer.Controls["UserControlSistemePC"].Hide();

错误是这样的:

System.NullReferenceException:“对象引用未设置为 object 的实例。”

我该如何隐藏它们? 请帮忙。

就像我们在同一页上一样,我假设您提供的所有样本都在 Form1 class 中,在我的重建中,除了默认构造函数和自动生成的 class 的部分一半之外,它什么都不包含。 我的设计师如下所示: 在此处输入图像描述 左上角是名为 panelContainer 的 Panel。 在您的帖子中,您提到“a panelContainer”,暗示这是一些自定义 class 或其他名称的名称,但是从代码片段中我推断它只是一个普通的面板。 此外,为了编译它,我必须创建六种其他控件类型。 由于我很懒,所以我只是扯掉了Button:

namespace StackOverflowWinformsTest
{
    internal class UserControlLaptop : Button
    {
        public UserControlLaptop() : base()
        {
            Text = nameof(UserControlLaptop);
            Name = nameof(UserControlLaptop);
        }
    }
}

其他的完全一样,只是名称不同。

据我了解,您最终只想在每个点击处理程序的末尾添加您发布的两条隐藏线。

好的,这是我的第一个(尽管是次要的)批评:你的几个名字还有很多不足之处。 _obj 几乎没有任何描述性。 就个人而言,我不喜欢私有财产的下划线约定,尽管我知道有些人和团体喜欢它。 但请至少称它为“_mainForm”或其他名称。 由于您在那里有一个 getter-setter,因此“实例”本身就是一个不错的首选选项。 另一个引起我注意的名字是 PnlContainer。 在现代面向对象语言中,匈牙利表示法相当普遍,但我怀疑这只是“PanelContainer”的缩写。 在我看来,您可以只使用全名,或者直接将其称为“容器”。 哦,最后是“guna2Button4_Click”? 我什至不确定从哪里开始...我知道这一点不是问题的一部分,但是由于无论如何我都在对代码进行彻底的评论,所以我想我只是将其添加到好措施。

在我第一次重现您的问题的测试中,我发现Form1.Instance.PnlContainer.Controls["UserControlPCsiPeriferice_Menu"].BringToFront();由于ControlCollection[string]返回一个基于控件名称的值,该值未在您的代码中设置,因此已经导致空引用异常。 我从上下文中推测它是在控件本身中设置的,这就是为什么我添加了行Name = nameof(UserControlLaptop); 在我的测试控制中。

Form1.Instance.PnlContainer.Controls["UserControlTablete"].Hide();

导致 NullReferenceException 因为...Controls["UserControlTablete"]在第一次执行代码时是 null,所以您实际上是在调用null.Hide(); 根本原因是尚未将名为“UserControlTablete”的控件添加到 Instance.PnlContainer。 有几种方法可以解决这个问题:

// Just check if the key exists, and if not don't do anything
if (Form1.Instance.PnlContainer.Controls.ContainsKey("UserControlTablete"))
    Form1.Instance.PnlContainer.Controls["UserControlTablete"].Hide();
// Or, better yet, use the null-conditional operator
Form1.Instance.PnlContainer.Controls["UserControlSistemePC"]?.Hide();

如果使用上面列出的方法之一调用Control.Hide()方法,则该方法可以正常工作,但是在您提出的问题中,您提到了尝试panelContainer.Controls["UserControlSistemePC"].Hide(); 也是。 简而言之,这是:

panelContainer.Controls["UserControlSistemePC"]?.Hide();

会起作用,但是我不确定这里的整体行为是你想要的。

本质上,问题是panelContainerForm1.Instance.PnlContainer可能不是同一个 object,如果我不得不猜测我会说它们不是。

宣言

public static Form1 Instance {get {...}}

从本质上讲,只有一个Form1 实例可供您通过调用 Form1.Instance 访问。 但是,可能存在 Form1 的其他实例,并且很可能当您的程序启动时,它会使用以下代码行:

Application.Run(new Form1());

如果是这种情况,当您按下运行时,您将看到一个不同于 Form1.Instance 返回的 Form1。 实际上,使用 Form1 class 编写的方式,如果您想使用与调用 Form1.Instance 时获得的相同实例启动程序,您需要这样做

Application.Run(Form1.Instance);

如果这就是您使用 class 的方式,那么无论您编写panelContainer还是Form1.Instance.PnlContainer在功能上都没有区别。

但是,请考虑这一点(开发理念警告:,!):通常在设计类时,如何使用 class 并不重要。

如果您设计 class 以内部调用 static 实例本身的方法,假设 static 实例与当前实例相同,那么您可以确保创建另一个实例它根本不像预期的那样工作。

老实说,从我所见,我不确定 Instance 属性是否存在任何理由。 所有对Form1.Instance.whatever的调用都可以更改为whatever 如果你这样做了,你还有什么理由需要 PnlContainer 属性吗? 如果没有,您也可以摆脱它,而直接使用 panelContainer 。

这是关于从定义它们的 class 内部调用 static 属性和方法的最后一点。 您实际上甚至不需要 class 名称,所以

Form1.Instance.PnlContainer.Controls["UserControlPCsiPeriferice_Menu"].BringToFront();
Form1.Instance.PnlContainer.Controls["UserControlSistemePC"].BringToFront();
Form1.Instance.BackButton.Visible = true;
Form1.Instance.PnlContainer.Controls["UserControlTablete"]?.Hide();
Form1.Instance.PnlContainer.Controls["UserControlSistemePC"]?.Hide();

完全一样

Instance.PnlContainer.Controls["UserControlPCsiPeriferice_Menu"].BringToFront();
Instance.PnlContainer.Controls["UserControlSistemePC"].BringToFront();
Instance.BackButton.Visible = true;
Instance.PnlContainer.Controls["UserControlTablete"]?.Hide();
Instance.PnlContainer.Controls["UserControlSistemePC"]?.Hide();

这将我们带到了我想提出的最后一个与表单相关的观点:如果有任何其他方式(通常有,尤其是在 C# 等现代语言中),不要编写需要硬编码字符串值来实现功能的代码。 迟早你会追踪到由if (status == "deactivated")而不是if (status == "Deactivated")引起的无穷无尽的错误,或者必须重命名某些东西并且不确定所有硬编码字符串在哪里。

在这种情况下,一个简单的替换

Form1.Instance.PnlContainer.Controls["UserControlTablete"]?.Hide();

将会

Form1.Instance.PnlContainer.Controls.OfType<UserControlTablete>().SingleOrDefault().Hide();

我选择了SingleOrDefault()方法而不是First()方法,因为这样如果有多个 UserControlTabletes,它会抛出异常,而不是仅仅隐藏问题。

我怀疑性能稍微好一点的替代方法是为我们要隐藏的每个控件设置一个私有变量:

private UserControlSistemePC sistemePC;
private UserControlLaptop laptop;
private UserControlTablete tablete;

然后点击处理程序中的if语句变为

if (sistemePC == null)
{
    // Do stuff;
}

最后,隐藏语句看起来像这样:

laptop?.Hide();
tablete?.Hide();

我将以 Form1 class 的重构版本作为结束,删除 Instance 属性并摆脱按字符串访问:

namespace StackOverflowWinformsTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private UserControlSistemePC sistemePC;
        private UserControlPCsiPeriferice_Menu sistemePCMenu;
        private UserControlLaptop laptop;
        private UserControlLaptop_Menu laptopMenu;
        private UserControlTablete tablete;
        private UserControlTablete_Menu tableteMenu;

        private void sistemePC_Click(object sender, EventArgs e)
        {
            if (sistemePC == null)
            {
                sistemePC = new UserControlSistemePC();
                sistemePC.Dock = DockStyle.Fill;
                panelContainer.Controls.Add(sistemePC);
                sistemePCMenu = new UserControlPCsiPeriferice_Menu();
                sistemePCMenu.Dock = DockStyle.Top;
                panelContainer.Controls.Add(sistemePCMenu);
            }
            sistemePCMenu.BringToFront();
            sistemePC.BringToFront();
            BackButton.Show();
            laptop?.Hide();
            tablete?.Hide();
        }

        private void buttonLaptop_Click(object sender, EventArgs e)
        {
            if (laptop == null)
            {
                laptop = new UserControlLaptop();
                laptop.Dock = DockStyle.Fill;
                panelContainer.Controls.Add(laptop);
                laptopMenu = new UserControlLaptop_Menu();
                laptopMenu.Dock = DockStyle.Top;
                panelContainer.Controls.Add(laptopMenu);
            }
            laptopMenu.BringToFront();
            laptop.BringToFront();
            BackButton.Show();
            tablete?.Hide();
            sistemePC?.Hide();
        }

        private void buttonTablete_Click(object sender, EventArgs e)
        {
            if (tablete == null)
            {
                tablete = new UserControlTablete();
                tablete.Dock = DockStyle.Fill;
                panelContainer.Controls.Add(tablete);
                tableteMenu = new UserControlTablete_Menu();
                tableteMenu.Dock = DockStyle.Top;
                panelContainer.Controls.Add(tableteMenu);
            }
            tableteMenu.BringToFront();
            tablete.BringToFront();
            BackButton.Show();
            laptop?.Hide();
            sistemePC?.Hide();
        }
    }
}

如果您有很多类似的控件,您可能会考虑为它们开发一个更完整的框架,这样您就不会最终得到一堆具有几乎相同内容的处理程序,但按原样可能没问题。

请注意,如果您使用的是最新版本的 C#,您将收到有关字段可能为 null 的警告。 您只需要通过在定义中将它们标记为可为空来更新语法来修复它们:

private UserControlSistemePC? sistemePC;

(当您使用它时,您也可以简化new语句。)

最后,您可能还需要记住使用Lazy<T>来满足您的惰性求值需求的可能性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM