I am developing a Client/server app.
In the Client app, I have a Main Form that is an MDI parent.
The Main Form has a Load event that creates a new instance of a Child Form and makes it visible.
This event also establishes Main as the MdiParent of Child.
The Child form is meant to be a sign-in screen.
From the child form, I create a reference to the parent, to be able to call methods from the parent.
However, upon executing the MdiParent.RequestConnection method, the GUI becomes stuck.
So I tried to execute the method from a Thread, but it is not accepting my declaration, even if I'm seemingly following the correct syntax.
I don't see what am I doing wrong. Please help
Main form
public partial class frmMainForm: Form
{
public frmMainForm()
{
InitializeComponent();
}
Thread runningClient;
public MyTcpClient client= new MyTcpClient ();
frmChildForm frmSignIn;
bool clientConnected;
private void frmMainForm_Load(object sender, EventArgs e)
{
clientConnected= false;
panelSidebar.Hide();
if(frmSignIn == null)
{
frmSignIn= new frmChildForm();
frmSignIn.MdiParent = this;
frmSignIn.Show();
}
}
public void TurnOnPanels()
{
panelSidebar.Visible = true;
panelSidebar.BringToFront();
}
public void RequestConnection(string username)
{
string serverRsp = client.Connect(username);
if(serverRsp.Equals("SUCCESS"))
{
MessageBox.Show("Signed In", "Welcome", MessageBoxButtons.OK, MessageBoxIcon.Information);
clientConnected = true;
frmSignIn.Close();
}
}
}
And my child form
public partial class frmChildForm : Form
{
frmMainForm frmParent;
Thread clientRunning;
public frmChildForm()
{
InitializeComponent();
frmParent= (frmMainForm)this.MdiParent;
}
private void frmSignIn_FormClosing(object sender, FormClosingEventArgs e)
{
frmParent= (frmMainForm)this.MdiParent;
frmParent.TurnOnPanels();
}
private void btnSignIn_Click(object sender, EventArgs e)
{
if (txtSignInUsername.Text.Equals(""))
{
MessageBox.Show("No empty fields.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
//This is where it fails
clientRunning= new Thread(new ParameterizedThreadStart(frmParent.RequestConnection);
//"No Overload for RequestConnection matches delegate ParameterizedThreadStart"
//If I try to include the parameter inside that call, I get a "waiting for method //name" syntax error message instead.
clientRunning.Start(txtSignInUsername.Text.ToUpper());
}
}
private void frmSignIn_Load(object sender, EventArgs e)
{
frmParent = (frmMainForm)this.MdiParent;
}
}
I also tried to do it from the main form by creating a thread inside RequestConnection, where it was supposed to execute client.Connect, but I got the same error.
Couple of things you need to fix
public void RequestConnection(object username) // changed parameter type
{
if (username == null)
throw new ArgumentNullException(nameof(username));
if (!(username is string))
throw new InvalidCastException("Expect string");// give proper message
string serverRsp = client.Connect(username.ToString());
if (serverRsp.Equals("SUCCESS"))
{
MessageBox.Show("Signed In", "Welcome", MessageBoxButtons.OK, MessageBoxIcon.Information);
clientConnected = true;
//this is require to solve cross-thread operation
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(delegate ()
{
frmSignIn.Close();
}));
else
frmSignIn.Close();
}
}
you need to get MdiParent in Form Load Event and remove from Constructor.
Your child form load event/ or use Parent changed Event
private void FrmLogin_Load(object sender, EventArgs e)
{
frmParent = (MainForm)this.MdiParent;
}
The ParametrizedThreadStart delegate actually takes an object as parameter.
So you must provide it a method that takes an object as parameter, not as string. In your method you will receive the object, check that it is a string then convert it to a string.
Related
In my windows form application I call, from the main MDIContainer form, a class in which I invoke the open of the child form.
In Main Form :
private void btnTemplate_ItemClick(object sender, ItemClickEventArgs e)
{
beiProgressBar.EditValue = "Form opening in progress...";
repositoryItemMarqueeProgressBar1.ShowTitle = true;
beiProgressBar.Visibility = BarItemVisibility.Always;
bwTemplate.RunWorkerAsync();
}
private void bwTemplate_DoWork(object sender, DoWorkEventArgs e)
{
FrmTemplate frm;
frm = new FrmTemplate();
Callback.SetFormTemplate(this, ref frm);
}
In the Callback class I show the child form :
public delegate void SetFormTemplateCallback(FrmMain pFormMain, ref FrmTemplate pForm);
public static void SetFormTemplate(FrmMain pFormMain, ref FrmTemplate pForm)
{
if (pFormMain.InvokeRequired)
{
SetFormTemplateCallback d = new SetFormTemplateCallback(SetFormTemplate);
pFormMain.Invoke(d, new object[] { pFormMain, pForm });
}
else
{
pForm.MdiParent = pFormMain;
pForm.InitForm();
pForm.Show();
}
}
This operation randomly hangs my application.
I also tried with BeginInvoke but the problem is still present.
I noticed that often the application freeze when it is minimized.
It is difficult also debug this error.
Anyone has ever reached a similar behavior ?
what i have to do is kiosk app that read informations from a serial port and switch between three different forms depending on data received. forms can also switch between each other by button.
simplyfing:
1 Form1 active until data received
2 main program read data
3 form1 closed
4 form2 opend and elaborate recived data
5 form2 button clicked
6 form3 opened
7 form 2 closed
8 loop restart
my approach
is to use this example
https://www.codeproject.com/Articles/7536/How-To-Swap-Top-Level-Forms?msg=969882 and i make this modify to ApplicationContext but if i use originale example it work but i don't know how to pass method with delegate, if i use modified code a can pass delegate bu form freeze and some time form not open. i.m not sure that tis is the bast way, i cannot move readin serial data to form1
any suggestion or example to follow?
public class MainFormManager : ApplicationContext
{
protected bool exitAppOnClose;
// original
//Declare delagete callback function, the owner of communication
public SetOrderDelegate ProcessOrder;
public Form CurrentForm {
get { return MainForm; }
set {
if (MainForm != null) {
// close the current form, but don't exit the application
exitAppOnClose = false;
MainForm.Close();
exitAppOnClose = true;
}
// switch to the new form
MainForm = value;
MainForm.Show();
}
}
// modified
private Frm1 frm1;
private Frm2 frm2;
private Frm3 frm3;
public void ShowFrm1()
{
if (frm1 == null) {
frm1 = new Frm1();
frm1.Closed += mainFormClosed; // avoid reshowing a disposed form
this.ProcessOrder += new SetOrderDelegate(frm1.ProcessOrder);
MainForm = frminizio;
MainForm.Show();
} else {
frm1.Activate();
}
}
private void mainFormClosed(object sender, EventArgs e)
{
frminizio = null;
}
public void ShowFrm2()
{
if (frm2 == null) {
frm2 = new Frm2();
frm1.Closed += frm2Closed; // avoid reshowing a disposed form
//this.ProcessOrder += new SetOrderDelegate(frm3.ProcessOrder);
MainForm.Close();
MainForm = frm2;
MainForm.Show();
} else {
frm2.Activate();
}
}
private void frm2Closed(object sender, EventArgs e)
{
frm2 = null;
}
public void ShowFrm3()
{
if (frm3 == null) {
frm3 = new Frm3();
frm3.Closed += frm3Closed; // avoid reshowing a disposed form
//this.ProcessOrder += new SetOrderDelegate(frm3.ProcessOrder);
MainForm.Close();
MainForm = frm3;
MainForm.Show();
} else {
frmPaga.Activate();
}
}
internal sealed class Program
{
public Program()
{
//all other stuff
Application.Run(mainFormManager);
}
static void SerialReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(500);
//SetTextDeleg BcRun = BarCodeRun;
string data = serial.ReadLine();
if (data == true) {
// this is only a semplyfied example //
frm1.setdata(data);
task.loop (2000);
frm2.setdata(data);
frm1.close();
frm2.open();
}
}
}
It freezes because you have Thread.Sleep(500) in your SerialReceived method. So if you get a bunch of data, you'll be frozen pretty much at all times.
You shouldn't have thread pauses in your serial IO events because it backs up the data and depending on the baud speed you are going at, you'll likely lose data (the underlying chip, called UART doesn't carry a lot of onboard memory to store all the buffered data).
I have problem with logging operation in WP7. When I click LogInButton it doesn't gets value to prompt = e.Result in proxy. What can I do to wait until async call is ended? I though about Thread. Sleep but i suppose it isn't able to do in WP 7.
namespace WP7App
{
public partial class MainPage : PhoneApplicationPage
{
bool prompt;
// Constructor
public MainPage()
{
InitializeComponent();
}
// login operation
private void Log(string username, string passwd)
{
Service1Client proxy = new Service1Client();
proxy.LogInCompleted += new
EventHandler<LogInCompletedEventArgs>(proxy_LogInCompleted);
proxy.LogInAsync(username, passwd);
}
public void proxy_LogInCompleted(object sender, LogInCompletedEventArgs e)
{
prompt = e.Result;
}
//button action
void LogInButton_Click(object sender, RoutedEventArgs e)
{
if (LoginBox.Text == null) { MessageBox.Show("please fill login box"); }
if (PasswdBox.Password == null) { MessageBox.Show("enter your password"); }
string login = LoginBox.Text;
string passwd = PasswdBox.Password;
Log(login, passwd);
if (prompt == true)
{
NavigationService.Navigate(new Uri("/Pages/MainLogged.xaml", UriKind.Relative));
}
else
{
MessageBox.Show("logging failed");
}
}
}
}
The only thing you should do is just provide user information, that some operation is still running.
If you really need that (the operation result is necessary for application) it can be done in a way, that blocks user from making another operations (like a fullscreen panel which should be shown just before starting async operation and closed when it's completed).
I have a Windows Forms application at the moment, and I want to create a new thread and run a method on another class that accepts an input.
For example
public partial class Form1: Form {
SerialPort serialInput;
// I want to create a new thread that will pass the parameter serialInput into the method
// SMSListener on another class and run the method contionously on the background.
}
class SMS
{
public void SMSListener(SerialPort serial1)
{
serial1.DataReceived += port_DataRecieved;
}
private void port_DataRecieved(object sender, SerialDataReceivedEventArgs e)
{
// Other codes
}
}
How do I perform this in C#? I have seen numerous examples on the web, and most of them run the method on the same class with no parameters, but none that suits my requirements.
Perhaps a Background Worker could help you?
It is a bit hard to understand what you are aiming at.
public class Runner
{
private readonly BackgroundWorker _worker = new BackgroundWorker();
public Runner()
{
_worker.DoWork += WorkerDoWork;
}
public void RunMe(int payload)
{
_worker.RunWorkerAsync(payload);
}
static void WorkerDoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
while (true)
{
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
// Work
System.Threading.Thread.Sleep((int)e.Argument);
}
}
}
I am not an expert on Multithreading but to the best of my knowledge you can only start threads on methods that accept an object parameter and return void. So in order to achieve that for your problem (don't shoot me down if there is a better approach!) I would do something like
public partial class Form1: Form {
SerialPort serialInput;
// I want to create a new thread that will pass the parameter serialInput into the method
// SMSListener on another class and run the method contionously on the background.
SMS sms = new SMS();
Thread t = new Thread(sms.SMSListenerUntyped);
t.Start(serialInput);
}
class SMS
{
public void SMSListenerUntyped(object serial1) {
if (serial1 is SerialPort) //Check if the parameter is correctly typed.
this.SMSListener(serial1 as SerialPort);
else
throw new ArgumentException();
}
public void SMSListener(SerialPort serial1)
{
serial1.DataReceived += port_DataRecieved;
}
private void port_DataRecieved(object sender, SerialDataReceivedEventArgs e)
{
// Other code.
}
How about just use the ThreadPool directly with a anonymous method allowing you to access your surrounding locals?
public void OnButtonClick(object sender, EventArgs e)
{
SerialPort serialInput = this.SerialInput;
System.Threading.ThreadPool.QueueUserWorkItem(delegate
{
SmsListener listener = new SmsListener(serialInput);
});
}
Is it possible to close a form while the constructor is executing (or simply to stop it showing at this stage)?
I have the following code:
public partial class MyForm : Form
{
public MyForm()
{
if (MyFunc())
{
this.Close();
}
}
}
Which throws an ObjectDisposedException in Main(), here:
static void Main()
{
...
// Following line errors
Application.Run(new MyForm());
}
I’ve tried checking the result of MyForm like this:
static void Main()
{
...
MyForm frm = new MyForm();
if (frm != null)
{
// Following line errors
Application.Run(frm);
}
}
But that doesn’t seem to help. Can anyone tell me a way around this, please? Maybe a way to check the form to see if it still exists?
Calling Close from the constructor of the Form is not possible, as it will call Dispose on a Form that has not yet been created. To close the Form after construction, assign an anonymous event handler to the Load event that closes your Form before it is displayed for the first time:
public partial class MyForm : Form
{
public MyForm()
{
if (ShouldClose())
{
Load += (s, e) => Close();
return;
}
// ...
}
// ...
}
The only thing you could do it set a flag to close it in the constructor, and then closing it in the Shown event. Of course, if you're doing that, it makes sense to move the code to determine whether it should be closed there in the first place.
The following works well:
public partial class MyForm : Form
{
public MyForm()
{
if (MyFunc())
{
this.Shown += new EventHandler(MyForm_CloseOnStart);
}
}
private void MyForm_CloseOnStart(object sender, EventArgs e)
{
this.Close();
}
}
When you call Close() on a form, internally it is disposing of the form and releasing any managed resources. When you do this:
Application.Run(new MyForm());
You'll likely get an ObjectDisposedException. What you need to do is set the Form's visibility through a property:
Application.Run(new MyForm() { Visible = false });
Just make sure you remove the call to Close() in the constructor, or even move the property assignment there too.
Can you make MyFunc static? and then do something like:
static void Main()
{
...
if (MyForm.MyFunc())
{
Application.Run(new MyForm());
}
}
this would essentially give you the same control over whether the form is going to be constructed or not?
I found adding a handler to the 'Load' event is better as this way the dialog is never displayed at all. With the 'Shown' event you might briefly see the dialog open and then close which may be confusing:
public partial class MyForm : Form
{
public MyForm()
{
if (MyFunc())
{
this.Load += MyForm_CloseOnStart;
}
}
private void MyForm_CloseOnStart(object sender, EventArgs e)
{
this.Close();
}
}
I think it is not wise to close a form in the constructor. If you do this, users of your form wouldn't know whether to ShowDialog or not.
The following code would be quite normal use:
// in the parent form:
public void ShowMyForm()
{
MyForm form = new MyForm();
form.propertyA = ...;
from.propertyB = ...;
DialogResult dlgResult = form.ShowDialog(this);
ProcessDialogResult(dlgResult);
}
If you decided in the constructor whether the Form ought to be shown, you would have to add code after construction to decide whether to call ShowDialog or not and whether to Process the dialog result.
Furthermore, are you sure that changing the properties will never influence whether the form is to be shown or not? Also after future changes?
During construction the form is not shown / opened yet. So I'm afraid Close() doesn't do what you expect.
The neat method is to do the checks that you wanted to do in the constructor in the Form_Load. Add an event handler for form-load and do your checks in the event handler. Use the property DialogResult to indicate that you decided not to show the form.
private void FormMain_Load (object sender, EventArgs e)
{
if (FormShouldNotLoad())
{
this.DialogResult = System.Windows.Forms.DialogResult.Abort;
Close();
// Warning, this does not work, see below, (but we're almost there!)
}
}
The user of the code could check the result of the dialog:
// in the parent form:
public void ShowMyForm()
{
MyForm form = new MyForm();
form.propertyA = ...;
from.propertyB = ...;
DialogResult dlgResult = form.ShowDialog(this);
switch (dlgResult)
{
case System.Windows.Forms.DialogResult.Abort:
ProcessFormNotLoaded();
break;
case System.Windows.Forms.DialogResult.OK:
ProcessFormOk();
break;
// etc.
}
}
However, calling Close() in the event handler for form-load won't work, because Close() can only be called properly after Load is completed.
Therefore, instead of calling Close(), you should BeginInvoke the Close() function, so the Close function will be called after loading is done:
private void FormMain_Load (object sender, EventArgs e)
{
if (FormShouldNotLoad())
{
this.DialogResult = System.Windows.Forms.DialogResult.Abort;
// invoke the Close function after Load completed
this.BeginInvoke(new MethodInvoker( () => this.CancelLoading())
}
}
Environment.Exit(...) is working for me (without window flickering):
public partial class MyForm : Form
{
public MyForm()
{
if (weShouldClose)
{
Environment.Exit(0);
}
}
}
If you want your window to never be seen(no flickering windows that open for an instant and then disappear):
public new void Show()
{
if (MyFunc())
base.Show();
else
; // dispose or whatever
}
Though Show(...) has 2 overloads and ShowDialog(...) has 2 too.
Doesn't work for the main form that is opened via Application.Run(). But who would do that anyways? Except there is also a way to open main form without using Application.Run().