Forgive me if this is a bit garbled, I'm a bit new on Windows Forms, having spent months in ASP.NET
Basically, I am using Quartz.NET in my Windows Form application - when a job is executed, it fires another class file - the parameters it passes in do not contain a reference to the form, and I don't think I can change this.
What I want to do is refresh a grid on the page after the job executes - and the only place that 'tells' me a job has been executed are in other files, rather than the forms code. I can't figure out a way of accessing methods/objects on the form without starting a new instance of it, which I don't want to do.
EDIT: To sum up, I just want a way to sent a message or something to the already open Main form from another class
Why not raise event from your class to winform. Thats the elegant way to do this. To do send message, you can use interop to call sendMessage which requires handle of the window
Actualy, if members of a class were not static, you wont be able to access them without an instance of that class. Try to accuire the same instance of the class that your actions are applied on it.
The easiest way is to pass the instance of the main form to the class consuming the Quartz.NET event, so that the consuming class can then call methods on the main form. I'm guessing that class would be created in the main form somewhere anyway, so it would be something like:
var quartzConsumer = new QuartzConsumer(this);
...
class QuartzConsumer {
MainForm _form;
public QuartzConsumer(MainForm form) {
_form = form;
...
}
void OnTimer(..) {
_form.UpdateGrid();
}
}
EDIT as #hundryMind says, another solution is for the main form to subscribe to an event on the consuming class:
class QuartzConsumer {
public delegate void DataChangedEventHandler();
public event DataChangedEventHandler DataChanged;
void OnTimer(..) {
if (this.DataChanged != null) this.DataChanged();
}
}
// in MainForm:
var quartzConsumer = new QuartzConsumer(..);
quartzConsumer.DataChanged += this.OnDataChanged;
...
void OnDataChanged() {
// update the grid
}
Related
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.
So I'm working with SdlDotNet - which basically converts SDL calls into what C# should look like and I ran into an issue.
That issue being that because the SdlDotNet is running in a different class to the main part of my application - I can't detect when it's closing.
The SdlDotNet library has an event that fires when it is told to close, and that event is:
SdlDotNet.Core.Events.Quit
In the object viewer - the event is shown as such:
public static event System.EventHandler<QuitEventArgs> Quit
Member of SdlDotNet.Core.Events
What I've done, is there is a main Windows form application that calls upon the SDL class like so:
private void drawToScreen()
{
//Starts the SDL off drawing to the screen
SDLDraw sdl = new SDLDraw();
sdl.startDrawing();
//How would I go about detecting SdlDotNet.Events.Quit
//From the class I've instanced
//When I was on my original Windows Forms implementation
//It worked like this:
////sdl.FormClosed += new FormClosedEventHandler(detectClose);
//But just copying that structure and trying
////sdl.Events.Quit += new QuitArgs(detectClose);
//Doesn't have the same effect, because sdl does not contain a definition for 'Events'
}
private void detectClose(object sender, QuitArgs e)
{
MessageBox.Show("SDL closed!")
}
So, I guess the question is how do I listen for Events.Quit firing in the class I called from the class I called it from?
Thanks in advance!
The declaration reveals that this is a static event, therefore it is associated with the class, not an instance. Use
SdlDotNet.Core.Events.Quit += new QuitArgs(detectClose);
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.
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
What is the best design decision for a 'top-level' class to attach to an event to a class that may be '5+ layers down in the callstack?
For example, perhaps the MainForm has spawned an object, and that object has spawned a callstack of several other object calls. The most obvious way would be to chain the event up the object hierarchy, but this seems messy and requires a lot of work.
One other solution ive seen is to use the observer pattern by creating a publically accessible static object which exposes the event, and acts as a proxy between the bottom-level object, and the top-level 'form'.
Any recommendations?
Here's a pseudo-code example. In this example, the MainForm instantiates 'SomeObject', and attaches to an event. 'SomeObject' attaches to an object it instantiates, in an effort to carry the event up to the MainForm listener.
class Mainform
{
public void OnLoad()
{
SomeObject someObject = new SomeObject();
someObject.OnSomeEvent += MyHandler;
someObject.DoStuff();
}
public void MyHandler()
{
}
}
class SomeObject
{
public void DoStuff()
{
SomeOtherObject otherObject = new SomeOtherObject();
otherObject.OnSomeEvent += MyHandler;
otherObject.DoStuff();
}
public void MyHandler()
{
if( OnSomeEvent != null )
OnSomeEvent();
}
public event Action OnSomeEvent;
}
If your application isn't based on Composite UI Application Blocks, the easiest solution is to put a "listener" class between Main form and your other components which both classes can easily access. Conceptually, the classes are laid out as follows:
---------- ----------------
| MainForm | | Some Component |
--------- ----------------
| |
Hooks onto Notifies
| |
\ /
-----------------
| Proxy Notifier |
-----------------
Here's some example code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
FakeMainForm form = new FakeMainForm();
form.CreateComponentAndListenForMessage();
Console.ReadKey(true);
}
}
class FakeMainForm
{
public FakeMainForm()
{
Listener.AddListener(MessageRecieved);
}
void MessageRecieved(string msg)
{
Console.WriteLine("FakeMainForm.MessageRecieved: {0}", msg);
}
public void CreateComponentAndListenForMessage()
{
ComponentClass component = new ComponentClass();
component.PretendToProcessData();
}
}
class Listener
{
private static event Action<string> Notify;
public static void AddListener(Action<string> handler)
{
Notify += handler;
}
public static void InvokeListener(string msg)
{
if (Notify != null) { Notify(msg); }
}
}
class ComponentClass
{
public void PretendToProcessData()
{
Listener.InvokeListener("ComponentClass.PretendToProcessData() was called");
}
}
}
This program outputs the following:
FakeMainForm.MessageRecieved: ComponentClass.PretendToProcessData() was called
This code allows you to invoke methods directly on any listener, no matter how far apart they are in the call stack.
Its easy to rewrite your Listener class so that its a little more generic and works on different types, but you should get the idea.
My initial intention would be to try and avoid that, so that an object's scope has obvious boundaries. In the particular case of Forms, I would attempt to have the child's parent form manage all required communications withs its ancestors. Can you be more specific about your case?
My first thought is that from your MainForm's perspective, it should have no idea what is going on 5 levels down. It should only know about its interactions with the object that it spawned.
With that, if you main form wants to perform some action asynchronously, it should be able to do that by calling a method on the spawned object asynchronously.
Now from your spawned object's point of view, if you allowed your caller to perform some method asynchronously, there's no need to push the event model further down... just call the methods directly down the stack. You're already on another thread.
Hopefully that helps a little. Just remember the levels of your app should only be aware of what goes on in the level immediately below them.
WPF uses routed events. These are static and can bubble up or tunnel down the element tree. I don't know if you are using WPF, but the idea of static events might help you out.
I wouldn't say this is a design fault, there are valid reasons for the main form to want to listen to what an object is doing. One scenario I've encountered is displaying status messages to the user to indicate what background processes are doing, or what multiple controls are doing in a multi-threaded app that lets you have multiple screens/"pages" open at once.
In the Composite UI Application Block, the basic equivalent of a dependency injection container wires up events when its instantiating objects in the same work item (a work item is just an object container for a group of related user controls). It does this by scanning for special attributes such as [EventPublication("StatusChanged")] on events and [EventSubscription("StatusChanged")] on public methods. One of my applications uses this functionality so that a user control instantiated way down in the innards of the application can broadcast status information (such as "Loading customer data...45%") without knowing that that data is going to end up in the main form's status bar.
So a UserControl can do something like this:
public void DoSomethingInTheBackground()
{
using (StatusNotification sn = new StatusNotification(this.WorkItem))
{
sn.Message("Loading customer data...", 33);
// Block while loading the customer data....
sn.Message("Loading order history...", 66);
// Block while loading the order history...
sn.Message("Done!", 100);
}
}
...where the StatusNotification class has an event with the a signature like
[EventPublication("StatusChanged")]
public event EventHandler<StatusEventArgs> StatusChanged;
... and the above Message() and Dispose() methods on that class invoke that event appropriately. But that class didn't explicitly have that event hooked up to anything. The object instantiator will have automatically hooked up the events to anybody with a subscription attribute of the same name.
So the MainForm has an event handler that looks something like this:
[EventSubscription("StatusChanged", ThreadOption=ThreadOption.UserInterface)]
public void OnStatusChanged(object sender, StatusEventArgs e)
{
this.statusLabel.Text = e.Text;
if (e.ProgressPercentage != -1)
{
this.progressBar.Visible = true;
this.progressBar.Value = e.ProgressPercentage;
}
}
... or some such. It's more complicated than that since it will rotate through multiple status notifications for a given number of seconds since multiple user controls can be broadcasting status messages around the same time.
So to recreate this behavior without actually switching over to CAB (which, to be honest, is much more complicated than I think it really needs to be), you could either have a MessageNotificationService object that you pass around your application or that you turn into a static/singleton object (I usually avoid this approach since it's harder to test), OR you could have you sub usercontrols be instantiated by a factory class that does the event wiring up for you. Objects could register with the factory by attributes of your own creation or by explicitly calling methods that say "hey, anytime you create an object with an event of this signature, I want to know about it."
Just be careful to have whatever class you implement unhook the events when an object gets disposed because it's stupid easy in this scenario to end up with something that won't get garbage collected.
Hope this helps!