使用一种形式的控制到另一种形式

时间:2009-11-15 03:15:29

标签: c# winforms oop

我有一个应用程序,在一种形式(Form1)我有很多checkBoxes和textBoxes,在Form2我只有一个textBox,但我想从{{1}中添加一些内容} textBoxes并放入Form1 textBox,就像这样,但是在表单之间:

Form2

textBox1.Text = "Test: " + textBox1 + "\n" + textBox3; 中的textBox1以及Form2中的第二个textBox1textBox3,但我该如何做到这一点?感谢。

5 个答案:

答案 0 :(得分:7)

根据您启动第二个表单的方式,您可以在Form2中分配一个引用Form1的成员对象,或者如果您使用的是MDI接口,则可以使用表单集合从中检索对Form1的引用。

例如,您可以在Form2类中使用以下代码:

public partial class Form2 : Form
{
    public Form1 LaunchOrigin { get; set; }
    // and so on

现在,您可以在启动Form2时分配LaunchOrigin成员。这是一个例子:

Form2 newForm = new Form2();
newForm.LaunchOrigin = this;
newForm.Show();

您现在可以访问Form1及其所有成员。这是一个简单的例子:

    private void Form2_Load(object sender, EventArgs e)
    {
        this.Text = LaunchOrigin.Text;
    }

您必须记住控件被声明为私有,因此您无法直接访问它们。你可以在Form1上写一个引用该控件的属性,但这通常是个坏主意。但是,为了完整起见,这里有一些代码可用于公开Form1上的按钮:

public partial class Form1 : Form
{
    public Button theButton;
    public Form1()
    {
        InitializeComponent();
        theButton = button1; // where button1 is what I dragged on
    }
    // and so on

虽然您要求的内容相对容易实现,但它会让您走上一些脆弱的应用程序结构。仔细想想你试图在表单之间展示什么,也许它应该是一个可以绑定到两个表单的独特类型,这样一旦你改变了底层类型,就可以改变两者的表示。

答案 1 :(得分:7)

有很好的方法可以做到这一点以及丑陋的方式......从UGLY开始:

  1. 最丑陋的一种方法是将对Form1的引用传递给Form2的构造函数,然后将该引用存储为字段以供日后使用。它很讨厌,因为它在两者之间产生了非常紧密的耦合。更改Form1的界面或行为会影响Form2。

  2. 一种不那么难看但仍然是hackish的方法是将Form1中的字符串值传递给Form2的构造函数 - 或者Form2上的一些公共/内部方法。你仍然依赖于这些字符串,但至少它不是Form1和Form2的完全耦合。只是期望Form2总是有一个Form1来为它们提供这些字符串。

  3. ...在这里填写一些更讨厌的变化。

    我建议你研究一个可以解决这个问题的应用程序范围的解决方案,而不会创建对象之间的依赖关系。

    创建一个适用于轻量级“pub / sub” - 或发布/订阅 - 模型的消息传递接口。我们的想法是,您的应用中有一些组件可以发布消息(您的字符串值 - 或更复杂的东西)以及您的应用中订阅消息的其他组件。

    当表单1启动时,它会在消息传递界面注册“嘿,我要发布这种类型的消息”(其中类型是您提供的一些指示符)。当表单2启动时,它会向消息传递接口注册“Yo,当有人发布此类消息时,将其传递给我”。

    发布者和订阅者都实现了一些已知的接口,以便您的消息处理程序可以与它们通信(IPublisher,ISubscriber) - 并接收/发送消息。如果有必要,一个组件既不是发布者也不是订阅者(基本上将系统中的对象视为“同伴”或“同事”)是没有理由的。一些MessageReceived事件和一些代码来管理一组发布者/订阅者和你'很高兴去。

    有关详细信息,请check out the Mediator pattern更详细地介绍此类解决方案。

答案 2 :(得分:3)

我认为大卫在达科他的答案和上面的杰夫唐尼奇给出了你所需要的一切,但我会“充实”杰夫明智地提出的更广泛方法的一个简单例子,我希望能让你开始

我可能与杰夫的回答有一点分歧,我相信这个问题的任何解决方案都是“依赖注入”或“依赖管理”的变体:即使你走“最抽象”的路线并拥有“发布者”向“未知订阅者”“播放”活动,但这只是我个人的意见。

这是一个简单的类型示例,有助于我在过去理解。

创建一个单独的公共静态类,在其中向要修改的所有表单上插入TextBox的静态引用(或者存储表单本身,或者其他):定义公​​共静态方法,将数据从一个表单的文本框移动到其他:非常快速的概述:(使用超长变量名是故意的,仅用于说明目的)

using System;
using System.Collections.Generic;
using System.Windows.Forms;

// note : compiles against FrameWork 2.0 and 4.0
// wanted this to work w/o Linq, automatic properties, etc.

namespace MessageHandler
{
    public static class TextBoxMessenger
    {
        // internal List of TextBoxes of interest on all Forms
        internal static List<TextBox> _messageEnabledTBxes = new List<TextBox>();

        // public Property to get/set the collection of TextBoxes of interest
        public static List<TextBox>MessageEnabledTextBoxes
        {
            get { return _messageEnabledTBxes; }

            set { _messageEnabledTBxes = value; }
        }

        // in case you want to register one TextBox at a time
        public static void RegisterTextBoxForMessaging(TextBox theTBx)
        {
            _messageEnabledTBxes.Add(theTBx);
        }

       // send from one specific TBx to another
        public static void setText(TextBox originTBx, TextBox destinationTBx)
       {
           destinationTBx.Text = originTBx.Text;
       }

       // send to a specified list of TextBoxes
        public static void setText(TextBox originTBx, List<TextBox> destinationTBxs)
       {
           foreach (TextBox theTBx in destinationTBxs)
           {
               theTBx.Text = originTBx.Text;
           }
       }

        // set text in all other TextBoxes in MessageEnabledTextBoxes list
        public static void setText(TextBox originTBx)
       {
           foreach (TextBox theTBx in _messageEnabledTBxes)
           {
               // a needless check, since assigning the text of the
               // original TextBox to itself wouldn't "hurt" anything
               // but, imho, much better "practice" to always test
               if (theTBx != originTBx) theTBx.Text = originTBx.Text;
           }
       }
    }
}

所以在行动中这是如何工作的:让我们使用一个示例,其中您的Form1 Load事件如下所示:

// assume Form2 has a single TextBox on it named 'textBox1'
public Form2 myForm2;

private void Form1_Load(object sender, EventArgs e)
{
    myForm2 = new Form2();
    myForm2.Show();

    // register all the TextBoxes

    // note the redundant use of 'this here : it's a deliberate choice to make
    // the code communicate to a future user/reader/maintainter
    TextBoxMessenger.RegisterTextBoxForMessaging(this.textBox1);
    TextBoxMessenger.RegisterTextBoxForMessaging(this.textBox2);
    TextBoxMessenger.RegisterTextBoxForMessaging((TextBox)myForm2.Controls["textBox1"]);

    // or ...
    //TextBoxMessenger.MessageEnabledTextBoxes = new List<TextBox> 
    //{
    //    this.textBox1, this.textBox2, (TextBox)myForm2.Controls["textBox1"]
    //};
}

你可以通过在Form1上放一个按钮来测试上面的内容:

private void button1_Click(object sender, EventArgs e)
{
    // tests

    // one to all ...
    //TextBoxMessenger.setText(this.textBox1);

    // one to a specific TextBox on another Form
    //TextBoxMessenger.setText(this.textBox1, (TextBox) myForm2.Controls["textBox1"]);

    // one to a List<TextBox>
    TextBoxMessenger.setText(this.textBox1, new List<TextBox> { this.textBox2, (TextBox)myForm2.Controls["textBox1"]});
}

但是,请注意“丑陋”,“糟糕的代码味道”,例如:

(TextBox)myForm2.Controls [“textBox1”] //施法是邪恶的!设置一个血腥的参考!

这就是你想要摆脱的一种事情,超越一个像这样的例子进入“竞技场”,杰夫,我相信,指向你。

希望这有帮助。在“长期运行”中,消息传递到更高级别,我相信Jeff所规定的,是imho,使代码示例比这个例子更强大,更具“推广性”的“皇家之路”。最好,

答案 3 :(得分:1)

在StackOverflow上多次询问此问题或其变体。我认为这是由于对Forms和Visual Studio设计师的误解引起的。

Form只是一个恰好代表可视窗口的标准类。将您自己的私有字段和公共属性添加到表单是完全合理的。

因此,当您需要在表单甚至应用程序的其他部分之间传递数据时,只需创建一个包含此数据的新类(而不是Form),并将其称为“ApplicationData”。这完全取决于此类包含的属性和字段。

然后,对于需要访问数据的每个Form,添加ApplicationData类型的public get / set属性。最后在显示每个表单之前将此属性设置为ApplicationData对象。

然后,表单可以从中获取数据并以任何方式更新此对象。因为它是一个引用类型(即类),所以对象的所有更改对于使用相同对象的任何其他形式都是可见的。

这是一个粗略的例子。请注意,此代码仅供参考:

class ApplicationData{
    private string _firstName;
    public string FirstName;
    {
       get { return _firstName;; }
       set { __firstName;=value; }
    }

    private string _lastName;
    public string LastName;
    {
       get { return _lastName; }
       set { __lastName=value; }
    }
}

class ChildForm : Form
{
   private ApplicationData _applicationData=null;

   public ApplicationData AppData
   {
       get { return _applicationData; }
       set { _applicationData=value; }
   }

   void Load_Form(object sender, EventArgs args)
   {
         txtFirstName.Text=AppData.FirstName;
         txtLastName.Text=AppData.LastName;
    }

   void Form_Closing(object sender, EventArgs args)
   {
         AppData.FirstName=txtFirstName.Text;
         AppData.LastName=txtLastName.Text;
    }

}

class MainForm : Form
{
    private ApplicationData _applicationData=new ApplicationData();

    void Button_Click(object sender, EventArgs args)
    {
        ChildForm childForm=new ChildForm ();

        ChildForm.AppData=_applicatonData;

        ChildForm.ShowDialog();

        string fullName=_applicatonData.LastName + " " + _applicatonData.FirstName
    }
}

其他问题中的一些答案,例如发布者/订阅者设计,对于除了非常复杂的应用程序之外的任何事情都是完全矫枉过正的。保持简单是一个非常重要的指导方针。

当然,Visual Studio设计器看起来所有类必须是可视化的,并且是从工具箱或“新建表单”选项创建的,这无济于事。这完全是错误的。

创建和使用“ApplicationData”类的设计模式是将演示文稿与内容分离的第一步。很遗憾Visual Studio在这方面没有提供更多的指导。

答案 4 :(得分:0)

您甚至可以通过编写参数化构造函数来完成它。

e.g。

namespace SomeNamespace
{
    public partial class Form2 : Form
    {
       string someStr = string.Empty;

        public Form2()
        {
            InitializeComponent();
        }

        public Form2(string params) // Parametrized Constructor
        {
            InitializeComponent();

            someStr  = params
        }

        private void Form2_Load(object sender, EventArgs e)
        {
          Form2TextBox.Text = someStr  ;
        }
    }
}

现在从Form1以这种方式调用Form2

Form2 objF2 = new Form2(Form1TextBox1.Text);
objF2.Showdialog();