How to implement a logging window in C#? - c#

So using windows form builder, I have created a new form with textbox in it, calling this form as LogForm.cs, this form/class has a method called log(string text).
In my main form class (Form1.cs), I have created an instance of that form.
LogForm logForm = new LogForm();
logForm.log("Logger has started...");
and it show fine on the LogForm textbox. But when I call logForm.log("Some logging info...") On my code inside a thread, it somehow makes my application crash.
How do I deal with this? Please help me demostrate a small code.I am fairly new to C# and programming as a whole so I hope you consider.

Use/call this function in LogForm.log (btw methods in C# are usually capitalized).
private void SetText(string text)
{
Action set = () => yourTextBox.Text = text;
if (yourTextBox.InvokeRequired)
{
yourTextBox.Invoke(set);
}
else
{
set.Invoke();
}
}
If it cannot be set from the current thread yourTextBox.InvokeRequired will be true and the function will work it out. Otherwise it just sets it directly.
Inspiration from this answer at possible duplicate.
Since you are saying the problem persists I'll show a bit more code and try to expain it further.
First of all, I edited the SetText method. I added the private modifier since this function is not indended to be called anywhere outside of LogForm. I also added the curly brackets since that's my preferred style and it also makes sure that the if-statement behaves as expected.
public void Log(string message) {
SetText(message);
//do stuff
}
Both of these methods (Log and SetText) are placed inside the LogForm class. You can now call logForm.Log("Logger has started..."); from any thread as long as your form (containing the textbox) is already initialized. This usually happens in the constructor by calling InitializeComponent(); on the first line.
Without knowing more about your code this is probably as far as I can help you.

Related

C# form is blocked after creation

I am creating a Form when a certain event occurs. I put this created Form into a static member of the class where it is created. I debugged the code and everything works fine but the Form stays blocked and the user can't do anything in this window. It just appears with a loading animation (see picture). So nothing in the opened window is clickable, you can't even close it.
class CallManagementObserver : CallObserver
{
private static FrmIncomingCall frmCurrentCall;
public CallManagementObserver()
{
}
public void callChangedEvent(CallEv[] events)
{
foreach (CallEv currentEvent in events)
{
switch (currentEvent.getID())
{
case TermConnRingingEv.ID:
// Incoming call
frmCurrentCall = new FrmIncomingCall(currentEvent);
frmCurrentCall.Show();
frmCurrentCall.Update();
break;
case CiscoCallInfoChangedEv.ID:
// User accepted external call on terminal
frmCurrentCall.Close();
break;
case TermConnActiveEv.ID:
// User is in call
frmCurrentCall.Close();
break;
case ConnDisconnectedEv.ID:
// Caller has hung up
frmCurrentCall.Close();
break;
default:
break;
}
}
}
}
}
As you can see above I wrote my own Form class whose code is here:
public partial class FrmIncomingCall : Form
{
Call incomingCall;
CallEv currentEvent;
public FrmIncomingCall(CallEv currentEvent)
{
InitializeComponent();
this.currentEvent = currentEvent;
this.incomingCall = currentEvent.getCall();
}
private void initGui()
{
Connection[] callConnections = incomingCall.getConnections();
Address caller = callConnections[1].getAddress();
lblIncomingCallSource.Text = caller.getName();
}
private void btnAcceptCall_Click(object sender, System.EventArgs e)
{
TermConnEv termConnEv = (TermConnEv)currentEvent;
TerminalConnection termConn = termConnEv.getTerminalConnection();
termConn.answer();
}
private void frmIncomingCall_Load(object sender, System.EventArgs e)
{
initGui();
}
}
When I show the Form via ShowDialog() it is usable but the program stops (since this is what dialogs are made for I guess).
Any ideas what I'm doing wrong? Nothing freezes, the program is running correctly.
Well, your application is poorly designed... It seems that you have no idea of what multithreading is and why you should use it.
If the application hangs forever, then either there is a deadlock (something like the dialog wait on the calling system and the calling system wait on the dialog).
As I have no idea what CallEv is and how it is intended to be used.
Well, if the calling system works and the UI is never updated, then obviously, you never let the UI have time to be updated because your UI thread is 100% of the time using the calling system or waiting on it.
That means that the calling system should probably be used from another thread and that you should have some communication between both threads...
It might also be possible that the calling system might be used in many different ways (as it would be the case for serial port and TCP communication) where one could use what fit most with his application.
Another problem with your code is that when you close a dialog, as far as I know it cannot be used anymore without recreating the dialog as the dialog would be disposed... So you would need to set the formCurrentCall to null and update any affected code. Alternatively, you might hide the form instead and show it again when required.
In any case, it is hard to help you because we don't have any idea of what is CallEv and other classes or events in your code. Also, we have no idea which code is executing when the UI is not responding (or updated). So the question do not have enough informations. In fact, such problem are way easier to debug using a debugger as it is far easier to see what code is run and which line of code take time to execute or even to see which code is not executed.

Trying to close a form after the next one is shown in C# cf

I've been studying Android lately and I tried to port one of its functions to C# compact framework.
What I did is create an Abstract class that I call Activity.
This class looks like this
internal abstract class Activity
{
protected Form myForm;
private static Activity myCurrentActivity = null;
private static Activity myNextActivity = null;
internal static void LoadNext(Activity nextActivity)
{
myNextActivity = nextActivity;
if (myNextActivity != null)
{
myNextActivity.Show();
if (myCurrentActivity != null)
{
myCurrentActivity.Close();
myCurrentActivity = null;
}
myCurrentActivity = myNextActivity;
myNextActivity = null;
}
}
internal void Show()
{
//PROBLEM IS HERE
Application.Run(myForm);
//myForm.Show();
//myForm.ShowDialog();
//
}
internal void Close()
{
myForm.Close();
}
internal void GenerateForm()
{
///Code that uses the Layout class to create a form, and then stores it in myForm
//then attaches click handlers on all the clickable controls in the form
//it is besides the point in this problem
}
protected abstract void Click(Control control);
//this receives all the click events from all the controls in the form
//it is besides the point in this problem
}
The problem I have is with running the part of the Show() command
Basically all my classes implement the above class, load an xml file and display it.
When I want to transition to a new class/form (for example going from ACMain to ACLogIn)
I use this code
Activity.LoadNext(new ACLogIn);
Which is supposed to load the next form, show it , and unload the current form
I have tried these solutions (in the Show() method) and here is the problem with each one
using myForm.ShowDialog()
This works, but blocks execution, which means that the old form does not close, and the more I move between the forms the more the process stack increases
using myForm.Show()
This works, closes the old form after the old one is shown, but immediately after that closes the program and terminates it
using Application.Run(myForm)
This works only on the first form loaded, when I move to the next form, it shows it then throws an exception saying "Value does not fall within the expected range"
Can someone help me fix this or find an alternative?
If you're really after creating your own framework for this navigation, you need to re-work you thinking. The Form instance passed into Application.Run must never close - when it does, Application.Run finishes execution and (typically) your static void Main entry point exits and the app terminates.
What I would propose is that you change your Activity to either being a UserControl:
public abstract class Activity : UserControl
{
....
}
or Composing one
public abstract class Activity
{
private UserControl m_control;
....
}
Then instead of closing and showing Forms, parent all of the Activities inside the main Form as a container.
As fair warning, this is going to get complex when you start wanting to show things in a Tab motif instead of a Stack, or having split views. Frameworks seem simple to create, but they're not so I'd at least consider using something already done unless you have compelling reasons to want to roll your own.
Application.Run is generally used with the overload that takes a Form parameter. This would be the "main" form that would be responsible for starting/showing other forms. This "main" form could be "hidden". But, I think that's a little awkward.
Alternatively, you don't need a main form, you can use Application.Run() to start a message pump to process Windows messages; but, then the thread is busy processing messages and cannot show dialogs (they must be shown in the thread that is running Application.Run). You can get around this by creating one or more form objects before calling Application.Run and these form objects could create a Timer object that would call Form.Show() or Form.ShowDialog() on the Timer.Tick event handler so that for form is shown after the call to Run. I think this is a little awkward as well.
Both of these solutions kind of circumvent the way you're expected to use Windows and WinForms; so, I think you need to think about re-designing this application to work with the way that Windows and .NET works.

C# Updating TextBox From Another Class

All I am trying to do is update a textBox (in this case txtInit) from another class. I have been reading a lot about how a UI Thread has to change itself, and something about using a dispatcher. I found an answer on here that seemed close, but I couldnt get it to work for me... it said to try using the line:
MainForm.Dispatcher.Invoke(new Action(delegate() {MainForm.myInstance.txtInit.Text = "Text"};);
In my ServerSide class, I need to send a String to the txtInit textbox on my MainForm.. and that is all I need to do.. thanks for any help.
Classes have nothing to do with threads(which is your problem right now).
Each Control has an Invoke method which will do the right thread synchronization for you.
So you can do
MainForm.myInstance.txtInit.Invoke((sender, args) => (sender as TextBox).Text = "text");
To improve performance you can test(which basically tells you if you're in the same thread) the Control.IsInvokeRequired property.
Another way to do it is by using the SynchronizationContext of the UI thread which you need to capture in the constructor of the form from SynchronizationContext.Current and then do
syncContext.Send((obj) => MainForm.myInstance.txtInit.Text = "Text", null);
I would probably just create a public method on the MainForm that you can pass a string to and let that method set the text for the text box. You can also control whether or not you need to us the Invoke call (different threads) so you never have to worry about coding this in other areas - just call the method and pass the string.
Here is an example:
public partial class Form1 : Form
{
public delegate void UpdateText(string text);
public Form1()
{
InitializeComponent();
}
public void SetTextBoxText(string text)
{
// Check to see if invoke required - (from another thread)
if(textBox1.InvokeRequired)
{
textBox1.Invoke(new UpdateText(this.SetTextBoxText),
new object[]{text});
}
else
{
textBox1.Text = text;
}
}
}
If I understand correctly, it seems you want to access the Windows form elements from another thread or from some asynchronous events. In such case following links may help you.
http://msdn.microsoft.com/en-us/library/ms171728.aspx
Update UI from multiple worker threads (.NET)
Controlling form elements from a different thread in Windows Mobile

C# windows forms custom controls cross-thread operation

I have one main windows form and within that form I have custom controls that represents different screens in application. I want to access this control's child controls. There's something I'm not getting here...sometimes I get this error:
Cross-thread operation not valid:
Control 'lblText' accessed from a thread
other than the thread it was created on.
but sometimes everything works OK. I don't completelly understand why the error...probably something with external device (MEI BillAcceptor) which has an event (inside Form1 class) that does the changes to the control... so let me write a simple code...
//user control
public partial class Screen2 : UserControl
{
public void changeValue(string txt)
{
lblText.Text = txt;
}
}
and the method changeValue is called from a form1 when particular event is rised...
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
BillAcceptor.SomeBillAcceptorEvent +=
new SomeBillAcceptorEventHandler(changeText);
}
private void changeText(object sender, EventArgs args)
{
_screen2.changeValue("some text");
}
}
So the most annoying thing is that sometimes everything actually works... So my question is "do I have to use Invoke here?" or how do I solve this with less changes to the application...
In your handler. do something like this.
if (this.InvokeRequired)
{
Invoke(new MethodInvoker(() =>
{
_screen2.changeValue("some text");
}));
}
else
{
_screen2.changeValue("some text");
}
I would guess that the event is being raised on a seperate thread other that the main UI thread.
Yes you need to use Invoke if there is a possibility of that method being called from a different thread.
You can check this.InvokeRequired(), if true, then use invoke, if false do a normal call.
This occurs due to thread unsafe call
You should make only thread safe calls in program
Check this link.
The short answer is yes, you must use Invoke. See this question and its accepted answer if you need details.
The reason the exception is only thrown some of the time, by the way, comes down to timing. You currently have a race condition in which sometimes you get lucky and sometimes you don't.
By the way, here is pretty handy pattern for this sort of thing.
Refactor any code that sets form values into its own private void method(s).
In this new method, call InvokeRequired. If it returns true, call Invoke, passing the current method so as to recurse back into it. If it returns false, go ahead and make the change.
Call this new method from the event handler.
For example:
private void ChangeScreen2() {
if (this.InvokeRequired) {
this.Invoke(new MethodInvoker(ChangeScreen2));
}
else {
_screen2.changeValue("some text");
}
}
private void changeText(object sender, EventArgs args)
{
ChangeScreen2();
}
The idea being that you sequester all code that modifies the form into these methods that always begin with a check of InvokeRequired and always Invoke themselves if so required. This pattern works with .NET 1.0 onward. For even neater approach, see the accepted answer to this question, which works with .NET 3.0 and later.

C# Application.Run without Form

Is it possible to call Application.Run, but to not pass a form parameter, or is there an alternative if there’s no form to call?
The Run method doesn’t seem to have any overloads that don’t accept a form.
For example, if I wanted to instantiate a class first, and then have that call the form, is there way to do the equivalent of:
Application.Run(myClass);
Just to clarify, I do still want the functionality that .Run() provides. That is, establish a loop to keep the application running, but instead of tracking a form, track a class or other object.
This is relating to the compact framework initially. I assume that's why the Run method doesn't have the overload I was looking for.
The Run method doesn’t seem to have any overloads that don’t accept a form.
Uh... http://msdn.microsoft.com/en-us/library/ms157900.aspx
Application.Run Method
Begins running a standard application message loop on the current thread, without a form.
public static void Run()
I'm not clear whether you want to do:
You want to load your form somewhere else other than the Main()
Or Run a console or service application with no UI.
For (1):
static void main()
{
//Your program starts running here<<<
//Do some stuff...
FormRunner a = new FormRunner();
a.RunForm();
} // << And ends here
class FormRunner {
public void RunForm() {
Application.Run(new Form());
}
//You could call which ever form you want from here?
} // << And ends here
What you need to know is your program starts from the first line of the main and ends at the last. However, when you call Application.Run(FORM) it loads up a windows message loop for that form. Its a special loop that keeps the program still in the main and waits for events (they're called Windows Messages in win32 API)
And so the program does not end until the user clicks the close button. When this happens, thats when your program actually will return from its Main.
(2) So now if you just want a pure console app with no forms:
static void main()
{
AcceptInputs()
DrawScreen()
//Do something else.
//Make sure your flow stays within the main
} // << Once you come here you're done.
void AcceptInputs()
{
while(true) {
//Keep accepting input
break; // Call break when you're done. You'll be back in the main
}
}
I hope that helped.
You can use the overload of Application.Run that accepts an application context as its only parameter. An ApplicationContext is basically just a class that you can inherit from and add any functionality you like. See the example in the link for more information.
using System;
using System.Windows.Forms;
static class Program
[STAThread]
static void Main() {
Application.Run(new myClass());
}
internal class myClass : ApplicationContext {
public myClass() {
Application.Run(new myWindow());
}
}
}
The problem here, though, is that something will have to call this instance of myClass and tell it to exit or else the program will just keep running even after all forms are closed. And calling ExitThread() in the constructor is ignored.

Categories