简体   繁体   English

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

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

I have an application built in C# and Windows Forms.我有一个内置于 C# 和 Windows Forms 的应用程序。 I have a basic panel open with several UserControls using different buttons.我打开了一个基本面板,其中有几个使用不同按钮的用户控件。

For example, I have the PC button and Laptop and Tablet;例如,我有 PC 按钮和笔记本电脑和平板电脑; for each button I created 2 UserControls, one occupying the (dock properties) top of the container and the other the center (dock fill).对于每个按钮,我创建了 2 个用户控件,一个占据容器的(码头属性)顶部,另一个占据中心(码头填充)。

The problem lies in, I have a panelContainer which is the main panel to show something.问题在于,我有一个 panelContainer,它是显示某些内容的主面板。 When I press the Home button, I want it to open the 2 UserControls and to hide the others, the same should be the case with the other buttons.当我按下主页按钮时,我希望它打开 2 个用户控件并隐藏其他按钮,其他按钮也应该如此。

Each UserControl created occupies that panelContainer, but I can't hide the other UserControls.创建的每个 UserControl 都占用该 panelContainer,但我无法隐藏其他 UserControl。

This is the cod creating the panelContainer to display the 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; }
    }

This is the Button PC which open that 2 userControl in panelContainer这是在 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;
       
    }

This is the Button Laptop which open that 2 userControl in panelContainer这是在 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;
    }

This is the Button Tablet which open that 2 userControl in panelContainer这是在 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;

    }

As you can see, each individual button opens the 2 USerControls and displays them, but my problem is how to hide the others.如您所见,每个单独的按钮都会打开 2 个 USerControls 并显示它们,但我的问题是如何隐藏其他按钮。 I have tried these 2 methods, but none of them work, and I have the same error with both.我已经尝试过这两种方法,但它们都不起作用,而且我对这两种方法都有同样的错误。

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

And the error is this:错误是这样的:

System.NullReferenceException: 'Object reference not set to an instance of an object.' System.NullReferenceException:“对象引用未设置为 object 的实例。”

How can I hide them?我该如何隐藏它们? Please help.请帮忙。

Just so we're on the same page, I assumed all of the samples you gave to be in the Form1 class, which in my reconstruction contains nothing else except the default constructor and the partial half of the class that's autogenerated.就像我们在同一页上一样,我假设您提供的所有样本都在 Form1 class 中,在我的重建中,除了默认构造函数和自动生成的 class 的部分一半之外,它什么都不包含。 My designer looks as follows:我的设计师如下所示: 在此处输入图像描述 In the top-left is the Panel called panelContainer.左上角是名为 panelContainer 的 Panel。 In your post you refer to "a panelContainer" implying that's the name of some custom class or something, however from the code snippet I inferred it's just a normal Panel.在您的帖子中,您提到“a panelContainer”,暗示这是一些自定义 class 或其他名称的名称,但是从代码片段中我推断它只是一个普通的面板。 Also, in order for it to compile I had to create six other control types.此外,为了编译它,我必须创建六种其他控件类型。 Since I'm lazy, I just ripped off Button:由于我很懒,所以我只是扯掉了Button:

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

The others are exactly the same, just with different names.其他的完全一样,只是名称不同。

From what I understand, you ultimately would like to simply add the two hiding lines you post at the end of each of the click handlers.据我了解,您最终只想在每个点击处理程序的末尾添加您发布的两条隐藏线。

Ok, here's my first (albeit minor) criticism: a couple of your names leave a lot to be desired.好的,这是我的第一个(尽管是次要的)批评:你的几个名字还有很多不足之处。 _obj is about as undescriptive as you can get. _obj 几乎没有任何描述性。 Personally I'm not a fan of the underscore convention for private properties, although I know some people and groups like it.就个人而言,我不喜欢私有财产的下划线约定,尽管我知道有些人和团体喜欢它。 But please, at least call it "_mainForm" or something.但请至少称它为“_mainForm”或其他名称。 Since you have a getter-setter right there, "instance" presents itself as a decent go-to option.由于您在那里有一个 getter-setter,因此“实例”本身就是一个不错的首选选项。 The other name that caught my attention was PnlContainer.另一个引起我注意的名字是 PnlContainer。 Hungarian notation is fairly widely discouraged in modern object-oriented languages, but I suspect this is just short for "PanelContainer".在现代面向对象语言中,匈牙利表示法相当普遍,但我怀疑这只是“PanelContainer”的缩写。 In my opinion you could've just had the full name, or otherwise just called it "Container".在我看来,您可以只使用全名,或者直接将其称为“容器”。 Oh, and finally "guna2Button4_Click"?哦,最后是“guna2Button4_Click”? I'm not even sure where to start with this one...I know this point wasn't part of the question, but since I'm doing a thorough commentary on the code anyway I figured I'd just add it in for good measure.我什至不确定从哪里开始...我知道这一点不是问题的一部分,但是由于无论如何我都在对代码进行彻底的评论,所以我想我只是将其添加到好措施。

In my first test to reproduce your issue, I found that the line Form1.Instance.PnlContainer.Controls["UserControlPCsiPeriferice_Menu"].BringToFront();在我第一次重现您的问题的测试中,我发现Form1.Instance.PnlContainer.Controls["UserControlPCsiPeriferice_Menu"].BringToFront();already causes a null-reference exception by virtue of the fact that ControlCollection[string] returns a value based on the Name of the Control, which isn't set in your code.由于ControlCollection[string]返回一个基于控件名称的值,该值未在您的代码中设置,因此已经导致空引用异常。 I presume from context it's set in the controls themselves, which is why I added the line Name = nameof(UserControlLaptop);我从上下文中推测它是在控件本身中设置的,这就是为什么我添加了行Name = nameof(UserControlLaptop); in my test controls.在我的测试控制中。

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

Causes a NullReferenceException because ...Controls["UserControlTablete"] is null the first time the code is executed, so you're essentially calling null.Hide();导致 NullReferenceException 因为...Controls["UserControlTablete"]在第一次执行代码时是 null,所以您实际上是在调用null.Hide(); The root cause is that no control with the name "UserControlTablete" has been added yet to Instance.PnlContainer.根本原因是尚未将名为“UserControlTablete”的控件添加到 Instance.PnlContainer。 There are a couple ways to fix this:有几种方法可以解决这个问题:

// 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();

The Control.Hide() method works fine if it's called using one of the ways listed above, but in your questioned you mentioned trying panelContainer.Controls["UserControlSistemePC"].Hide();如果使用上面列出的方法之一调用Control.Hide()方法,则该方法可以正常工作,但是在您提出的问题中,您提到了尝试panelContainer.Controls["UserControlSistemePC"].Hide(); as well.也是。 In short, this:简而言之,这是:

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

will work, however I'm not sure the overall behavior here is what you're looking for.会起作用,但是我不确定这里的整体行为是你想要的。

Essentially, the issue is that panelContainer and Form1.Instance.PnlContainer may not be the same object, and if I had to guess I would say they aren't.本质上,问题是panelContainerForm1.Instance.PnlContainer可能不是同一个 object,如果我不得不猜测我会说它们不是。

The declaration宣言

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

essentially makes it so that there is just one instance of Form1 that you access by calling Form1.Instance.从本质上讲,只有一个Form1 实例可供您通过调用 Form1.Instance 访问。 However, there can be other instances of Form1, and chances are when your program starts it does so with a line that reads:但是,可能存在 Form1 的其他实例,并且很可能当您的程序启动时,它会使用以下代码行:

Application.Run(new Form1());

If that's the case, when you press run you will see a Form1 that is different from what's returned by Form1.Instance.如果是这种情况,当您按下运行时,您将看到一个不同于 Form1.Instance 返回的 Form1。 In fact, with the Form1 class written the way it is, if you wanted to start the program with the same instance as you get when you call Form1.Instance you would need to do实际上,使用 Form1 class 编写的方式,如果您想使用与调用 Form1.Instance 时获得的相同实例启动程序,您需要这样做

Application.Run(Form1.Instance);

If that's how you're using the class, then functionally it makes no difference whether you write panelContainer or Form1.Instance.PnlContainer .如果这就是您使用 class 的方式,那么无论您编写panelContainer还是Form1.Instance.PnlContainer在功能上都没有区别。

However, consider this (development philosophy alert:,!): Normally when designing a class, it shouldn't matter how the class is used.但是,请考虑这一点(开发理念警告:,!):通常在设计类时,如何使用 class 并不重要。

If you've designed the class to internally call methods on a static instance of itself on the assumption that that static instance is the same as the current instance, you can be sure that at some point someone's going to want to create another instance and find it doesn't work at all like expected.如果您设计 class 以内部调用 static 实例本身的方法,假设 static 实例与当前实例相同,那么您可以确保创建另一个实例它根本不像预期的那样工作。

Honestly, from what I've seen I'm not sure there's any reason for the Instance property to exist at all.老实说,从我所见,我不确定 Instance 属性是否存在任何理由。 All the calls to Form1.Instance.whatever can just be changed to whatever .所有对Form1.Instance.whatever的调用都可以更改为whatever If you do this then, is there any reason you will still need the PnlContainer property?如果你这样做了,你还有什么理由需要 PnlContainer 属性吗? If not, you could get rid of that too, and just use panelContainer directly instead.如果没有,您也可以摆脱它,而直接使用 panelContainer 。

Here's one last point about calling static properties and methods from inside the class where they're defined.这是关于从定义它们的 class 内部调用 static 属性和方法的最后一点。 You actually don't even need the class name, so您实际上甚至不需要 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();

is exactly the same as完全一样

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();

That brings us to the last form-related point I wanted to bring up: please, please don't write code that requires hardcoded string values for functionality if there's any other way (which there normally is, especially in modern languages like C#).这将我们带到了我想提出的最后一个与表单相关的观点:如果有任何其他方式(通常有,尤其是在 C# 等现代语言中),不要编写需要硬编码字符串值来实现功能的代码。 Sooner or later you'll end up tracking down endless bugs caused by if (status == "deactivated") instead of if (status == "Deactivated") or having to rename something and not being sure where all the hardcoded strings are.迟早你会追踪到由if (status == "deactivated")而不是if (status == "Deactivated")引起的无穷无尽的错误,或者必须重命名某些东西并且不确定所有硬编码字符串在哪里。

In this case, an easy drop-in replacement for在这种情况下,一个简单的替换

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

would be将会

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

I chose the SingleOrDefault() method rather than First() because that way if there are multiple UserControlTabletes, it will throw an exception rather than just hiding the issue.我选择了SingleOrDefault()方法而不是First()方法,因为这样如果有多个 UserControlTabletes,它会抛出异常,而不是仅仅隐藏问题。

An alternative which I suspect would have slightly better performance would be to have a private variable for each of those controls we'll want to hide:我怀疑性能稍微好一点的替代方法是为我们要隐藏的每个控件设置一个私有变量:

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

And then the if statements in the click handlers become然后点击处理程序中的if语句变为

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

Finally, the hide statements simply look like this:最后,隐藏语句看起来像这样:

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

I'm going to conclude with a refactored version of the Form1 class removing the Instance property and getting rid of the access-by-string:我将以 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();
        }
    }
}

If you have a lot of similar controls you might consider developing a more complete framework for them so you don't end up with a bunch of handlers with virtually the same stuff, but as-is it's probably fine.如果您有很多类似的控件,您可能会考虑为它们开发一个更完整的框架,这样您就不会最终得到一堆具有几乎相同内容的处理程序,但按原样可能没问题。

Note that if you're using the most recent version of C#, you'll get warnings about fields potentially being null.请注意,如果您使用的是最新版本的 C#,您将收到有关字段可能为 null 的警告。 You'll just need to update the syntax to fix those by marking them as nullable in the definition:您只需要通过在定义中将它们标记为可为空来更新语法来修复它们:

private UserControlSistemePC? sistemePC;

(While you're at it you can simplify the new statements too.) (当您使用它时,您也可以简化new语句。)

Finally, you might also bear in mind the possibility of using Lazy<T> for your lazy evaluation needs.最后,您可能还需要记住使用Lazy<T>来满足您的惰性求值需求的可能性。

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

相关问题 我如何使用C#从另一个用户控件的用户控件调用方法? - How can i call method from usercontrol from another usercontrol using c#? 如何在C#中的另一个UserControl中更新UserControl? - How to update UserControl in another UserControl in C#? 如何使用事件从UserControl调用方法到另一个UserControl或另一个aspx.Page? - How can i call method from UserControl to another UserControl or another aspx.Page using Events? Windows Forms C#:从UserControl访问TabControl - Windows Forms C#: Access TabControl from UserControl 如何使用asp.net从另一个usercontrol中调用usercontrol的方法? - How can i call method from usercontrol from another usercontrol using asp.net? 我如何使用mvvm将数据从用户控件传递到另一个用户控件 - How can I pass data from usercontrol to another usercontrol using mvvm 显示从另一个用户控件 WPF C# 触发的用户控件? - Show Usercontrol triggered from another Usercontrol WPF C#? 从另一个userControl C#访问userControl上的对象 - Accessing an object on userControl from another userControl c# 在另一种形式的用户控件中隐藏用户控件 - Hide a usercontrol in a usercontrol from another form WPF C#如何在另一个UserControl中的一个UserControl中更改按钮的文本? - WPF C# How do i change the Text of a Button that is in a UserControl in another UserControl?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM