C# 在WinForms之间传递连续数据
我正在制作一个个人WinForms应用程序。在我的场景中,假设我有一个C#C# 在WinForms之间传递连续数据,c#,.net,winforms,C#,.net,Winforms,我正在制作一个个人WinForms应用程序。在我的场景中,假设我有一个C#表单1Form1不断从互联网上获取实时交换数据。现在我点击Form1上的一个按钮,Form2打开。现在,我想从Form2上的Form1中获取一些值 我在Form2上有一个计时器,它可以从Form1收集数据,但是如何收集呢 我曾尝试使用属性,但无法做到这一点,因为它只在初始化Form2时更新一次 有解决办法吗 另外,如果不同时创建两个窗体,如何将一个类的单个实例传递给这两个窗体?编辑Form2的构造函数,以便在使用.Show
表单1
Form1
不断从互联网上获取实时交换数据。现在我点击Form1
上的一个按钮,Form2
打开。现在,我想从Form2
上的Form1
中获取一些值
我在Form2
上有一个计时器,它可以从Form1
收集数据,但是如何收集呢
我曾尝试使用属性,但无法做到这一点,因为它只在初始化Form2时更新一次
有解决办法吗
另外,如果不同时创建两个窗体,如何将一个类的单个实例传递给这两个窗体?编辑Form2的构造函数,以便在使用.Show或.ShowDialog运行新Form2时,可以从Form1传递一些值 Form2 myForm=新的Form2(value1、value2、value3 在Form2.cs上,您应将公共Form2()转换(或添加一个新的)为公共Form2(var值1,var值2…) 如果必须连续向Form2发送数据,可以使用共享内存或共享数据文件 I.解决方案:使用公共数据源 方法1:包含事件的数据源 如果是我,我可能不会直接从表格1中获取数据。相反,我将设置一个公共数据源,然后您甚至可以消除Form2上的计时器,并在数据进入时驱动它(如果您愿意)。(或者,您可以离开它,按照所需的时间间隔从数据源中提取数据。) 应该是这样的: 数据源类 然后,在打开表单时,只需创建ExchangeCommonDataSource的一个实例,并将其传递给这两个表单。在接收数据的表单中,您需要创建一个事件处理函数,并且无论您将其传递到何处的数据源,都将连接该事件 示例:接收类代码 然后,在第一个表单中,您只需设置所需的属性。实际上,您可以通过单独的事件处理程序,或通过创建自己的派生事件参数,然后使用
EventHandler
而不是常规事件处理程序,来获得指定要加载的实际数据的通知
示例:主窗体数据访问器
而且,通过这种方式,你不仅仅局限于这两种形式的交流;如果您决定使用不同的表单将其拆分,那么您可以让每个表单都有一个数据源的副本,并且每个表单都可以处理您定义的事件或新事件,并且您不需要绑定到期望彼此直接通信的模型。例如,这还允许创建一个单独的类,将一些日志数据写入磁盘,或者您可以想象的任何其他内容,而无需对任何现有内容进行重大更改
二,。外部更新的可扩展性 Dispatcher基类 所以,如果您希望更新,最终发送到另一个应用程序或另一台机器,甚至是其他应用程序,该怎么办 事实上,这是很好的解释,因为你对剩下的表单没有任何依赖关系。所以,假设您想要支持三种方法:初始、表单到表单方法;通过命名管道发送到同一台机器上的另一个应用程序;和TCP/IP完全连接到另一台机器。您需要做的就是定义一个充当调度器的类,将其连接为接收器,然后您可以连接该对象以获取来自表单的事件,并将数据放在您想要的任何位置 定义一个抽象类或接口来实现这一点应该相当简单,然后简单地为您想要支持的任何模式派生一个类: 示例:一个概念化的抽象分派器类 有关IDisposable的一些重要示例代码和更多信息,请参阅 为其他通信方法导出调度员 没有办法使表单本身从这个类派生,但是没有真正的需要,因为您可以像以前一样连接。但是,作为一个很快的例子(只是名义上的,实际上并没有实现协议,你真的应该考虑实现这些类型的最佳方法,但我想给你一个相当全面的例子,它不是真正的天真的版本往往是简单的。) 示例:(非常)概念上的基于管道的调度器 选项二:
静态
数据源上的属性
如果表单是从主表单外部创建的,则可以使用此选项,在这种情况下,您将无法访问其方法。这种方法背后的思想是,您需要一种简单的方法来找到您需要的任何项目,独立于主窗体本身,并通过提供静态方法,其他数据使用者可以使用仅通过访问类声明和某种键(如果可以有多个源)即可访问的属性自己查找源
示例:ExchangeCommonDataSource.cs
我发现这是一个比选项1更具吸引力的解决方案,这仅仅是因为任何可以访问类和ID的东西都可以获得数据源,而且它非常容易实现,并且自动支持执行数据源类的多个实例
它具有相当低的开销,而且由于在大多数情况下,获取数据源是不可能在紧密循环中完成的事情(如果是这样的话,您将拥有本地副本,而不是每次都从字典中查找它们),因此任何小的性能损失都应该值得轻松使用。而且,最重要的是,即使从一个数据源开始,也可以轻松地将应用程序扩展到更多数据源,而无需重写任何代码或进行任何进一步的工作
例如,假设您只有一个数据源,那么使用它的一个非常快速的方法就是只使用字典键的一个已知值,然后您现在就可以在第二个数据源中硬编码它。因此,例如,您可以将空GUID作为键,并将其用于两个表单。
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
}
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);
}
public void GetDataFromExchange()
{
mySource.SomeData1 = GetSomeData1FromExchange();
}
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;
}
}
}
// 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://stackoverflow.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);
// }
}
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();
}
// 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 ...
}
}
public partial class frmClientClass
{
ExchangeCommonDataSource myDataSource = null;
public void InitializeSource(Guid parSourceGuid)
{
myDataSource = ExchangeCommonDataSource.GetConnection(parSourceGuid);
}
}
// 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 ...
}
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) { /* ... */ }
}
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) { /* ... */ }
}
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
);
//...
}
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
}