Использование элементов управления одной формы в другую - PullRequest
3 голосов
/ 15 ноября 2009

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

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

Как textBox1 в Form2, а во втором textBox1 и textBox3 в Form1, но как мне это сделать? Спасибо.

Ответы [ 5 ]

7 голосов
/ 15 ноября 2009

Существуют достойные способы сделать это и безобразные способы сделать это ... начиная с UGLY:

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

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

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

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

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

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

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

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

7 голосов
/ 15 ноября 2009

В зависимости от того, как вы запускаете вторую форму, вы можете либо назначить объект-член в 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

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

3 голосов
/ 15 ноября 2009

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

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

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

Создайте один общедоступный статический класс, в который вы вставляете статические ссылки на 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"] // кастинг - это зло! установить кровавую ссылку!

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

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

1 голос
/ 26 ноября 2009

Этот вопрос или его разновидности задавались много раз на StackOverflow. Я думаю, что это вызвано неправильным пониманием форм и конструктора 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 не дает больше рекомендаций в этой области.

0 голосов
/ 15 ноября 2009

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

, например

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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...