Использование элементов управления одной формы в другую

У меня есть приложение, которое в одной форме (Form1) У меня есть много флажков и текстовых полей и в Form2 У меня есть только textBox, но я хочу поместить некоторое содержимое из Form1 текстовые поля и положить в Form2 textBox, как это, но между формами:

textBox1.Text = "Test: " + textBox1 + "\n" + textBox3;

Как textBox1 в Form2 а второй textBox1 а также textBox3 в Form1, но как я могу это сделать? Благодарю.

5 ответов

Решение

В зависимости от того, как вы запускаете вторую форму, вы можете либо назначить объект-член в Form2, который ссылается на Form1, либо, если вы используете интерфейс MDI, существует коллекция форм, из которой вы можете получить ссылку на вашу Form1.

Например, у вас может быть следующий код в вашем классе Form2:

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

Теперь вы можете назначить члена LaunchOrigin при запуске Form2. Вот пример:

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. Одним из самых уродливых было бы передать ссылку на Form1 в конструктор Form2, а затем сохранить эту ссылку как поле для дальнейшего использования. Это противно, потому что это создает очень тесную связь между ними. Изменение интерфейса или поведения Form1 влияет на Form2.

  2. Менее уродливый, но все же хакерский способ сделать это - передать строковые значения из Form1 в конструктор Form2 - или какой-нибудь публичный / внутренний метод в Form2. У вас все еще есть зависимость вокруг этих строк, но по крайней мере это не полная связь между Form1 и Form2. Это просто ожидание того, что Form2 всегда будет иметь Form1 для подачи этих строк.

... заполните здесь еще несколько неприятных вариантов.

Я предлагаю вам рассмотреть решение для всего приложения, которое решает эту проблему и не создает межобъектных зависимостей.

Создайте интерфейс обмена сообщениями, который работает на облегченной модели "публикация / подписка" или "публикация / подписка". Идея состоит в том, что у вас есть некоторые компоненты в вашем приложении, которые публикуют сообщения (ваши строковые значения - или что-то более сложное) и другие компоненты в вашем приложении, которые подписываются на сообщения.

Когда форма 1 запускается, она регистрируется в интерфейсе обмена сообщениями "Эй, я собираюсь публиковать сообщения этого типа" (где тип - это какой-то индикатор, который вы предоставляете). Когда форма 2 запускается, она регистрируется с интерфейсом обмена сообщениями, говоря: "Эй, когда кто-то публикует сообщение такого типа, передайте его мне".

И издатель, и подписчик реализуют некоторый известный интерфейс, чтобы ваш обработчик сообщений мог общаться с ними (IPublisher, ISubscriber) - и получать / отправлять сообщения. Нет никакой причины, по которой один компонент может быть как издателем, так и подписчиком, если это гарантировано (по сути, рассматривая объекты в системе как "коллеги" или "коллеги"). Несколько событий MessageReceived и некоторый код для управления коллекцией издателей / подписчиков и вас. хорошо идти.

Для получения более подробной информации, ознакомьтесь с шаблоном посредника, который описывает этот тип решения более подробно.

Я думаю, что ответы Дэвида из Дакоты и Джеффа Донничи, выше, дают вам все, что вам нужно, но я "продемонстрирую" тривиальный пример более широкого подхода, мудро предложенного Джеффом, который, я надеюсь, поможет вам начать.

У меня есть небольшое несогласие с ответом Джеффа: я считаю, что любое решение этой проблемы - это вариант "внедрения зависимостей" или "управления зависимостями": даже если вы идете по "наиболее абстрактному" маршруту и ​​у вас есть "издатели" "транслировать" события "неизвестным подписчикам", но это только мое личное мнение.

Вот простой пример типа, который мне было полезно понять в прошлом.

Создайте один общедоступный статический класс, в который вы вставляете статические ссылки на TextBox для всех форм, которые вы хотите изменить (или сохраняете сами формы, или что-то еще): определите общедоступные статические методы для перемещения данных из текстового поля одной формы в другую (s).): очень быстрый набросок: (использование сверхдлинных имен переменных является преднамеренным и предназначено только для пояснения)

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"] // приведение - это зло! установить кровавую ссылку!

Это та вещь, от которой вы хотите избавиться, выйдя за пределы подобного примера на "арену", на которую, я полагаю, указывает Джефф.

Надеюсь, это полезно. В "долгосрочной перспективе" абстракция передачи сообщений на более высокий уровень, который, как я полагаю, предписывает Джефф, является "королевской дорогой" к созданию примеров кода, гораздо более мощных и более "обобщаемых", чем этот пример. Лучший,

Этот вопрос или его разновидности задавались много раз на Stackru. Я думаю, что это вызвано неправильным пониманием форм и дизайнера Visual Studio.

Форма - это просто стандартный класс, представляющий визуальное окно. Вполне разумно добавить свои личные поля и общедоступные свойства в форму.

Поэтому, когда вам нужно передать данные между формами или даже другими частями вашего приложения, просто создайте новый класс (не форму), который будет содержать эти данные, и назовите его чем-то вроде "ApplicationData". От вас зависит, какие свойства и поля содержит этот класс.

Затем для каждой формы, которой требуется доступ к данным, добавьте открытое свойство get / set типа ApplicationData. Наконец, установите это свойство для объекта 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 не дает большего руководства в этой области.

Вы даже можете сделать это, написав параметризованный конструктор.

например

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();
Другие вопросы по тегам