I have two classes:
mainGUI and preferencesGUI
I want to call a method in the mainGUI class called updateGUI, every time the applychanges button is pressed. When I try to do it, though, nothing happens.
Here are my classes:
updateGUI:
public void updateGUI()
{
String[] colors = (Regex.Split(File.ReadAllText(prefpath), ","));
int[] colori = new int[colors.Length];
for (int x = 0; x < colors.Length; x++)
{
colori[x] = Convert.ToInt32(colors[x].ToString());
}
preferencesGUI pg = new preferencesGUI();
pg.R1 = colori[0];
pg.G1 = colori[1];
pg.B1 = colori[2];
pg.R2 = colori[3];
pg.G2 = colori[4];
pg.B2 = colori[5];
outputbox.ForeColor = Color.FromArgb(colori[0], colori[1], colori[2]);
outputbox.BackColor = Color.FromArgb(colori[3], colori[4], colori[5]);
eventlist.ForeColor = Color.FromArgb(colori[0], colori[1], colori[2]);
eventlist.BackColor = Color.FromArgb(colori[3], colori[4], colori[5]);
}
Button click event handler on preferencesGUI:
private void applychanges_Click(object sender, EventArgs e)
{
mainGUI mg = new mainGUI();
mg.updateGUI();
}
Thanks for any help.
First, I'm assuming that mainGUI is already open when preferencesGUI is making this call. So when you do this:
private void applychanges_Click(object sender, EventArgs e)
{
mainGUI mg = new mainGUI();
mg.updateGUI();
}
All you're doing is creating a new instance of mainGUI and then letting it fall out of scope so yes, it does nothing.
This is a pretty common hurdle in GUI development. How do you get the different components to be aware of each other. One approach that people try initially is the singleton. However this can cause issues if you want multiple instances of the component and really it's just kind of messy. A better idea is to pass in the instance of the control but this design isn't very modular since it requires that the preferencesGUI class be aware of the mainGUI class.
Instead you should create an event inside your preferencesGUI like so:
class preferencesGUI
{
// ... Some code ...
public event Action RequestGUIUpdate;
protected void OnRequestGUIUpdate() { if (this.RequestGUIUpdate == null) return; RequestGUIUpdate(); }
// ... Some more code ...
private void applychanges_Click(object sender, EventArgs e)
{
OnRequestGUIUpdate();
}
// ... Some more code ...
}
So now the preferencesGUI is capable of notifying when a GUI update is necessary. So when you create the preferencesGUI instance from mainGUI attach an event handler like so:
preferencesGUI preferences = new preferencesGUI();
preferences.RequestGUIUpdate += new Action(updateGUI);
Related
Sorry for the title, i didn't find it easy to resume.
My issue is that I need to implement a c# dll that implements a 'scan' method, but this scan, when invoked, must not block the main thread of the application using the dll. Moreover, it is a duty that after the scan resolves it rises an Event.
So my issue (in the deep) is that i'm not so experienced at c#, and after very hard investigation i've come up with some solutions but i'm not very sure if they are the "right" procedures.
In the dll i've come up with:
public class Reader
{
public delegate void ReaderEventHandler(Object sender, AlertEventArgs e);
public void Scan(String ReaderName)
{
AlertEventArgs alertEventArgs = new AlertEventArgs();
alertEventArgs.uuiData = null;
//Code with blocking scan function here
if (ScanFinnished)
{
alertEventArgs.uuiData = "Scan Finnished!";
}
alertEventArgs.cardStateData = readerState[0].eventState;
ReaderEvent(new object(), alertEventArgs);
}
public event ReaderEventHandler ReaderEvent;
}
public class AlertEventArgs : EventArgs
{
#region AlertEventArgs Properties
private string _uui = null;
private uint cardState = 0;
#endregion
#region Get/Set Properties
public string uuiData
{
get { return _uui; }
set { _uui = value; }
}
public uint cardStateData
{
get { return cardState; }
set { cardState = value; }
}
#endregion
}
While in the main app I do:
Reader reader;
Task polling;
String SelectedReader = "Some_Reader";
private void bButton_Click(object sender, EventArgs e)
{
reader = new Reader();
reader.ReaderEvent += new Reader.ReaderEventHandler(reader_EventChanged);
polling = Task.Factory.StartNew(() => reader.Scan(SelectedReader));
}
void reader_EventChanged(object sender, AlertEventArgs e)
{
MessageBox.Show(e.uuiData + " Estado: " + e.cardStateData.ToString("X"));
reader.Dispose();
}
So here, it works fine but i don't know if it's the proper way, in addition i'm not able to handle possible Exceptions generated in the dll.
Also tried to use async/await but found it difficult and as I understand it's just a simpler workaround Tasks.
What are the inconvinients of this solution? how can i capture Exceptions (are they in other threads and that's why i cant try/catch them)? Possible concept faults?
When your class sends events, the sender usually is that class, this. Having new object() as sender makes absolutely no sense. Even null would be better but... just use this.
You shouldn't directly raise events as it might result in race conditions. Might not happen easily in your case but it's just a good guideline to follow. So instead of calling ReaderEvent(new object(), alertEventArgs); call RaiseReaderEvent(alertEventArgs); and create method for it.
For example:
private void RaiseReaderEvent(AlertEventArgs args)
{
var myEvent = ReaderEvent; // This prevents race conditions
if (myEvent != null) // remember to check that someone actually subscribes your event
myEvent(this, args); // Sender should be *this*, not some "new object()".
}
Though I personally like a bit more generic approach:
private void Raise<T>(EventHandler<T> oEvent, T args) where T : EventArgs
{
var eventInstance = oEvent;
if (eventInstance != null)
eventInstance(this, args);
}
Which can then be used to raise all events in same class like this:
Raise(ReaderEvent, alertEventArgs);
Since your scan should be non-blocking, you could use tasks, async/await or threads for example. You have chosen Tasks which is perfectly fine.
In every case you must understand that when you are not blocking your application, your application's main thread continues going like a train. Once you jump out of that train, you can't return. You probably should declare a new event "ErrorEvent" that is raised if your scan-procedure catches an exception. Your main application can then subscribe to that event as well, but you still must realize that those events are not (necessarily) coming from the main thread. When not, you won't be able to interact with your GUI directly (I'm assuming you have one due to button click handler). If you are using WinForms, you'll have to invoke all GUI changes when required.
So your UI-thread safe event handler should be something like this:
void reader_EventChanged(object sender, AlertEventArgs e)
{
if (InvokeRequired) // This true for others than UI Thread.
{
Invoke((MethodInvoker)delegate
{
Text = "My new title!";
});
}
else
Text = "My new title!";
}
In WPF there's Dispather that handles similar invoking.
I am writing a tool which switchs between a lot of states. For some events I need to be sure they wont get executed a second time while the called function (inside the event) is running. This is how I managed it before:
// Global variables //
public bool func1IsRunning = false;
public bool func2IsRunning = false;
...
public void listView_DoubleClick(object sender, EventArgs e)
{
if(!func1IsRunning)
{
func1();
func1IsRunning = false;
}
}
public void func1()
{
func1IsRunning = true;
// some code in here //
}
But with every extension of my tool the list of the global variables grows up. Also the events and functions getting less clear to read.
Isnt there a way like this(?):
public void listView_DoubleClick(object sender, EventArgs e)
{
if(DoubleClick.IsHandled)
{
func1();
}
}
public void func1()
{
// some code in here //
// ................. //
DoubleClick.IsHandled = true; // at the end of the function //
}
So what I am looking for is a way to determine if an event is still running or not. My code is working, im just unhappy with how it looks like.
Any ideas?
UPDATE 1
I decided to use Steve's answer as it solves my problem by the clearest way.
Anyway it is NOT running correctly for now.
Here is how my code looks like:
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= new EventHandler(listView_DoubleClick);
itemEdit();
}
finally
{
listView.DoubleClick += new EventHandler(listView_DoubleClick);
}
}
The code above is NOT disabling the handler.
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= listView_DoubleClick;
itemEdit();
}
finally
{
listView.DoubleClick += listView_DoubleClick;
}
}
This code is also not disabling the handler.
This is the line where the handler gets enabled (MainForm.Designer.cs):
this.listView.DoubleClick += new System.EventHandler(this.listView_DoubleClick);
There are no errors raised. The event just gets fired again and again. Where is the problem?
UPDATE 2:
As Sinatr asked in the comments below if my function is really waiting or just enabling user input he discovered where the mistake was made.
Steve's answer is correct according to my wrong written question. Thanks a lot to all of you guys.
Just disable the event handler
public void listView_DoubleClick(object sender, EventArgs e)
{
try
{
listView.DoubleClick -= listView_DoubleClick;
// Now, even if func1 causes a DoubleClick event,
// or user manages to trigger a DobuleClick
// there is no event registered and this code could
// reentered until you exit from func1.
func1();
}
finally
{
// Important part. the finally block is required
// because you should readd the event handler
// ALSO in case an exception occurs in func1
// and it is not handled there
listView.DoubleClick += listView_DoubleClick;
}
}
EDIT
Looking at your comment I suspect that this DoubleClick event is assigned to more than one control. If this is the case, using the global listView global instance of a listview doesn't disable the double click on other controls that are linked to the same code.
If this is the case then you need a more generic approach
public void listView_DoubleClick(object sender, EventArgs e)
{
Control c = sender as Control;
try
{
if(c != null)
{
c.DoubleClick -= listView_DoubleClick;
// Now, even if func1 causes a DoubleClick event,
// or user manages to trigger a DobuleClick
// there is no event registered and this code could
// reentered until you exit from func1.
func1();
}
}
finally
{
// Important part. the finally block is required
// because you should readd the event handler
// ALSO in case an exception occurs in func1
// and it is not handled there
if(c != null) c.DoubleClick += listView_DoubleClick;
}
}
Of course, this is just to enable/disable DoubleClicks events, it cannot works if you assign this event handler to other standard events like Click that have the same signature (object sender, EventArgs e)
How about something like the following using locks:
private object globalLock = new object();
private Dictionary<int, object> lockObjects = new Dictionary<int, object>();
public void listView_DoubleClick(object sender, EventArgs e)
{
object lockObject;
lock (globalLock) // to avoid two threads creating the object
{
if (!lockObjects.ContainsKey(1))
lockObjects.Add(1, new object());
lockObject = lockObjects[1];
}
if (Monitor.TryEnter(lockObject) // enter only if no thread has already entered
{
try { func1(); }
finally { Monitor.Exit(lockObject); }
}
}
This is different to Steve's logic in the matter that it is thread-safe.
A simple state-machine should solve your problem without requiring too many variables. Create an Enum named AppState like this:
enum AppState
{
Ready = 1,
InsideListView1Click = 2,
InsideListView1DoubleClick = 3
InsideListView2Click = 4,
InsideListView2DoubleClick = 5
}
This enum could grow as you add new controls and/or event-handlers to your application. Now use a single global variable that keeps track of the application state and modify it inside event-handlers appropriately:
private AppState m_State = AppState.Ready;
And in the event-handlers you would do:
private void ListView1_DoubleClick(object sender, EventArgs e)
{
lock
{
if(m_State != AppState.Ready)
return;
else
m_State = AppState.InsideListView1DoubleClick;
}
//Do your stuff
m_State = AppState.Ready;
}
This way newer calls will be ignored instead of being queued. If you expect to be in multiple states at the same time, you could apply [Flags] attribute on this enum as well. Also note that enums are thread-safe and evaluating them is atomic, so multi-threading shouldn't be a problem either.
I'm trying to generate a Thread for the redrawing-function of my existing poly-drawing. I read here it is possible that UI can be realized in Threads see here LINK but I cant use it on my redrawSingelPoly() function.... Any ideas how I can use redrawSingelPoly() as an thread ?
In my MainWindow.xaml.cs:
Is called when the user press a button on my main window:
private void SingleSelectedMeasurement(object sender, RoutedEventArgs e)
{
System.Windows.Controls.Button button = (System.Windows.Controls.Button)sender;
RedrawSingelMeasurement(Convert.ToInt16(button.Tag));
}
private void RedrawSingelMeasurement(int selectedMeasurement)
{
selectedMeasurement = selectedMeasurement - 1;
for (int i = 0; i < measurements.Length; i++)
{
if (selectedMeasurement != i)
{
measurements[i].draw = false; //block drawing
}
else
{
measurements[i].draw = true; // remove block for drawing
}
}
measurements[selectedMeasurement].redrawSingelPoly();
}
In my Measurement.cs:
public void redrawSingelPoly()
{
Polyline poly = new Polyline();
poly.Stroke = colorBrush;
poly.StrokeThickness = basicLineThick;
//first clean all
coordinateSystem.Children.Clear();
poly.Points = points;
//draw
coordinateSystem.Children.Add(poly);
}
You cannot access to DependencyProperties of DependencyObject (in your case: coordinateSystem) from the thread different to the one it's related to.
If you want to speed up your application, you should create custom control, override its OnRender method and draw your custom graphics there: it will remove a lot of logical and visual tree logic and will work a lot faster.
Ideally one window only can run on one Dispatcher, however you can put different visuals in different threads by HostVisual but in very limited scenarios. Maybe this article can help you:
http://blogs.msdn.com/b/dwayneneed/archive/2007/04/26/multithreaded-ui-hostvisual.aspx
What the article you mention talks about is actually just having one thread for each window. Drawing dedicated elements in a different thread is not possible.
private void SingleSelectedMeasurement(object sender, RoutedEventArgs e)
{
var button = (System.Windows.Controls.Button)sender;
Task.Factory.StartNew (
() => OnUi(RedrawSingelMeasurement(Convert.ToInt16(button.Tag))));
}
//here's a sample on how to get despatcher for the ui thread
private void OnUi (Action action)
{
if (_dispatchService == null)
_dispatchService = ServiceLocator.Current.GetInstance<IDispatchService>();
//or _dispatchService = Application.Current.Dispatcher - whatever is suitable
if (_dispatchService.CheckAccess())
action.Invoke ();
else
_dispatchService.Invoke(action);
}
This is a fictional example but I was wandering what happens if the InitialiseTimer function gets called twice. Does the timer elapsed function get triggered twice. Will this change if the functions are made static?
private static void InitialiseTimer()
{
TheTimer = new System.Timers.Timer();
TheTimer.Interval = 400;
TheTimer.Elapsed += new ElapsedEventHandler(TheTimer_Elapsed);
TheTimer.AutoReset = false;
}
public void TheTimer_Elapsed(object sender, ElapsedEventArgs e)
{
//Do stuff in here
}
I was going to use below to prevent this
Has an event handler already been added?
Thanks,
Richard
If you register the event handler twice, it will be invoked twice every time the event is raised.
This won't change if you make TheTimer_Elapsed static, because you'll still hold two references to this static method.
In most cases there's no need to write compicated things like what Blair Conrad posted in the question you linked to. Just don't forget to use -= every time you have += and you'll be safe.
I think the following demonstrates the scenario and does indeed fire twice, also propose a simple change (commented code) to the Init method that should fix the behavior. (Not thread safe btw, additional locks would be required)
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var counter = 0;
var ts = new ThreadStart(() =>
{
Foo.Fired += (o, e) =>
{
counter++;
};
Foo.InitialiseTimer();
Foo.InitialiseTimer();
});
var t = new Thread(ts);
t.Start();
Thread.Sleep(30);
Assert.AreEqual(1, counter);
}
}
public class Foo
{
private static System.Timers.Timer TheTimer = null;
public static event EventHandler Fired;
public static void InitialiseTimer()
{
//if (TheTimer != null)
//{
// TheTimer.Stop();
// TheTimer = null;
//}
TheTimer = new System.Timers.Timer();
TheTimer.Interval = 10;
TheTimer.Elapsed += new ElapsedEventHandler(TheTimer_Elapsed);
TheTimer.AutoReset = false;
TheTimer.Start();
}
public static void TheTimer_Elapsed(object sender, ElapsedEventArgs e)
{
//Do stuff in here
if (Fired != null)
{
Fired(null, null);
}
}
}
if you call the method InitialiseTimer twice you will create two Timers each of them will have only one event handler attached but they might elapse both. It's not really about having the method static or not, it's more about the method itself, you could check if TheTimer is null and do the rest only if it's null so you assign it only once.
If event is registered twice you will have two executions.
You can check if event is null, and the problem will be solved.
Static or not, you are recreating the Timer. So you can invoke the InitialiseTimer many, many times without adding more than a single handler. You will end up with many timers though...
Wierd behaviour when passing values to and from second form.
ParameterForm pf = new ParameterForm(testString);
works
ParameterForm pf = new ParameterForm();
pf.testString="test";
doesn't (testString defined as public string)
maybe i'm missing something? Anyway I'd like to make 2nd variant work properly, as for now - it returns null object reference error.
Thanks for help.
Posting more code here:
calling
Button ParametersButton = new Button();
ParametersButton.Click += delegate
{
ParameterForm pf = new ParameterForm(doc.GetElementById(ParametersButton.Tag.ToString()));
pf.ShowDialog(this);
pf.test = "test";
pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit);
};
definition and use
public partial class ParameterForm : Form
{
public string test;
public XmlElement node;
public delegate void ParameterSubmitResult(object sender, XmlElement e);
public event ParameterSubmitResult Submit;
public void SubmitButton_Click(object sender, EventArgs e)
{
Submit(this,this.node);
Debug.WriteLine(test);
}
}
result:
Submit - null object reference
test - null object reference
pf.ShowDialog(this); is a blocking call, so pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit); is never reached: switch the order.
Submit(this,this.node); throws a null object reference because no event is assigned to it (see above). Generally, you should always check first: if (Submit != null) Submit(this,this.node);
You should change ``pf.ShowDialog(this);topf.Show(this);` so that your main form isn't disabled while your dialog box is open, if that's what you want, or use the model below (typical for dialog boxes.)
I'm not sure what pf_Submit is supposed to do, so this might not be the best way to go about it in your application, but it's how general "Proceed? Yes/No" questions work.
Button ParametersButton = new Button();
ParametersButton.Click += delegate
{
ParameterForm pf = new ParameterForm(testString);
pf.ShowDialog(this); // Blocks until user submits
// Do whatever pf_Submit did here.
};
public partial class ParameterForm : Form
{
public string test; // Generally, encapsulate these
public XmlElement node; // in properties
public void SubmitButton_Click(object sender, EventArgs e)
{
Debug.WriteLine(test);
this.Close(); // Returns from ShowDialog()
}
}
When you want to use your second variant, you have to use a getString()-Method, where you can put the e.g. "testString". The way you wrote it, "testString" should be a method (and got brackets).
EDIT (a bit more precise):
You could write:
pf.getString(testString);
, if "pf" is an instance of your own class, otherwise you had to look up, whether you can retrieve a String in this class.
the thing was in line order :)
pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit);
and
pf.Test = "test";
should have been set before
pf.ShowDialog(this);
my mistake thingking that parameter can be passed after 2nd form was displayed
thnx for answers