有没有办法缩短这段代码?C# WinForms

3

我正在学习Windows窗体中的MDI表单,并且正在使用这个简单的应用程序进行尝试: alt text

每个ToolStripMeniItem调用特定表单的单个实例,但正如您所看到的(请参阅我的代码),我的代码对于每个ToolStripMeniItem都是重复的,我该如何缩短它?

        public static Form IsFormAlreadyOpen(Type FormType)
        {
            foreach (Form OpenForm in Application.OpenForms)
            {
                if (OpenForm.GetType() == FormType)
                    return OpenForm;
            }
            return null;
        }

        private void form1ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Form1 f1 = null;
            if (IsFormAlreadyOpen(typeof(Form1)) == null)
            {
                f1 = new Form1();
                f1.MdiParent = this;
                f1.Show();
            }
            else
            {
                Form selectedForm = IsFormAlreadyOpen(typeof(Form1));
                foreach (Form OpenForm in this.MdiChildren)
                {
                    if (OpenForm == selectedForm)
                    {
                        if (selectedForm.WindowState == FormWindowState.Minimized)
                        {
                            selectedForm.WindowState = FormWindowState.Normal;
                        }
                        selectedForm.Select();
                    }
                }
            }
        }

        private void form2ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Form2 f2 = null;
            if (IsFormAlreadyOpen(typeof(Form2)) == null)
            {
                f2 = new Form2();
                f2.MdiParent = this;
                f2.Show();
            }
            else
            {
                Form selectedForm = IsFormAlreadyOpen(typeof(Form2));
                foreach (Form OpenForm in this.MdiChildren)
                {
                    if (OpenForm == selectedForm)
                    {
                        if (selectedForm.WindowState == FormWindowState.Minimized)
                        {
                            selectedForm.WindowState = FormWindowState.Normal;
                        }
                        selectedForm.Select();
                    }
                }
            }
            // and so on... for the other ToolStripMeniItem
        }
4个回答

5
通用函数是指可以与调用者定义的一组类型一起使用的函数。
因此,您可以将 int doubleIt(int a) { return a*2; } 改为 T doubleIt<T>(T a){ return a*2; } 这意味着您可以定义一个函数,并像这样调用它:
int intResult = doubleIt(...pass in a int..);
double doubleResult = doubleIt(...pass in a double..);
float floatResult = doubleIt(...pass in a float..);

因此,对于您的代码,您需要创建一个方法,将要创建的表单类型作为T的值。

然而,编译器不知道T是一个表单类型,并且您可以在其上调用new,因此您必须使用where子句来限制可作为T传递的类型,其中说T必须是一个表单并且必须有一个您可以访问的构造函数

因此,您可以用特定于所需f1类型的通用函数替换核心功能。

所以,您不再使用 Form f1 = null;,而是使用一个通用类型,您将其传递给 T f1 = null;

private void ClickEvent<T>(object sender, EventArgs e) where T: Form, new() {
            T f1 = null;
    .
    .
    .
 }

然后从真实的事件处理程序中调用此方法。

private void form1ToolStripMenuItem_Click(object sender, EventArgs e)       {
            ClickEvent<Form1>(sender, e)
        }

        private void form2ToolStripMenuItem_Click(object sender, EventArgs e) {
           ClickEvent<Form1>(sender, e) 
        }

因此,您最终会得到类似以下的内容:
public static Form IsFormAlreadyOpen(Type FormType)
{
    foreach (Form OpenForm in Application.OpenForms)
    {
        if (OpenForm.GetType() == FormType)
            return OpenForm;
    }
    return null;
}

private void ClickEvent<T>(object sender, EventArgs e) where T: Form, new() 
    {
    T f1 = null;
    if (IsFormAlreadyOpen(typeof(T)) == null)
    {
        f1 = new T();
        f1.MdiParent = this;
        f1.Show();
    }
    else
    {
        Form selectedForm = IsFormAlreadyOpen(typeof(T));
        foreach (Form OpenForm in this.MdiChildren)
        {
            if (OpenForm == selectedForm)
            {
                if (selectedForm.WindowState == FormWindowState.Minimized)
                {
                    selectedForm.WindowState = FormWindowState.Normal;
                }
                selectedForm.Select();
            }
        }
    }
}

    private void form1ToolStripMenuItem_Click(object sender, EventArgs e)       {
        ClickEvent<Form1>(sender, e)
    }

private void form2ToolStripMenuItem_Click(object sender, EventArgs e) {
   ClickEvent<Form1>(sender, e) 
}

额外提示:如果您可以使用Linq,那么可以这样做。

  using System.Linq;

             public static Form IsFormAlreadyOpen(Type FormType)        {       
                return Application.OpenForms.Where( f => f.GetType() == FormType).FirstOrDefault();
            }

            private void ClickEvent<T>(object sender, EventArgs e) where T: Form, new() 
            {
                T selectedForm = IsFormAlreadyOpen(typeof(T); 
                if (selectedForm == null)             {
                    (new T() { MdiParent = this; }).Show()
                }
                else {  
                    this.MdiChildren.Where(o => o == selectedForm).ForEach(
                        openForm => { 
                            selectedForm.WindowState = (selectedForm.WindowState == FormWindowState.Minimized) ? FormWindowState.Normal : selectedForm.WindowState;
                            openForm.Select();
                        }
                    );
                }
            }
            private void form1ToolStripMenuItem_Click(object sender, EventArgs e)       {
                ClickEvent<Form1>(sender, e)
            }

            private void form2ToolStripMenuItem_Click(object sender, EventArgs e) {
               ClickEvent<Form2>(sender, e) 
            }

还有其他方法可以缩短这段代码,但使用泛型和linq将是最有效的。


我认为这有点太高级了,显然OP正在学习winforms...这似乎有点过度设计了... - t0mm13b
虽然这正是我想要的,一个可重复使用的方法。 - yonan2236
虽然我在这段代码上遇到了一个错误:if (IsFormAlreadyOpen(typeof(T)) == null) { f1 = new Form1(); f1.MdiParent = this; f1.Show(); } - yonan2236
错误:无法隐式将类型“OneInstance.Form1”转换为“T”。 - yonan2236
抱歉,将 f1 = new Form1(); 替换为 f1 = new T();,我已经更改了答案。 - Preet Sangha
显示剩余5条评论

2

我在考虑将常见功能分解为通用方法。它使用泛型和Activator.CreateInstance,但除此之外,下面的代码几乎都是OP的代码,只是重构过。

private void form1ToolStripMenuItem_Click(object sender, EventArgs e)
{
    OpenForm<Form2>();
}

private void OpenForm<T>() where T : Form
{
    T form = null;

    if (IsFormAlreadyOpen(typeof(T)) == null)
    {
        form = Activator.CreateInstance<T>();
        form.MdiParent = this;
        form.Show();
    }
    else
    {
        Form selectedForm = IsFormAlreadyOpen(typeof(T));
        foreach (Form OpenForm in this.MdiChildren)
        {
            if (OpenForm == selectedForm)
            {
                if (selectedForm.WindowState == FormWindowState.Minimized)
                {
                    selectedForm.WindowState = FormWindowState.Normal;
                }
                selectedForm.Select();
            }
        }
    }
}

除了拼写错误之外,还有其他的问题吗?(哎呀,糟糕)~~ 另外,继续传递 object sender, EventArgs e 也不会有什么坏处,尽管如果不需要的话,这可能会导致冗余。这只是给 @yonan 的一个提醒。 - jcolebrand
这段代码对我很有效。而且很容易理解……谢谢Adrift先生。 - yonan2236

2

也许更容易使用 MenuItem 类的 'Tag' 属性,动态构建菜单项并将其添加到 MenuStrip 中,并为所有菜单项使用一个 Click 事件处理程序,然后找出使用了哪个 tag,例如,您可以像这样将 tag 设置为表单

menuItem = new MenuItem("Form 1");
menuItem.Tag = Form1;
menuItem.Click += new EventHandler(frmMDI_FormHandler_Click);
frmMDI.MenuStrip.Add(menuItem);

这就是我所想的,动态添加onclick处理程序。我认为你甚至可以通过这种方法重写EventHandler来传递第三个值,因此你可以从代码中传递表单类型。...另外,+1使菜单项可以在代码后端中添加(可能从XML文件加载以实现完全可定制性?:p) - jcolebrand
在您的表单加载时,如果您想要添加多个项目,则需要重复块menuItem = new ... ... frmMDI.MenuStirp.Add(menuItem);(但您可能会遇到需要限制此块运行频率的情况...这只是一个考虑因素)。 - jcolebrand
尽管使用控件的Tag属性可能会有风险,如果某人或某些东西试图将Tag用于不同的目的。我可能会创建一个继承自MenuItem并添加特定于此用法的属性的自定义类。 - MusiGenesis

0

缩短此代码的黑客方法(不过我觉得这不是你的意思 :p)

另外请注意:"IsXYZ(val)" 意味着布尔型响应... 请考虑一下。尝试使用 "GetOpenForm(Type FormType)"

public static Form IsFormOpen(Type FormType) {
  foreach (Form OpenForm in Application.OpenForms) 
    if (OpenForm.GetType() == FormType)
      return OpenForm;
  return null;
}

请注意,定义“缩短代码”的帮助...你只是指减少生成新代码所需的按键次数吗?
此外,请注意,结合Preet和Tommie的答案可以让您查看sender并获取调用您的方法的类型,从而允许您在那里自定义逻辑。检查每个表单的类型的奇怪逻辑是导致代码过于复杂的原因。如果有另一种方法来识别打开的窗口,那可能对您有利。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接