从表单访问类,反之亦然

时间:2017-09-01 11:54:29

标签: c# winforms visual-studio

在网上搜索了两天而没有找到一个我能理解的解决方案之后,我不得不在这里询问答案。

我有一个用vb.net编写的Windows窗体应用程序,工作正常。我已经决定用c#重写这个,我觉得这不会是一个太大的问题但是......

我在项目中有两个课程:

FormJobs& AppJobs

FormJobs包含以某种方式修改表单的方法和函数。

AppJobs包含其他所有方法和功能(检查,扫描等)。

在我的主窗体(FrmStart)上,On_load事件使用AppJobs中的函数来检查网络是否已启动(公共bool CheckNetConnection),然后检查以确保根保存文件夹存在(public void CheckRoot)。

如果CheckNetConnection为false或CheckRoot不存在,那么FormJobs类中的方法会将某些按钮设置为禁用,某些标签会显示有关错误的信息,并设置表单的高度。

以上在VB.net中有效,但我不断收到带有C#代码的StackOverflowException或NullReferenceException。

我知道异常的原因是因为这两个类和表单都保持相互调用所以我知道我需要删除这段代码,但我不知道如何让每个类和表单相互访问。这显然是糟糕的设计,因为我刚刚开始学习C#所以任何帮助都会非常感激。

但我的主要问题是: - 我如何获得一个表单来访问多个类?                            允许类互相访问?                            让类更改表单吗?

FrmStart代码

AppJobs Appjobs = new AppJobs();

private void FrmStart_Load(object sender, EventArgs e)
    {

                    KeyPreview = true;

        if (Appjobs.CheckNetConnection(this) == true)
        {
            Appjobs.CheckRoot(this);
        }

AppJobs代码

public class AppJobs
{

    FormJobs Formjobs = new FormJobs();

    public string AppRoot = Properties.Settings.Default.DefaultFolder;
    public string DefaultDevice = Properties.Settings.Default.DefaultScanner;
    public bool NoDirectory = false;

    DialogResult MsgBoxQuestion;

    public bool CheckNetConnection(Form StartForm)
    {

        IPHostEntry ServerIP = new IPHostEntry();
        bool ConnectedToServer = false;
        string CurrentRoot = "MyServer";

        if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
        {
            try
            {
                IPHostEntry DNSTest = Dns.GetHostEntry(CurrentRoot);
                if (DNSTest.AddressList.Length > 0)
                {
                    ConnectedToServer = true;
                }
                else
                {
                    ConnectedToServer = false;

                }


            }
            catch (System.Net.Sockets.SocketException ex)
            {
                ConnectedToServer = false;
            }
        }

        return ConnectedToServer;

    }

    public void CheckRoot(Form StartForm)
    {
        if (string.IsNullOrEmpty(AppRoot))
        {
            Formjobs.SetHeight(StartForm);
            return;


        }else if(AppRoot == "0")
        {
            Formjobs.SetHeight(StartForm);
            return;
        }
        else
        {
            if ((!Directory.Exists(AppRoot)))
            {
                NoDirectory = true;
                MsgBoxQuestion = MessageBox.Show(AppRoot + " is set, but the directory does not exist." + Environment.NewLine
                    + Environment.NewLine + "Would you like to create the folder now?", "Root folder missing", MessageBoxButtons.YesNo);
                if (MsgBoxQuestion == DialogResult.Yes)
                {
                    Directory.CreateDirectory(AppRoot);
                    NoDirectory = false;
                }
                else
                {
                    MessageBox.Show("You will not be able to use this program until you create a root folder.", "No root folder selected",MessageBoxButtons.OK);
                }

            }

        }

    }
}

FormJobs代码

public class FormJobs
{

    AppJobs Appjobs = new AppJobs();

    public void SetHeight(Form StartForm)
    {

        if (Appjobs.AppRoot == null | Appjobs.AppRoot == "0") {

if (Appjobs.DefaultDevice == null | Appjobs.DefaultDevice == "0") {

    if (StartForm.Controls["MenuStrip1"].Visible == true) {
        StartForm.Height = 167;
        StartForm.Controls["LblNoRoot"].Visible = true;
        StartForm.Controls["LblNoRoot"].Location = new Point(0, 24);
        StartForm.Controls["LblNoRoot"].Text = "There is no root folder selected. Please select a root folder to continue.";
        StartForm.Controls["LblNoDevice"].Visible = true;
        StartForm.Controls["LblNoDevice"].Location = new Point(0, 48);
        StartForm.Controls["LblNoDevice"].Text = "There is no default device selected. Please select a default device to continue.";
        StartForm.Controls["BtnOkTickets"].Enabled = false;
        StartForm.Controls["BtnQueryTickets"].Enabled = false;
        StartForm.Controls["BtnSearch"].Enabled = false;

    }else

        {
        StartForm.Height = 147;
        StartForm.Controls["LblNoRoot"].Visible = true;
        StartForm.Controls["LblNoRoot"].Location = new Point(0, 9);
        StartForm.Controls["LblNoRoot"].Text = "There is no root folder selected. Please select a root folder to continue.";
        StartForm.Controls["LblNoDevice"].Visible = true;
        StartForm.Controls["LblNoDevice"].Location = new Point(0, 33);
        StartForm.Controls["LblNoDevice"].Text = "There is no default device selected. Please select a default device to continue.";
        StartForm.Controls["BtnOkTickets"].Enabled = false;
        StartForm.Controls["BtnQueryTickets"].Enabled = false;
        StartForm.Controls["BtnSearch"].Enabled = false;

        }


}

2 个答案:

答案 0 :(得分:1)

扩展评论:您只需删除FormJobs和AppJobs类中的new部分。 将代码保留在FormJobs类中,例如:AppJobs appObj;
然后在你的主窗体中创建一个FormJobs obj和一个AppJobs obj并设置它的属性 即在主要表格中:

AppJobs appObj = new AppJobs(); 
FormJobs formObj = new FormJobs(); 
formObj.appObj = appObj;

我必须说我不喜欢你采取的这种方法...

你应该想到另一种方式或至少重构你的代码,FormJobs不需要AppJobs方法,反之亦然,所有对FormJobs和AppJobs的调用都来自你的主表单。

答案 1 :(得分:1)

问题的原因之一是每个人都在改变您的StartForm。除此之外,这种意大利面条使其难以理解,如果Startform发生变化,它肯定无法使您的课程可重复使用和维护。

在我看来,AppJobs旨在决定表单的外观(例如,它决定StartForm应该更改高度),而FormJobs执行计算需要改变这个高度。 StartForm显然只是允许每个人对他做出改变。

更好的设计是StartForm不会要求AppJobs更改其大小,并询问操作员是否应生成文件夹。相反,如果应该问appJobs建议:“你认为我应该拥有哪个高度”。之后,它可以询问FormJobs:“请根据此规范调整我的身高”

FormJobs应该信任StartForm它已经收集了有关StartForm应该是什么样子的正确信息。 FormJobs不应该向AppJobs询问任何信息:“嘿AppJobs,StartForm要求我将其外观改为某些规格,但我不确定StartForm是否正确完成了它的工作。请告诉我是否这些规范是正确的,并给我一些遗漏的信息“)

正确划分任务将是:

  • AppJobs根据其内部状态指定任何StartForm的格式(a.o. AppRoot,并存在某些文件夹)
  • StartForm是显示所有项目的人。他决定向谁索要规格,以及如何处理返回的规格。他也是唯一与经营者沟通的人
  • FormJobs是一个显然知道StartForm中所有元素的类。如果您只有一种StartForm类型,那么Appjobs应该是Startform类的一部分。如果您认为可能有几个不同的Startform类,所有类都应该以相同的方式操作,那么这些StartForm类是否都不能从FormJobs类派生出来? / LI>

无论如何,重新设计没有人导致操纵StartForm

显然,根据AppRoot,defaultDevice等,StartForm布局数量有限。您似乎在if之后缺少一些“else”,因此此列表可能不准确。你仍然会明白这个想法:

enum StartFormLayouts
{
    DefaultDevice0,
    AppRoot0,
    Other,        
}

// class that specifies the layout of any startform
class AppJobs
{
    private bool IsAppRoot0 
    {
        get{return Appjobs.AppRoot == null || Appjobs.AppRoot == "0";}
    }
    private bool IsDefaultDevice0
    {
        get{return Appjobs.DefaultDevice == null || Appjobs.DefaultDevice == "0";}
    }

    public StartFormLayoug GetPreferredLayout()
    {
         if (this.IsAppRoot0)
         {
             if (this.IsDefaultDevice)
             {
                  return StartFormLayout.DefaultDevice0;
             }
             else
                  return StartFormLayout.AppRoot0;
          }
          else
          {
              return StartFormLayout.Other;
          }
    }

    public bool ShouldAskDirectoryCreation()
    {
        return (!this.IsAppRoot0 && !Directory.Exists(AppRoot));
    }
}

请注意,此类不需要StartForm,也不需要AppJobs。它可以与任何想知道它是否应该要求DirectoryCreation的类一起使用。由于它也不会说任何语言,即使是中文StartForm也可以使用它。毕竟,StartForm是唯一知道它所说的语言以及如果请求某个布局该怎么做的人。

此外,您是否注意到我使用了双||来使用布尔OR而不是按位或?

我使用if (a)之类的语句代替if(a=true) C#布尔值是一个真正的布尔值,与C和C ++中的布尔值相矛盾。

应该能够根据请求的布局进行布局的各种表单类包含与您的

类似的功能

这取决于您是否决定让它成为StartFormStartForm本身的基类。如果您希望它处理具有所需控件的每个表单类,请考虑使用接口:

public Interface IStartForm
{
    public int Height {get; set;}
    public Label LabelNoRoot {get;}
    public Label LabelNoDevice {get; }
    public Button BtnTickets {get;}
    ...

这样,您可以设置包含这些标签和按钮的任何表单的大小,即使它们的名称与您使用的字符串不同。

但是又一次:如果你只想要StartForm的大小,那么这应该是StartForm中的一个函数。

public SetHeight(StartFormLayout layout, IStartForm startForm)
{
    switch (layout)
    {
        case StartFormLayout.DefaultDevice0:
            if (startForm.MenuStrip.Visible)
            {
                startForm.Height = ...;
                startForm.LabelNoRoot.Location = ...
                // etc
            }
            else
            {
               ...

注意到由于这种关注点的分离,AppJobs和FormJobs不必彼此了解。 AppJobs和FormJobs也不必知道'StartForm'是什么,只是它有标签和按钮等需要改变。

class StartForm : Form, IStartForm
{
    public Label LabelNoRoot {get{return this.label1; } }
    ...

    private void FrmStart_Load(object sender, EventArgs e)
    {
        AppJobs layoutdesigner = new AppJobs(...);
        StartFormLayout layoutdesigner = layouter.GetPreferredLayout();

        FormJobs layouter = new FormJobjs();
        layouter.SetHeight(this)
    }

注意到我的表单没有名为“LabelNoRoot”的标签,但是Label1应该用作LabelNoRoot。另外:因为我使用的是类型而不是字符串,你可以确定我不能像处理按钮那样处理标签。我不能偶然尝试按标签。当您使用字符串来标识要布局的项目时,可以轻松完成的任务。