在C#中怎样在两个Form之间传递数据(Winfrom)?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在C#中怎样在两个Form之间传递数据(Winfrom)?相关的知识,希望对你有一定的参考价值。

public class Form1 : System.Windows.Forms.Form//两个窗体之1

static string OPC;//静态变量用来保存

public string xx

setOPC=value;

getreturn OPC;

public class Form2 : System.Windows.Forms.Form//两个窗体之2

Form1 form1=new Form1();

form1.xx=要传递的值;

这个时候当执行了Form2后,Form2的值就保存在Form1中的OPC里。

根据具体问题类型,进行步骤拆解/原因原理分析/内容拓展等。
具体步骤如下:/导致这种情况的原因主要是……

参考技术A 在父窗体中显示子窗体时,加上子窗体.Owner=this;在子窗体中定义1个父窗体对象,在Load函数里边让父窗体对象=(父窗体类型)this.Owner。之后用这个父窗体对象就可以在子窗体的任何位置直接用它的参数了(这类参数要定义成公共的).将父窗体对象定义成子窗体的1个变量,不是在Load中定义父窗体对象,只是在Load中获取父窗口,这样在子窗体的任何位置都应当可以用这个父窗口对象了

在 WinForms 之间传递连续数据

【中文标题】在 WinForms 之间传递连续数据【英文标题】:Passing continuous data between WinForms 【发布时间】:2011-08-22 02:25:35 【问题描述】:

我正在制作个人 WinForms 应用程序。在我的场景中说我有一个 C#Form1Form1 不断从 Internet 获取实时 Exchange 数据。现在我点击Form1 上的一个按钮,Form2 就会打开。现在我想要来自Form1Form2 上的一些值。

我在Form2 上有一个计时器,它可以从Form1 收集数据,但是如何?

我曾尝试使用属性,但无法做到这一点,因为它只更新一次,就像我们初始化 Form2 时一样。

有什么办法吗?

另外,如果它们不是同时创建的,我如何将一个类的单个实例传递给两个表单?

【问题讨论】:

有完整源代码示例的最终解决方案吗? @shelleybutterfly 的回答非常详细,这正是我所做的,我已经完成了“方法 1:带有事件的数据源”,它非常整洁,非常干净,我很耳目一新交换数据,每秒刷新 10 次,效果很好,如果我可以多次投票,我已经为她做了 【参考方案1】:

我。解决方案:使用通用数据源

方法一:带事件的数据源

好吧,如果是我,我可能不会尝试直接从 Form1 获取数据。相反,我会设置一个公共数据源,然后您甚至可以取消 Form2 上的计时器,并在数据进入时驱动它(如果您愿意)。 (或者您可以保留它,并按照所需的时间间隔从数据源中提取。)

应该是这样的:

数据源类

public class ExchangeCommonDataSource

    public event EventHandler NewDataReceived;
    public void FireNewDataReceieved()
    
        if (NewDataReceived != null)
           NewDataReceived();
    

    private string mySomeData1 = "";
    public string SomeData1 
    
        get
        
            return SomeData1;
        

        set
        
            SomeData1 = value;
            FireNewDataReceieved();
        
    

    // properties for any other data 

然后,当您打开表单时,您只需创建一个 ExchangeCommonDataSource 实例,并将其传递给两个表单。在接收数据的表单中,您需要创建一个事件处理函数,并且无论您将数据源传递到何处,都将连接该事件。

示例:接收类代码

public void HandleDataReceived(object sender, EventArgs e)

    // display the data
    DoSomethingWith(mySource.SomeData1);

    // etc...


private ExchangeCommonDataSource mySource;
public void SetDataSource(ExchangeCommonDataSource newSource)

    mySource = newSource;
    mySource.NewDataRecieved += new EventHandler(HandleDataReceived);

然后,在您的第一个表单中,您只需设置所需的属性。实际上,您可以通过单独的事件处理程序或通过创建自己的派生 EventArgs 然后使用 EventHandler<ExchangeCommonEventArgs> 而不是常规事件处理程序来获得指定要加载的实际数据的通知。

示例:主表单数据访问器

public void GetDataFromExchange()

    mySource.SomeData1 = GetSomeData1FromExchange();

此外,通过这种方式,您不仅可以使用这两种形式进行交流;如果您决定将其拆分为不同的表单,您可以让他们每个人都有一个数据源的副本,并且每个人都可以处理您定义的事件或新事件,并且您不会被绑定到您所在的模型重新期望彼此之间直接交流。例如,这还允许创建一个单独的类,将一些日志数据写入磁盘,或者您可以想象的任何其他内容,而无需对您现有的任何内容进行重大更改。


二。外部更新的可扩展性

调度程序基类

那么,如果您想更新以最终发送到另一个应用程序或另一台机器怎么办?

嗯,这实际上已经很好地解释了,因为您对剩下的表单没有任何依赖关系。所以,假设你想支持三种方法:initial,form to form 方法;通过命名管道发送到同一台机器上的另一个应用程序;和 TCP/IP 完全连接到另一台机器。您需要做的就是定义一个充当调度程序的类,将其连接为接收器,然后您可以连接该对象以获取来自表单的事件并将数据放置在您想要的任何位置。

定义一个抽象类或接口来执行此操作应该相当简单,然后简单地为您想要支持的任何模式派生一个类:

示例:一个概念抽象 Dispatcher 类

public class ExchangeDataDispatcher :
   IDisposable

    public ExchangeDataDispatcher(ExchangeCommonDataSource parDataSource)
    
        myDataSource = parDataSource;
        myDataSource.HandleDataReceived += 
            new EventHandler(HandleDataReceived);

        DispatcherInitialization();
    

    private ExchangeCommonDataSource myDataSource;

    private void HandleDataReceived(object sender, e EventArgs)
    
        // here you could record statistics or whatever about the data
        DispatcherHandleDataReceived(EventArgs);
    

    protected abstract void  DispatcherHandleDataReceived(e EventArgs);

    protected abstract void DispatcherShutdown();

    // significantly ripped from Microsoft's page on IDisposable
    private bool disposed = false;
    protected virtual void Dispose(bool disposing)
    
        // Check to see if Dispose has already been called.
        if(!this.disposed)
        
            // If disposing equals true, dispose all managed
            // and unmanaged resources.
            if(disposing)
            
                // call a function which can be overridden in derived
                // classes
                DispatcherShutdown();
            

            // Note disposing has been done.
            disposed = true;
        
            

请参阅the Microsoft page on IDisposable 了解一些出色的示例代码以及有关 IDisposable 的更多信息...

为其他通信方法派生调度程序

没有办法让表单本身从这个类派生,但没有真正的需要,因为您可以像以前一样连接。但是,作为一个简单的例子(只是名义上的,并没有真正实现协议,你真的应该考虑实现这些类型的东西的最佳方法,但我想给你一个相当全面的例子来说明它需要什么,它并不那么简单因为真正天真的版本往往是。)

示例:(非常)概念化的基于管道的调度程序

// add these to your using statments
using System.IO.Pipes;
using System.Threading;

// NOTE: take all the async stuff with a grain of salt; this should give you a
// basic idea but there's no way I've gotten it right without actually testing
// and debugging everything. See the link 
// http://***.com/questions/6710444/named-pipes-server-read-timeout
// for some information on why it has to be done this way: basically timeout
// is not supported for named pipe server streams.
public class ExchangeDataLocalMachineDispatcher :
   ExchangeDataDispatcher

    // see http://www.switchonthecode.com/tutorials/dotnet-35-adds-named-pipes-support
    // for some info on named pipes in .NET
    public ExchangeDataLocalMachineDispatcher(
        ExchangeCommonDataSource parDataSource, 
        NamedPipeServerStream ServerPipe
    ) :
      base(parDataSource)
    
        myPipe = ServerPipe;

        // do any extra initialization, etc. here, negotiation for instance

        StartPipeThread();
    

    private NamedPipeServerStream myPipe;
    private ExchangeCommonDataSource myDataSource;

    // assuming you have PipeMessage defined and that your handler
    // fills them in.
    private List<PipeMessage> myOutgoingMessages = 
        new List<PipeMessage>(); 

    private Thread myPipeThread;
    private bool EndPipeListener = false;
    private AutoResetEvent myWaitEvent = null;
    private AutoResetEvent myDataReadyToGoEvent = null;

    // set this to something reasonable for the response timeout
    private int WaitTimeout = 10000; 

    // example: at least every minute there should be data to send
    private int WaitForDataToSendTimeout = 60000; 

    private void StartPipeThread()
    
        IAsyncResult LastResult = null;

        Action<IAsyncResult> WaitForResult =
            (a) =>
            
                LastResult = a;
                myWaitEvent.Set();
            

        myPipeThread = new System.Threading.ThreadStart(
        () => 
        
          try
          
              myWaitEvent = new AutoResetEvent(false);

              myPipe.BeginWaitForConnection(
                  WaitForResult, null
              );

              bool TimedOut = !myWaitEvent.WaitOne(WaitTimeout);

              if (TimedOut || !LastResult.IsCompleted)
                  throw new Exception("Error: pipe operation error.");

              while (!EndPipeListener)
              
                  byte[] Response = myPipe.BeginRead(
                     WaitForResult, null
                  );

                  myWaitEvent.WaitOne(WaitTimeout);

                  if (TimedOut || !LastResult.IsCompleted)
                      throw new Exception("Error: pipe operation error.");

                  // another assumed function to handle ACKs and such
                  HandleResponse(Response);

                  myWaitEvent.Set();

                  // now wait for data and send
                  bool TimedOut = 
                      myDataReadyToGoEvent.WaitOne(WaitForDataToSendTimeout);

                  if (TimedOut || !LastResult.IsCompleted)
                      throw new Exception("Error: no data to send.");

                  // an assumed function that will pull the messages out of
                  // the outgoing message list and send them via the pipe
                  SendOutgoingMessages();

                  myDataReadyToGoEvent.Set();
              

              myWaitEvent.Set();
          

          finally
          
              // here you can clean up any resources, for instance you need
              // to dispose the wait events, you can leave the pipe for the
              // DispatcherShutdown method to fire in case something else
              // wants to handle the error and try again... this is all
              // fairly naive and should be thought through but I wanted  
              // to give you some tools you can use.

              // can't remember if you're supposed to use .Close
              // .Dispose or both off the top of my head; I think it's
              // one or the other.

              myWaitEvent.Dispose();
              myDataReady.Dispose();

              myWaitEvent = null;
              myDataReady = null;     
          
        
        );
    

    protected PipeMessage[] ConstructEventMessage(e EventArgs)
    
        // actually we're not using the event args here but I left it
        // as a placeholder for if were using the derived ones.

        return 
            PipeMessage.CreateMessagesFromData(
                myDataSource.GetMessageData()
            );
    

    protected override void  DispatcherHandleDataReceived(e EventArgs)
    
        // create a packet to send out; assuming that the 
        // ConstructEventMessage method is defined

        myOutgoingMessages.Add(ConstructEventMessage(e));
    

    protected override void DispatcherShutdown()
    
        // this is called from the base class in the Dispose() method
        // you can destroy any remaining resources here
        if (myWaitEvent != null)
        
            myWaitEvent.Dispose();
        

        // etc. and

        myPipe.Dispose();
    

    // you could theoretically override this method too: if you do, be
    // sure to call base.Dispose(disposing) so that the base class can
    // clean up if resources are there to be disposed. 
    // protected virtual void Dispose(bool disposing)
    // 
    //     // do stuff
    //     base.Dispose(disposing);
    // 

呸。请注意,我目前对 StartPipeThread 函数的长度非常不满意,我肯定会对其进行重构。

因此,您也可以为 TCP/IP 套接字或您可以想象的任何协议实现此功能,并且无需不断修改第一部分中的类即可处理所有这些。

对于那里的任何代码质量,我深表歉意;我愿意对此提出建议/更正/抨击,如果你让我知道,我会尽我所能做出更正。 :P


三。将数据放在需要的地方

完成此设置后,您需要将相同的数据传递给正在使用它的任何表单。如果您没有同时创建两个表单,那么您将需要某种方法来让每个目标都引用相同的数据源。 (注意:选项的编号绝不意味着这些是您唯一的选择!)

以下是一些这样做的选择:

选项 1:通过主表单上的属性

如果您的主窗体负责创建每个子窗体(例如,通过菜单项),则此方法适用。您只需创建一个成员变量来保存数据,并且无论您在何处创建数据,都可以在该成员中存储对它的引用。如果您有多个源实例,则可以存储它们,例如在字典中,您可以查找所需的。

示例:主窗体代码

private ExchangeCommonDataSource myData  get; set; 

// you can also store in something that lets you identify multiple
// possible data sources; in this case, you could use, say, email address
    // as a lookup: myData["mickey@example.com"]; 

//private Dictionary<string, ExchangeCommonDataSource> myData =
//  new Dictionary<string, ExchangeCommonDataSource>();

public frmMyMainForm()

    InitializeComponent();

    // ... other initialization for the main form ...

    // create the data here and save it in a private member on your
    // form for later; this doesn't have to be in the constructor,
    // just make sure you save a reference to the source when you 
    // do create your first form that uses the source.
    myData = new ExchangeCommonDataSource();


// then, in the methods that actually create your form
// e.g. if creating from a menu item, the handlers
public void FirstFormCreatorMethod()

    frmFirstForm = new frmFirstForm(myData);
    frmFirstForm.MdiParent = this;
    frmFirstForm.Show();


public void SecondFormCreatorMethod()

    frmSecondForm = new frmSecondForm(myData);
    frmSecondForm.MdiParent = this;
    frmSecondForm.Show();

选项 II:static 数据源上的属性

如果表单是从主表单外部创建的,则可以使用此选项,在这种情况下,您将无法访问其方法。这种方法背后的想法是,您需要一种简单的方法来查找所需的任何项目,独立于主表单本身,并且通过提供静态方法,其他数据消费者可以使用只能访问的属性自行查找源类声明,然后是某种键(如果可以有多个来源)。

示例:ExchangeCommonDataSource.cs

// a dummy source class; this is just the parts that were relevant
// to this particular discussion.
public partial class ExchangeCommonDataSource

    public string Username  get; set; 
    public string OptionalString  get; set; 
    public int MailboxNumber  get; set; 
    public Guid SourceGuid  get; set; 
    public long BigNumber  get; set; 


    // these static members provide the functionality necessary to look
    // retrieve an existing source just through the class interface 

    // this holds the lookup of Guid -> Source for later retreival
    static Dictionary<Guid, ExchangeCommonDataSource> allSources = 
            new Dictionary<Guid,ExchangeCommonDataSource>();

    // this factory method looks up whether the source with the passed 
    // Guid already exists; if it does, it returns that, otherwise it
    // creates the data source and adds it to the lookup table
    public static ExchangeCommonDataSource GetConnection(
            Guid parSourceGuid, string parUsername, long parBigNumber
    )
    
        // there are many issues involved with thread safety, I do not
        // guarantee that I got it right here, it's to show the idea. :)

        // here I'm just providing some thread safety; by placing a lock 
        // around the sources to prevent two separate calls to a factory
        // method from each creating a source with the same Guid. 
        lock (allSources)
        
            ExchangeCommonDataSource RetVal;
            allSources.TryGetValue(parSourceGuid, out RetVal);

            if (RetVal == null)
            
                // using member initializer, you can do this to limit the
                // number of constructors; here we only need the one 
                RetVal = new ExchangeCommonDataSource(parSourceGuid) 
                    Username = parUsername, BigNumber = parBigNumber
                ;

                allSources.Add(parSourceGuid, RetVal);
            

            return RetVal;
        
    

    // this function is actually extraneous since the GetConnection 
    // method will either create a new or return an existing source.
    // if you had need to throw an exception if GetConnection was
    // called on for existing source, you could use this to retrieve
    public static 
        ExchangeCommonDataSource LookupDatasource(Guid parSourceGuid)
    
        // again locking the sources lookup for thread-safety. the 
        // rules: 1. don't provide external access to allSources
        //        2. everywhere you use allSources in the class, 
        //           place a lock(allsources   block around it
        lock (allSources)
        
            ExchangeCommonDataSource RetVal;
            allSources.TryGetValue(parSourceGuid, out RetVal);
            return RetVal;
        
    

    // private constructor; it is private so we can rely on the 
    // fact that we only provide factory method(s) that insert the
    // new items into the main dictionary
    private ExchangeCommonDataSource(Guid SourceGuid) 
    
        // if you didn't want to use a factory, you could always do
        // something like the following without it; note you will
        // have to throw an error with this implementation because 
        // there's no way to recover. 

        //lock (allSources)
        //
        //   ExchangeCommonDataSource Existing; 
        //   ExchangeCommonDataSource.allSources.
        //      TryGetValue(parSourceGuid, out Existing);

        //   if (Existing != null)
        //      throw new Exception("Requested duplicate source!");
        //

        // ... initialize ...
    

现在要访问,客户端只需要某种密钥来访问数据:

示例:frmClientClass.cs

public partial class frmClientClass 

    ExchangeCommonDataSource myDataSource = null;

    public void InitializeSource(Guid parSourceGuid)
    
        myDataSource = ExchangeCommonDataSource.GetConnection(parSourceGuid);
    

我发现这通常是比选项 1 更具吸引力的解决方案,因为任何可以访问类和 ID 的东西都可以获取数据源,并且因为它相当容易实现,并且它自动支持执行多个实例您的数据源类。

它的开销相当低,而且由于在大多数情况下,获取数据源是不会在紧密循环中完成的(如果是的话,您将拥有本地副本,而不是从字典每次)任何小的性能损失都应该值得易用。而且,最重要的是,即使您从一个数据源开始,您也可以轻松地将应用程序扩展到更多,而无需重写任何代码或进行任何进一步的工作。

例如,假设您只有一个数据源,一种非常快速的使用方法是使用一个已知值作为您的 Dictionary 键,然后您现在可以在第二个中对其进行硬编码。因此,例如,您可以将空 GUID 作为您的密钥,并将其用于您的两个表单。即主表单或您的第一个数据表单将使用 Guid.Empty 调用 create 方法来最初创建数据,然后您可以在打开第二个表单时使用它来访问它。

选项 3:“单例”模式类

好的,我不会为此花费太多时间或编写代码,但如果我不提及它,我会失职。它与选项 2 非常相似,除了使用静态 Dictionary 来查找多个数据源之外,您创建了一个类,该类的一个实例存储在静态属性中,并且您防止(通过异常)任何尝试创建更多课程。然后,将所有构造函数设置为private,如果静态变量已经包含一个对象,则让它们抛出异常,然后创建一个 getInstance() 方法,该方法返回该类的单个实例,如果它为空则创建它。

现在,您需要了解一些线程安全小技巧问题才能编写传统的单例,因此请务必了解这些问题(*** 上存在处理该问题的问题)。如果您不需要任何特定知识来构造类的实例,则可以通过简单地初始化声明它的变量来避免这些问题,例如static MyClass theInstance = new MyClass();,如果您曾经使用过,我强烈建议您这样做。

我在(相当遥远的)过去使用过单例,并不是说它们偶尔没有用处,尤其是在嵌入式系统中。但是,这不是嵌入式系统,几乎每次我在 GUI 应用程序中使用 Singleton 时,我都会后悔这样做,因为我最终将它重写为允许多个实例的东西。如果你真的只需要一个副本,你所要做的就是在使用它的类中放置一个成员变量,比如你的主窗体,并确保你只创建一个。这样做,您甚至可以通过在可以触发异常的类中设置静态标志来使用该模式;首次创建对象时将其设置为 true,然后如果为 true,则可以抛出异常。

无论如何,我个人关于何时编写单例的第一条规则是:除非您确定永远不会需要多个单例,否则不要这样做。如果它通过了那个,那么第二条规则是:你错了,它有一种可能发生的方式,所以只要把它写成一个普通的类,并以其他方式处理它的单例性。 :) 说真的,真正的规则是,除非你有非常充分的理由或从中获得显着的好处,否则不要这样做。

哦,重申一下:很可能实现单例的模式,而无需编写规范的单例。模式很好,只要当需要该类的第二个实例时,消除该模式的成本非常低。

选项 4:单独的类

选项 4 与选项 2 非常相似,但在第二类中实现。 (事实上​​,如果你认为你可能有多个数据源,那么从这里开始是值得的,尽管最初设置的时间要多一点。)而不是将你的静态项作为该类的成员,实现另一个具有类似它们并提供访问权限的类。这是一种将类本身与类的创建分离的方法。例如,如果您正在编写一个库,并且您想提供几种不同类型的数据源,您可以实现一个基类,然后从基类派生您的其他对象,然后通过提供工厂的类提供创建机制创建不同种类的方法。

在这种情况下,您很可能根本不希望使用您的数据源的任何东西都必须知道有关数据源类的实现的任何信息,而只通过基本接口,这提供了一个简单的方法来做到这一点。如果您必须将其全部编写为基类静态成员,那么每次派生新类时都会强制重写基类,并且还会强制基类了解派生类的一些信息,其中每个一般来说,是要避免的。换句话说,并不是说它永远没有用,而是没有很好的理由就不要这样做,也不要在没有理解其含义的情况下这样做。

示例:外部类的代码

InfostoreBase.cs

// our data source base class; could do interface instead like:
// public interface IInfostoreBase
public abstract class InfostoreBase

    public abstract int Information  get; set; 
    public abstract string NameOfItem  get; set; 
    public abstract decimal Cost  get; set; 

    // ... etc ...

InfostoreEnterprise.cs

public class InfostoreHomeEdition :
    InfostoreBase

    public override int Information  get  /* ... */  set  /* ... */ 
    public override string NameOfItem  get  /* ... */  set  /* ... */ 
    public override decimal Cost  get  /* ... */  set  /* ... */ 

    public void SetFeatures(string parSomething)  /* ... */ 

InfostoreHomeEdition.cs

public class InfostoreEnterpriseEdition :
    InfostoreBase

    public override int Information  get  /* ... */  set  /* ... */ 
    public override string NameOfItem get  /* ... */  set  /* ... */ 
    public override decimal Cost  get  /* ... */  set  /* ... */ 

    public void SetBaseDiscount(decimal parSomethingElse)  /* ... */ 

InfostoreProvider.cs

public class InfostoreProvider

    static Dictionary<Guid, InfostoreBase> allSources = 
            new Dictionary<Guid,InfostoreBase>();

    public static InfostoreBase 
        GetHomeConnection(Guid CustomerKey, string HomeFeatures)
    
        lock (allSources)
        
            InfostoreBase RetVal;

            if (!ValidHomeKey(CustomerKey))
                throw new 
                    InvalidKeyException("not valid for Home Edition");

            allSources.TryGetValue(CustomerKey, out RetVal);

            if (RetVal == null)
            
                RetVal = new InfostoreHomeEdition();
                allSources.Add(CustomerKey, RetVal);
            

            var ActualVersion = (InfostoreHomeEdition) RetVal;

            RetVal.SetFeatures(HomeFeatures);

            return RetVal;
        
    

    public static InfostoreBase 
        GetEnterpriseConnection(Guid CustomerKey, decimal BaseDiscount)
    
        lock (allSources)
        
            InfostoreBase RetVal;

            if (!ValidEnterpriseKey(CustomerKey))
                throw new 
                    InvalidKeyException("not valid for Enterprise Edition");

            allSources.TryGetValue(CustomerKey, out RetVal);

            if (RetVal == null)
            
                RetVal = new InfostoreHomeEdition();
                allSources.Add(CustomerKey, RetVal);
            

            var ActualVersion = (InfostoreEnterpriseEdition) RetVal;

            RetVal.SetBaseDiscount(CostBase);

            return RetVal;
        
    

客户端类中的代码

private InfostoreBase myConnectionSource;
private void Initialize()

    // ...

    myConnectionSource = 
        InfostoreProvider.GetConnection(
            myKey, isEnterprise, myData
        );

    //...

结束

我认为这涵盖了很多可能的解决方案;它们都不是特别难以实施,并且每个都有自己的优点和缺点。一般来说,我会选择选项 2 或选项 4,但 [破纪录] 它始终取决于您的具体情况。我认为使用扩展这些来处理许多不同的情况会相当容易。当然,如果有任何问题,请告诉我。

【讨论】:

这是我今天尝试的方法,它可以工作,谢谢分配,它是一个单一的应用程序,所以不需要管道,但是这种方法是我使用它的技能先进的,我没有完全掌握它,但是任何怎么样,它就像魅力一样 再次感谢亲爱的非常详细的说明 太棒了!很高兴听到你让它工作...... :) 非常欢迎你。我很感激你特意感谢我,很高兴听到我试图帮助某人的事情对他们有用。 :) 嗨,如果它们不是同时创建的话,我如何将一个类的单个实例传递给两个表单 好的,最后添加了额外的部分,并带有一些选项。让我知道你有任何问题。 :)【参考方案2】:

另一种可能的处理方法是创建一些接口来表示数据提供者和数据接收者的角色,然后您将在表单上实现这些接口。这与使用公共数据源非常相似,但不是通过对象运行事物,而是实现接口并且数据可以直接到达需要的地方。通过数据源执行此操作可能会更有效,尽管在不了解所有细节的情况下很难说,但是如果您真的要传输大量数据,将其通过单独的数据源可能会降低您的效率,特别是如果您从未需要一个位置的所有数据。

在这里的示例代码中,我展示了如果您为不同类型的数据实现自己的事件参数会是什么样子,如果您希望能够在事件的公共数据源中使用它,这也可以使用对何时发送的内容有更多的粒度。 (请记住,我已经在网页上输入了所有内容而没有尝试编译它;这应该让你知道如何去做,但它可能(我估计 100% 改变)我没有不能完美地完成所有事情。:D)

public class FirstDataKindEventArgs : EventArgs

    public FirstDataKindEventArgs(int parID, string parName, string parOtherInfo)
    
        Id = parId; 
        Name = parName;
        OtherInfo = parOtherInfo;
    

    public int ID  get; set; 
    public string Name  get; set; 
    public string OtherInfo  get; set; 
 

// plus other event arg definitions

public interface IExchangeDataProvider

    event EventHandler<FirstDataKindEventArgs> FirstDataKindReceived;
    event EventHandler<SecondDataKindEventArgs> SecondDataKindReceived;
    event EventHandler<ThirdDataKindEventArgs> ThirdDataKindReceived;       


public interface IExchangeDataReceiver

    void ConnectDataProvider(IExchangeDataProvider Provider);

然后在您的数据提供表单中,您将实现接口:

public partial class MyProvidingForm : System.Windows.Forms.Form, IExchangeDataProvider

   // normal form stuff

   // ...

    #region IExchangeDataProvider
    public event EventHandler<FirstDataKindEventArgs> FirstDataKindReceived;
    public event EventHandler<SecondDataKindEventArgs> SecondDataKindReceived;
    public event EventHandler<ThirdDataKindEventArgs> ThirdDataKindReceived;

    public void FireDataReceived(EventArgs Data)
    
        FirstDataKindEventArgs FirstKindData = Data as FirstDataKindEventArgs;

        if (FirstDataKindEventArgs != null)
            if (FirstDataKindReceived != null)
                FirstDataKindReceived(FirstKindData);

        //... etc. 
    

    public void GotSomeDataOfTheFirstKind(int TheID, string SomeName, string Other)
    
        FirstDataKindEventArgs eArgs = 
            new FirstDataKindEventArgs(TheId, SomeName, Other);

        FireDataReceived(eArgs);
    

在您的接收者表单或您希望接收数据的其他类中:

    public partial class FirstDataKindReceivingForm :
        System.Windows.Forms.Form,
        IExchangeDataReceiver
    
        // usual form stuff

        // ...

        private IExchangeDataProvider myDataProvider;
        public void ConnectDataProvider(IExchangeDataProvider Provider)
        
            myDataProvider = Provider;
            myDataProvider.FirstDataKindReceived += 
                new EventHandler<FirstDataKindEventArgs>(
                    HandleFirstKindOfDataReceived
                );
        


        private void HandleFirstKindOfDataRecieved (
            object sender, FirstDataKindEventArgs
        )
        
            // do whatever with data
        
    

    #endregion

等等。

【讨论】:

【参考方案3】:

编辑 Form2 的构造函数,以便您可以在使用 .Show 或 .ShowDialog 运行新的 Form2 时从 Form1 传递一些值

Form2 myForm = new Form2(value1, value2, value3 ...);

在 Form2.cs 上,您应将 public Form2() 转换(或添加新的)公共 Form2(var value1, var value 2...)

如果你必须连续向 Form2 发送数据,你可以使用共享内存或共享数据文件。

【讨论】:

当然我需要将 cont 数据发送到 from2 ,所以我认为我需要一个命名管道的共享数据源 查看 shelleybutterfly 的回答 ;) 因为它是同一个应用程序中的两种形式,我会说实际的管道是多余的,并且像我发布的任何一种方法一样,您可以轻松地编写一个位于数据之间的类如果您最终移至单独的应用程序或单独的机器,则提供者和接收者可以根据需要发送数据。我可以更新我的数据源答案以说明我的意思。 就像一个注释:无论它是否是连续数据,它最终都会以离散块的形式发送。因此,例如,通过字节流 Pipe 发送和具有采用 byte[] 参数的函数在理论上没有区别,因为无论哪种方式它都会被分解。所以,我认为这也适用于这种方法;你可以只用一个void DataStreamChunkReceived(byte[] LastReceived) 来获取数据,这与byte[] ReceivedData = myPipe.Read(/*...*/); 调用之间没有真正的区别。 :)【参考方案4】:

Mahrous 在 db 论坛上的答案似乎是最简单的http://www.daniweb.com/software-development/csharp/threads/126879/617436#post617436 其他一些解决方案也是有效的,并且根据应用程序的设计可能是合适的。

【讨论】:

由于链接经常失效,如果您真正解释这里发生的事情而不是链接到外部站点会更好。另外,我确实看了一下,基本上它只是说给一个表单引用另一个表单,并直接在其上设置属性,但这有很多自己的问题,特别是如果这是一个应用程序未来得到进一步发展。传递表单会使表单高度耦合,并且几乎没有灵活性来改变事物,所以我只想说,除了一次性的爱好应用程序之外,我不会选择这种方式。

以上是关于在C#中怎样在两个Form之间传递数据(Winfrom)?的主要内容,如果未能解决你的问题,请参考以下文章

c# 中form与form之间的数据传递

在mui中怎样实现两个页面之间传递数据

菜鸟求助:c#三个窗体间传递数值

C#开发项目时怎么在form1和form2之间传递数据

C#问题...Form2怎么获取Form1的按钮事件!..

怎样在C#中实现父窗体向子窗体传值和子窗体向父窗体传值