I want to capture the OK Button's Click event on a MessageBox shown by another WinForms application.
I want to achieve this using UI Automation. After some research, I have found that IUIAutomation::AddAutomationEventHandler will do the work for me.
Though, I can capture the Click event of any other button, I'm unable to capture a Click event of the MessageBox.
My code is as follows:
var FindDialogButton = appElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "OK"));
if (FindDialogButton != null)
{
if (FindDialogButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
{
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, FindDialogButton, TreeScope.Element, new AutomationEventHandler(DialogHandler));
}
}
private void DialogHandler(object sender, AutomationEventArgs e)
{
MessageBox.Show("Dialog Button clicked at : " + DateTime.Now);
}
EDIT:
My Complete code is as follows:
private void DialogButtonHandle()
{
AutomationElement rootElement = AutomationElement.RootElement;
if (rootElement != null)
{
System.Windows.Automation.Condition condition = new PropertyCondition
(AutomationElement.NameProperty, "Windows Application"); //This part gets the handle of the Windows application that has the MessageBox
AutomationElement appElement = rootElement.FindFirst(TreeScope.Children, condition);
var FindDialogButton = appElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "OK")); // This part gets the handle of the button inside the messagebox
if (FindDialogButton != null)
{
if (FindDialogButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
{
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, FindDialogButton, TreeScope.Element, new AutomationEventHandler(DialogHandler)); //Here I am trying to catch the click of "OK" button inside the MessageBox
}
}
}
}
private void DialogHandler(object sender, AutomationEventArgs e)
{
//On Button click I am trying to display a message that the button has been clicked
MessageBox.Show("MessageBox Button Clicked");
}
I tried to keep this procedure as generic as possible, so that it will work whether the application you're watching is already running when your app is started or not.
You just need to provide the watched Application's Process Name or its Main Window Title to let the procedure identify this application.
Use one of these Fields and the corresponding Enumerator:
private string appProcessName = "theAppProcessName"; and
FindWindowMethod.ProcessName
// Or
private string appWindowTitle = "theAppMainWindowTitle"; and
FindWindowMethod.Caption
passing these values to the procedure that starts the watcher, e.g., :
StartAppWatcher(appProcessName, FindWindowMethod.ProcessName);
As you can see - since you tagged your question as winforms - this is a complete Form (named frmWindowWatcher) that contains all the logic required to perform this task.
How does it work:
When you start frmWindowWatcher, the procedure verifies whether the watched application (here, identified using its Process name, but you can change the method, as already described), is already running.
If it is, it initializes a support class, ElementWindow, which will contain some informations about the watched application.
I added this support class in case you need to perform some actions if the watched application is already running (in this case, the ElementWindow windowElement Field won't be null when the StartAppWatcher() method is called). These informations may also be useful in other cases.
When a new Windows is opened in the System, the procedure verifies whether this Window belongs to the watched application. If it does, the Process ID will be the same. If the Windows is a MessageBox (identified using its standard ClassName: #32770) and it belongs to the watched Application, an AutomationEventHandler is attached to the child OK Button.
Here, I'm using a Delegate: AutomationEventHandler DialogButtonHandler for the handler and an instance Field (AutomationElement msgBoxButton) for the Button Element, because these references are needed to remove the Button Click Handler when the MessageBox is closed.
When the MessageBox's OK Button is clicked, the MessageBoxButtonHandler method is called. Here, you can determine which action to take at this point.
When the frmWindowWatcher Form is closed, all Automation Handlers are removed, calling the Automation.RemoveAllEventHandlers() method, to provide a final clean up and prevent your app from leaking resources.
using System.Diagnostics;
using System.Linq;
using System.Windows.Automation;
using System.Windows.Forms;
public partial class frmWindowWatcher : Form
{
AutomationEventHandler DialogButtonHandler = null;
AutomationElement msgBoxButton = null;
ElementWindow windowElement = null;
int currentProcessId = 0;
private string appProcessName = "theAppProcessName";
//private string appWindowTitle = "theAppMainWindowTitle";
public enum FindWindowMethod
{
ProcessName,
Caption
}
public frmWindowWatcher()
{
InitializeComponent();
using (var proc = Process.GetCurrentProcess()) {
currentProcessId = proc.Id;
}
// Identify the application by its Process name...
StartAppWatcher(appProcessName, FindWindowMethod.ProcessName);
// ... or by its main Window Title
//StartAppWatcher(appWindowTitle, FindWindowMethod.Caption);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
Automation.RemoveAllEventHandlers();
base.OnFormClosed(e);
}
private void StartAppWatcher(string elementName, FindWindowMethod method)
{
windowElement = GetAppElement(elementName, method);
// (...)
// You may want to perform some actions if the watched application is already running when you start your app
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
TreeScope.Subtree, (elm, e) => {
AutomationElement element = elm as AutomationElement;
try
{
if (element == null || element.Current.ProcessId == currentProcessId) return;
if (windowElement == null) windowElement = GetAppElement(elementName, method);
if (windowElement == null || windowElement.ProcessId != element.Current.ProcessId) return;
// If the Window is a MessageBox generated by the watched app, attach the handler
if (element.Current.ClassName == "#32770")
{
msgBoxButton = element.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "OK"));
if (msgBoxButton != null && msgBoxButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
{
Automation.AddAutomationEventHandler(
InvokePattern.InvokedEvent, msgBoxButton, TreeScope.Element,
DialogButtonHandler = new AutomationEventHandler(MessageBoxButtonHandler));
}
}
}
catch (ElementNotAvailableException) {
// Ignore: this exception may be raised if you show a modal dialog,
// in your own app, that blocks the execution. When the dialog is closed,
// AutomationElement element is no longer available
}
});
Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, AutomationElement.RootElement,
TreeScope.Subtree, (elm, e) => {
AutomationElement element = elm as AutomationElement;
if (element == null || element.Current.ProcessId == currentProcessId || windowElement == null) return;
if (windowElement.ProcessId == element.Current.ProcessId) {
if (windowElement.MainWindowTitle == element.Current.Name) {
windowElement = null;
}
}
});
}
private void MessageBoxButtonHandler(object sender, AutomationEventArgs e)
{
Console.WriteLine("Dialog Button clicked at : " + DateTime.Now.ToString());
// (...)
// Remove the handler after, since the next MessageBox needs a new handler.
Automation.RemoveAutomationEventHandler(e.EventId, msgBoxButton, DialogButtonHandler);
}
private ElementWindow GetAppElement(string elementName, FindWindowMethod method)
{
Process proc = null;
try {
switch (method) {
case FindWindowMethod.ProcessName:
proc = Process.GetProcessesByName(elementName).FirstOrDefault();
break;
case FindWindowMethod.Caption:
proc = Process.GetProcesses().FirstOrDefault(p => p.MainWindowTitle == elementName);
break;
}
return CreateElementWindow(proc);
}
finally {
proc?.Dispose();
}
}
private ElementWindow CreateElementWindow(Process process) =>
process == null ? null : new ElementWindow(process.ProcessName) {
MainWindowTitle = process.MainWindowTitle,
MainWindowHandle = process.MainWindowHandle,
ProcessId = process.Id
};
}
Support class, used to store informations on the watched application:
It's initialized using the App's Process Name:
public ElementWindow(string processName)
but of course you can change it as required, using the Window Title as described before, or even remove the initialization's argument if you prefer (the class just need to not be null when the watched Application has been detected and identified).
using System.Collections.Generic;
public class ElementWindow
{
public ElementWindow(string processName) => this.ProcessName = processName;
public string ProcessName { get; set; }
public string MainWindowTitle { get; set; }
public int ProcessId { get; set; }
public IntPtr MainWindowHandle { get; set; }
}
Related
I'm using AutoUpdater.Net and I start the check on the application launch (this one work well). A second time from the MainWindow.
This is the function that I call :
public void CheckUpdate()
{
if (IsConnectedToInternet())
{
Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
Update.InstalledVersion = new Version(fvi.FileVersion);
Update.Synchronous = true;
//comment the line below to show the AutoUpdater.NET forms
Update.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent;
Update.Start("http:/myurl/");
}
else
IminaDialog.ShowMessage("Update error", "It seems you're not connected to internet.", ModalWindowButtons.Ok);
}
And the function AutoUpdateOnCheckForUpdateEvent is executing twice. It come to the last line of the method and directly restart the method. The call stack from the loading or from the MainWindow still the same. From the MainWindow I call it from the Instance of the parent class.
I hope someone can help and tell me if you want more informations.
EDIT
This is the call that execute twice AutoUpdateOnCheckForUpdateEvent (CheckUpdate execute once every time):
public static WpfCommand CommandCheckUpdate
{
get
{
if (_commandCheckUpdate == null)
{
_commandCheckUpdate = new WpfCommand();
_commandCheckUpdate.Executed += (sender, e) =>
{
FrameworkController.Instance.CheckUpdate();
};
}
return _commandCheckUpdate;
}
}
If I only call FrameworkController.Instance the CheckUpdate don't execute.
And here it's were the method is called on load (inside FrameworkController) :
public void Start(string[] mainArgs)
{
InitializeLog();
if (!License.RegisterLicense(null))
return;
DefaultMenuItems.PopulateMenuService(MenuService.Instance);
IminaUtils.Initialize(); // Initialize helper methods
CheckUpdate();
_firstConfigurationLoading = Configuration.Load();
MainWindowViewModel.ClearDiagnostic();
if (RightRepository.Instance.CurrentUser == null)
MainWindowViewModel.OpenLogin();
if (Configuration.ShutdownApp)
return;
ConnectConfiguration(mainArgs);
}
I'm trying to create a Popularity Contest for Forms in our primary front end. There are many items that are no longer used, but getting details on which are used and which are no longer used is proving to be difficult.
So I came up with the idea of logging a form when it is loaded and then in a year or so I'll run a group by and get an idea of which forms are used, how often, and by who. Now the issue is that I don't want to add a line to every forms InitializeComponent block. Instead I would like to put this in the Program.cs file and some how intercept all Form loads so I can log them.
Is this possible?
Edit
Using #Jimi's comment I was able to come up with the following.
using CrashReporterDotNET;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;
namespace Linnabary
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//This keeps the user from opening multiple copies of the program
string[] clArgs = Environment.GetCommandLineArgs();
if (PriorProcess() != null && clArgs.Count() == 1)
{
MessageBox.Show("Another instance of the WOTC-FE application is already running.");
return;
}
//Error Reporting Engine Setup
Application.ThreadException += ApplicationThreadException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//This is the SyncFusion License Key.
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("<Removed>");
//Popularity Contest
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent,
AutomationElement.RootElement, TreeScope.Subtree, (UIElm, evt) =>
{
try
{
AutomationElement element = UIElm as AutomationElement;
string AppText = element.Current.Name;
if (element.Current.ProcessId == Process.GetCurrentProcess().Id)
{
Classes.Common.PopularityContest(AppText);
}
}
catch (Exception)
{
//throw;
}
});
Application.Run(new Forms.frmMain());
}
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
ReportCrash((Exception)unhandledExceptionEventArgs.ExceptionObject);
Environment.Exit(0);
}
private static void ApplicationThreadException(object sender, ThreadExceptionEventArgs e)
{
ReportCrash(e.Exception);
}
public static void ReportCrash(Exception exception, string developerMessage = "")
{
var reportCrash = new ReportCrash("<Removed>")
{
CaptureScreen = true,
DeveloperMessage = Environment.UserName,
ToEmail = "<Removed>"
};
reportCrash.Send(exception);
}
public static Process PriorProcess()
{
Process curr = Process.GetCurrentProcess();
Process[] procs = Process.GetProcessesByName(curr.ProcessName);
foreach (Process p in procs)
{
if ((p.Id != curr.Id) && (p.MainModule.FileName == curr.MainModule.FileName))
{
return p;
}
}
return null;
}
}
}
However, I wonder if there is a way to get the name of the form instead of it's Text. Since this is accessing ALL windows and is therefor outside of the managed space, I doubt it. Still, it works and I'll post this as an answer tomorrow if no one else does so.
I'm posting the code that is required to detect and log Forms activity, for testing or for comparison reasons.
As shown, this code only needs to be inserted in the Program.cs file, inside the Main method.
This procedure logs each new opened Form's Title/Caption and the Form's Name.
Other elements can be added to the log, possibly using a dedicated method.
When a new WindowPattern.WindowOpenedEvent event detects that a new Window is created, the AutomationElement.ProcessId is compared with the Application's ProcessId to determine whether the new Window belongs to the Application.
The Application.OpenForms() collection is then parsed, using the Form.AccessibleObject cast to Control.ControlAccessibleObject to compare the AutomationElelement.NativeWindowHandle with a Form.Handle property, to avoid Invoking the UI Thread to get the handle of a Form (which can generate exceptions or thread locks, since the Forms are just loading at that time).
using System.Diagnostics;
using System.IO;
using System.Security.Permissions;
using System.Windows.Automation;
static class Program
{
[STAThread]
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
static void Main(string[] args)
{
Automation.AddAutomationEventHandler(
WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
TreeScope.Subtree, (uiElm, evt) => {
AutomationElement element = uiElm as AutomationElement;
if (element == null) return;
try
{
if (element.Current.ProcessId == Process.GetCurrentProcess().Id)
{
IntPtr elmHandle = (IntPtr)element.Current.NativeWindowHandle;
Control form = Application.OpenForms.OfType<Control>()
.FirstOrDefault(f => (f.AccessibilityObject as Control.ControlAccessibleObject).Handle == elmHandle);
string log = $"Name: {form?.Name ?? element.Current.AutomationId} " +
$"Form title: {element.Current.Name}{Environment.NewLine}";
File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "formLogger.txt"), log);
}
}
catch (ElementNotAvailableException) { /* May happen when Debugging => ignore or log */ }
});
}
}
Yeah, this should be easy. There are event hooks like OnLoad, OnShow, OnClose() for all forms and most user controls. If you wanted to see, at a more granule level what controls are being used by your users, you can hook up OnClick(), OnMouseOver() and about a hundred other events.
... and you can create your own custom events.
So, hook up the events by selecting the form, then properties (right click or F4 key). In the properties window at the top, you've got a "show events" button that looks like a lightning bolt. Click that and then pick, from the list, the event you want to use for this logging.
A not so expensive (maybe) solution can be this:
Create a new class MyBaseForm, which inherits from System.Windows.Forms.Form, and handle its load event in the way you need.
Now the hard part: modify all of the existing forms classes so they inherit from MyBaseForm and not from the default System.Windows.Forms.Form; and be sure you do the same for every future Form you will add to your solution.
Not bullet proof at all, it can be easy to forget to modify the base class for a new form and/or to miss the modification for an existing form class
But you can give it a try
Applying an IMessageFilter to the application to detect the WM_Create message and then determining if the target handle belonged to a Form would be ideal solution with a minimal performance hit. Unfortunately, that message does not get passed to the filter. As an alternative, I have selected the WM_Paint message to reduce the performance impact. The following filter code creates a dictionary of form type names and a count of Form's with that name ultimate disposal. The Form.Closed Event is not reliable under all closure conditions, but the Disposed event appears reliable.
internal class FormCreationFilter : IMessageFilter
{
private List<Form> trackedForms = new List<Form>();
internal Dictionary<string, Int32> formCounter = new Dictionary<string, Int32>(); // FormName, CloseCount
public bool PreFilterMessage(ref Message m)
{
// Ideally we would trap the WM_Create, butthe message is not routed through
// the message filter mechanism. It is sent directly to the window.
// Therefore use WM_Paint as a surrgogate filter to prevent the lookup logic
// from running on each message.
const Int32 WM_Paint = 0xF;
if (m.Msg == WM_Paint)
{
Form f = Control.FromChildHandle(m.HWnd) as Form;
if (f != null && !(trackedForms.Contains(f)))
{
trackedForms.Add(f);
f.Disposed += IncrementFormDisposed;
}
}
return false;
}
private void IncrementFormDisposed(object sender, EventArgs e)
{
Form f = sender as Form;
if (f != null)
{
string name = f.GetType().Name;
if (formCounter.ContainsKey(name))
{
formCounter[name] += 1;
}
else
{
formCounter[name] = 1;
}
f.Disposed -= IncrementFormDisposed;
trackedForms.Remove(f);
}
}
}
Create an instance and install the filter similar to the following example. The foreach loop is just shown to demonstrate accessing the count.
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
FormCreationFilter mf = new FormCreationFilter();
Application.AddMessageFilter(mf);
Application.Run(new Form1());
Application.RemoveMessageFilter(mf);
foreach (KeyValuePair<string, Int32> kvp in mf.formCounter)
{
Debug.Print($"{kvp.Key} opened {kvp.Value} times. ");
}
}
I'm trying to build a TAPI based phone call system using JulMar's Atapi x86. One of the functions is to pop a specific form on an inbound call. However, whenever the form pops, it comes up incorrect, as shown below (I have tried several forms as a test and they all do the same thing). There is no error, nothing in the output window to suggest what the issue is.
Code:
private void incomingcall(object sender, NewCallEventArgs e)
{
string phonenumber = e.Call.CallerId; //get the phone number of the call
SqlCommand getincoming = new SqlCommand(Querystrings.getincomingquery(), DB);
getincoming.Parameters.AddWithValue("##TELEPHONE", phonenumber);
DataTable results = new DataTable();
try
{
DB.Open();
using (var results = getincoming.ExecuteReader())
{
results.Load(results);
}
}
catch (Exception ex)
{
Inbound ib = new Inbound(phonenumber, null);
ib.Show();
}
finally
{
DB.Close();
}
if (results.Rows.Count == 1)
{
loadcontactrequest(Convert.ToInt32(results.Rows[0].ItemArray[0]), phonenumber);
}
else
{
loadinbound(phonenumber, results);
}
}
I have loaded these forms outside of this function at other points, meaning it is something to do with this function. Does anybody know where I'm going wrong?
EDIT:
private void loadcontactrequest(int ContactID, string phonenumber)
{
ContactRequest cr = new ContactRequest(ContactID, Global.loginbound("Single customer found", phonenumber));
cr.Show();
}
These functions have been tested elsewhere and work correctly individually, I believe it might be TAPI related.
EDIT 2 - Delegate:
public static void inittapi()
{
if (TestOptions.notapi)
return;
tapi = new TapiManager("Omitted");
tapi.Initialize();
foreach (TapiLine ad in tapi.Lines) //Get all lines available to this PC
{
if (ad.Name.ToUpper().Contains("Omitted"))
{
phoneline = ad;
phoneline.Open(MediaModes.All); //Open the phone line for making and receiving calls
phoneline.NewCall += new EventHandler<NewCallEventArgs>(new TAPI().incomingcall); //Add the incoming call event handler
}
}
}
It's possible that this event is triggered on a different thread than the UI thread of your application.
Modify the method like this to test whether this is the problem:
private void incomingcall(object sender, NewCallEventArgs e)
{
Form form;
if(Application.OpenForms.Count > 0)
{
form = Application.OpenForms[0];
}
if (form != null && form.InvokeRequired)
{
form.BeginInvoke(new Action(() => { incomingcall(sender, e); }));
return;
}
// Your current code goes here
}
This will identify that we are in a different thread than your main form (form) was created on and then execute the function again on the main form's thread.
So I'm making a Kinect application using buttons, and to navigate the app, I'm making new windows for each button. I'm come across an issue I haven't been able to find any help at all on, and would appreciate any help.
So to open the new window, I'm using this:
private void button1_Click(object sender, RoutedEventArgs e)
{
//SUPPOSED to uninitialize the Kinect
UninitializeKinectSensor(this.Kinect;
//To open the new window
Window1 newwindow = new Window1();
newwindow.Show();
//To close the first window...
Close();
{
SO that one line is supposed to uninitialize the Kinect so it'll be free for the new window to use, but when it goes to the new window, the Kinect freezes. If I use the mouse to go back to the first window, it works on the first window again, which it shouldn't.
I also added in this line in the initialization phase
public Window1()
{
//Other init code is here, but this is the line I added. It doesn't seem to do anything.
InitializeKinectSensor(this.Kinect);
}
Any help is greatly appreciated!! I'm sure it's something simple and I just failed miserably haha XD
Do you really have to create a new window instead of using pages?
In your MainWindow you create a frame that takes all the window and use this frame to navigate between pages. This way, you'll keep the focus of the kinect in your whole application.
Depends alot on what UninitializeKinectSensor is actually doing. Just as a quick fix, though, you can try calling uninitialize on a background worker and see if that helps at all.
Instead of using the "Show()" use "ShowDialog()".It's better if you can create a static class or method to initialize and uninitialized kinect.
public static void start()
{
KinectSensor.KinectSensors.StatusChanged += kinectSensorsStatusChanged;
DiscoverSensor();
}
private static void kinectSensorsStatusChanged(object sender, StatusChangedEventArgs e)
{
KinectSensor oldSensor = Kinect;
if (oldSensor != null)
{
UninitializeKinect();
}
var status = e.Status;
if (Kinect == null)
{
//updateStatus(status);
if (e.Status == KinectStatus.Connected)
{
Kinect = e.Sensor;
DiscoverSensor();
}
}
else
{
if (Kinect == e.Sensor)
{
//updateStatus(status);
if (e.Status == KinectStatus.Disconnected ||
e.Status == KinectStatus.NotPowered)
{
Kinect = null;
sensorConflict = false;
DiscoverSensor();
}
}
}
}
private static DispatcherTimer readyTimer;
private static void UninitializeKinect()
{
if (speechRecognizer != null && Kinect != null)
{
Kinect.AudioSource.Stop();
Kinect.SkeletonFrameReady -= kinect_SkeletonFrameReady;
Kinect.SkeletonStream.Disable();
Kinect.Stop();
//this.FrameSkeletons = null;
speechRecognizer.RecognizeAsyncCancel();
speechRecognizer.RecognizeAsyncStop();
}
if (readyTimer != null)
{
readyTimer.Stop();
readyTimer = null;
}
}
I have a main form that has most of the functionality in it. I was just wondering how would I pass on a variable from say a pop up form, to that main form.
Like for instance:
I have a main form that needs some connection info. So when you click the button "Enter Connection Info", it opens up a new form that the user can type in the IP Address he wants to use for his connection.
On this new form, I have a textbox and a button and once you enter the information it should close and pass on the string that contains the ip back to the original form.
Any suggestions? Do you think there is a better method of accomplishing this than using a windows form, and just going ahead and using a windows form or something? I'm quite perplexed on this issue at the moment.
Expose the textbox text as a public read only property. Show the connection form as a dialog and when it closes, get the connection from the property and then dispose the form:
in open form handler (button click, menu, whatever)
string connectionString = null;
using (ConnectionForm form = new ConnectionForm())
{
DialogResult result = form.ShowDialog();
if (result == DialogResult.Ok)
connectionString = form.ConnectionString
}
In you connection form:
public class ConnectionForm: Form
{
....
public string ConnectionString { get { return textBox1.Text; } }
}
You can create a public property in your main form and pass main form instance in pop-up constructor. In this way you can change the main form property.
You can also create an event in your pop-up form and hook it in your main form.
I like to use a pattern sort of like this (bear with me, c# is not my first language):
public class ValueForm: Form
{
public static string GetFromUser(string originalValue)
{
using (ConnectionForm form = new ConnectionForm())
{
form.TheValue = originalValue;
var result = form.ShowDialog();
if (result == DialogResult.Ok)
return form.TheValue;
else
return originalValue;
}
}
public string TheValue {
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
/* also some code behind your OK & cancel buttons to set
DialogResult appropriately,
and do any validation that you need to do
*/
}
and then you would use this like:
string newValue = ValueForm.GetFromUser(oldValue);
Reference Bind the controls on the dialog Form to properties of the Parent Form.
public dlgDbConnProps ( Form Owner )
{
// TODO: Complete member initialization
InitializeComponent ( );
owner = Owner;
}
private void cbo_ProviderLst_SelectedIndexChanged ( object sender, EventArgs e )
{
owner.Provider = cboLst.Text;
}
Here is another method that I have implemented:
... pass a Func to the child form constructor:
public dlgRequestLogin ( Func<string, string, bool> LoginMethod )
{
InitializeComponent ( );
p_loginMethod = LoginMethod;
}
... then handle on button click (or other appropriate event):
private void cmd_SendLoginCredentials_Click ( object sender, EventArgs e )
{
bool res = p_loginMethod.Invoke ( txt_UserID.Text, txt_UserPassword.Text );
}