【问题标题】:Method with generic and multiple parameters具有泛型和多个参数的方法
【发布时间】:2020-04-07 18:03:10
【问题描述】:

我想根据用户权限启用/禁用 Windows 窗体应用程序中的控件。

最初我想在每个表单类中编写一个方法来检查用户凭据,然后启用/禁用其控件。但后来我意识到我可以(也许)创建一个静态类方法,它将表单作为参数并完成这项工作。

所以我开始编写它,假设有时我只想启用一两个面板的控件,而不是整个表单。所以,我需要的参数是:

  • 不同数量的面板和/或
  • 一个表单类。

我在这项任务中遇到的困难是我在尝试使面板参数变化时遇到错误,而且我不知道如何设置可以采用任何表单类的参数。我所有的表单类显然都继承自 Form 泛型类,但我不知道如何应用它。

这是我得到的:

public static void Enable(TableLayoutPanel[] containers = null)
    {
        if (MyOF.isEnabled)
        {
            return;
        }
        else
        {
            try
            {
                foreach (TableLayoutPanel table in containers)
                {
                    foreach (Control control in table.Controls)
                    {
                        control.Enabled = false;
                    }
                }
            }
            catch (NullReferenceException)
            {
            }
        }
    }

【问题讨论】:

  • C# 中实际上有一个称为“泛型类型”的功能可以解决您的问题。从这个意义上说,您使用“通用”一词的方式是不正确的。 Form 是所有表单的基类。您可以创建一个像 public static void DoStuff<T>(T someForm) where T : Form { // implement here } 这样的方法,它可以接受任何特定表单作为参数并访问特定于该表单类型的成员。
  • 谢谢詹姆斯。我在之前的搜索中遇到了这个问题,但我错过了where T : Form 部分。我会试试的。
  • 另外,如何使 TableLayoutPanel 参数的数量变化?当我尝试调用仅传递一个面板的方法时,出现错误“无法从 ...Panel 转换为 ...Panel[]”
  • 查找params关键字
  • @JamesFaix this - public static void DoStuff<T>(T someForm) where T : Form 我称之为异端。 :-) 完全滥用通用用途。这就足够了 - public static void DoStuff(Form someForm) 如果将其限制为 Form,使其通用的目的是什么?

标签: c# visual-studio methods overloading


【解决方案1】:

如果我们记得Form 类派生自Control(间接地,通过派生自ContainerControl 派生自ScrollableControl,其派生自Control),并且Enabled 属性属于Control 类,我们可以编写一个方法来启用 any 控件的子控件(包括 FormTableLayoutPanel 控件),因为 Controls 集合也属于 Control 类:

public static void EnableChildren(Control control, bool enabled = true)
{
    foreach (Control child in control.Controls)
    {
        child.Enabled = enabled;
    }
}

然后,如果我们还希望能够将它与控件集合一起使用(如您的示例中所示),我们可以编写一个采用集合的重载:

public static void EnableChildren(IEnumerable<Control> controls = null, 
    bool enabled = true)
{
    if (controls == null) return;

    foreach (var control in controls)
    {
        EnableChildren(control, enabled);
    }
}

现在我们可以将它与FormTableLayoutPanel 控件的集合(或任何在其Controls 集合中包含控件的控件)一起使用。

使用示例:

var myForm = new Form1();

EnableChildren(this);     // 'this' is the current form
EnableChildren(myForm);   // a separate instance of a form control
EnableChildren(tableLayoutPanel1, false);  // A single TableLayoutPanel control

var tableLayoutPanels = new [] {tableLayoutPanel1, tableLayoutPanel2, tableLayoutPanel3};
EnableChildren(tableLayoutPanels);  // An array of tableLayoutPanel controls

【讨论】:

  • 感谢您的回答。我尝试了您的解决方案,但在尝试将 TableLayoutPanel 作为参数传递时出现错误“无法从 'System.Windows.Forms.TableLayoutPanel' 转换为 'System.Collections.Generic.IEnumerable”到方法。
  • @CharleyR。消息很清楚 - 您不能将单个控件转换为可枚举的控件。方法需要递归
  • 我添加了一些适合我的示例用法。你是怎么调用这个方法的?
  • 翻译成你的例子,我打电话是这样的:EnableChildren(tableLayoutPanel1);。我感谢所有答案,因为它们在某些时候都很有用。我将对所有内容进行投票并发布对我有用的内容。再次感谢。
  • 奇怪,你尝试的代码对我来说很好用。您是否修改了示例?
【解决方案2】:

我能想到的一个简单的方法就是这样。让我在这里离开一会儿。我从事的项目中所有表单控件都是从元数据构建的。元数据附带许可信息。因此,当控件放置在应有的位置时,它也被禁用或基于元数据设置为只读,但如果许可信息限制对它的访问,则整个功能将被隐藏。回到你的方法,这不是一个坏方法,我看到这是可以做到的。它可以通过 2 种方式完成,(从我的脑海中很快)。

  1. 使用用户控件作为要启用/禁用的组件的表面。创建接口
public interface IDisableableControl // make your fine name, no methods needed - marker interface
 . .  . . . 
public class MyFineUserControl : UserControl, IDisableableControl 

然后在您将要编写的static 方法中传递表单,并找到实现此接口的所有控件并按照您想要的方式工作。

2。 同样,您可以使用属性Tag,它在每个控件上都可用。这样,您实际上可以设置来自数据库存储的元数据的复杂安全对象,然后评估存储在 Tag 中的该对象以应用您的配置

你的方法需要递归

internal static void SetAllControls(Control parent)
{
    // Do something with control, for example parent.Enabled = false
    if (parent is IDisableableControl)
    {
       // here you use your logic, evaluate your parent you're dialing with and
       // enable/disable correspondingly 
       parent.Enabled = false;
       return;
    }
    foreach(var c in parent.Controls)
        SetAllControls(c);
} 

在现实生活中,您的 TOP 父级将是一个表单,不需要禁用,但某些孩子会。事实上,大多数时候,一旦你找到了一个实现了 IDisableableControlUserControl 应该是行尾,这意味着你不需要进入子控件,因为它们都坐在这个父控件上,所有这些都将是禁用

【讨论】:

  • 感谢您的回答伙伴。看起来真的很聪明。但由于我自己不是专业程序员,我缺乏应用您的解决方案的背景知识。我必须学习一点才能遵循你所做的,这很好,但我设法以不同的方式做到了,我的雇主希望尽快完成这个项目,所以我会坚持更快的解决方案。但再次感谢。
  • @CharleyR。嗯。你害怕我的解决方案吗?其实我的解决方法很简单!您所做的一切,创建一个空界面和一个用户控件 -> add new-user control。然后在用户控件中,只添加界面。编译你的应用程序。现在您可以将自定义用户控件拖放到表单或面板上。将其放在需要可禁用表面的地方。现在,在它上面放下你的面板。这是所有的了。您的解决方案是每次在每个表单上实际列出所有容器。我的解决方案是 - 没有指定控件/容器的 1 行代码。
  • 当然,但请注意,我从未上过“编程学校”,可以这么说,而且我正在学习 c# 功能时需要它们。在您发表评论之前,我从未听说过interface 或“用户控制”。我必须研究它,我肯定会在某个时候学习。
  • @CharleyR。学习界面比写这篇评论要花更少的时间。如果汽车是由接口ICar 制造的,它将具有Color, Doors, wheels, hood, trunk, seats.. 之类的属性和Stop, go, turn 之类的方法。然后,当您创建汽车时,您可以制作大轮子或小轮子,红色或蓝色......但是您班级的消费者会使用ICar,而不知道那里有什么确切的汽车。我给你的建议也是一样,如果你用它标记你的控件,这个控件的消费者不需要知道这个控件,只有当这个控件被接口标记时
  • @CharleyR。并且用户控制也很容易。不要将控件放在窗体上,而是将其放在用户控件上,然后将用户控件放在窗体上。不喜欢用户控制,你知道面板,自定义面板怎么样 - public class MyPanel : Panel, IDisableablePanel。像使用 .net 提供的控件/面板一样使用您的自定义控件/面板
【解决方案3】:

我设法用下面的代码完成了我想要做的事情,这几乎是我得到的所有有用答案的混合:

public static void EnableContainer(params Control[] containers)
    {
        if(containers.Count() == 0) { return; }
        if (MyOF.isEnabled)
        {
            return;
        }
        else
        {
            try
            {
                foreach (var container in containers)
                {
                    foreach (Control control in container.Controls)
                    {
                        control.Enabled = false;
                    }
                }
            }
            catch (NullReferenceException)
            {
            }
        }
    }
    public static void EnableForm<form>(form f) where form : Form
    {
        if (MyOF.isEnabled)
        {
            return;
        }
        else
        {
            foreach(Control control in f.Controls)
            {
                control.Enabled = false;
            }
        }
    }

欢迎社区提出改进建议,因为我远非专业程序员。再次感谢大家。

【讨论】:

  • 这里public static void EnableForm&lt;form&gt;(form f) where form : Form真的是多余的。首先,不要这样做EnableForm&lt;form&gt;,只需这样做EnableForm&lt;T&gt;,这是全球公认的规范。现在,目的是什么?您已经拥有(form f)。这里的泛型不合适。泛型的整个想法是能够使用任何东西 - public static void EnableForm&lt;T&gt;(T f) ,现在,您添加了 . where form : Form - 对表单的另一个限制。所以,只要做public static void EnableForm(form f) 就可以了!
  • 您也不需要检查containers.count == 0,因为foreach 根本不会运行。但是您应该将其替换为if (containers == null) return; 以避免NullReferenceException。您不需要else,因为if 块返回。您不需要捕获NullReferenceException,因为没有代码会抛出一个。名称也具有误导性 - 调用 EnableContainers禁用其中的所有控件。
  • @T.S.是的...阅读您的评论后,我发现我对泛型方法的整个概念,甚至对 C# 处理对象和变量的方式仍然有些困惑。但是你的评论帮助我开始理解事情。非常感谢。
  • @RufusL 你是完全正确的。感谢您的所有建议。
猜你喜欢
  • 1970-01-01
  • 2011-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多