I have this code in my Form1:
public partial class Form1 : Form
{
public static int hours;
public static int minutes;
public static int seconds;
FinishGate finishgate = new FinishGate();
public Form1()
{
InitializeComponent();
txtHours.MaxLength = 2;
txtMinutes.MaxLength = 2;
txtSeconds.MaxLength = 2;
lblFinished.Text = Convert.ToString(gate.Total);
}
private void btnFinish_Click(object sender, EventArgs e)
{
hours = Convert.ToInt32(txtHours.Text);
minutes = Convert.ToInt32(txtMinutes.Text);
seconds = Convert.ToInt32(txtSeconds.Text);
lblFinished.Text = Convert.ToString(gate.Total);
// Check if a runner has been selected
if (lstRunners.SelectedIndex > -1)
{
// Obtain selected runner
Runner selectedRunner = (Runner)lstRunners.SelectedItem;
// If runner hasn't finished
if (selectedRunner.HasFinished == false)
{
// Call the method in FinishGate class to process the runner
FinishGate.ProcessRunner(selectedRunner);
}
else
{
// Runner has finished / been processed so increase the total that have completed the climb by one
finishgate.Total++;
}
}
}
}
Here is the FinishGate.cs:
class Gate
{
private int total;
public int Total
{
get { return total; }
set { total = value; }
}
public static void ProcessRunner(Runner selectedRunner)
{
}
}
What I want to happen is that when a runner in the listbox is selected and the Process button is clicked, the boolean hasFinished in ProcessRunner is changed to true and the Total integer is increased by one, which then updates the lblFinished to also increase by one, but I can't get it to work.
My two main issues are: I'm not sure what the code would be in ProcessRunner() to say if hasFinished == false change it to true, else leave the boolean the way it is. The other issue is getting the lblFinished to update accordingly when the integer increments.
Any advice over where I'm going wrong and how to prevent this in the future would be great.
You could try this:
Put this line
lblFinished.Text = Convert.ToString(gate.Total);
at the end of the btnFinish_Click() event instead of the beginning. This way it will update after ProcessRunner() is run.
Also, add this to your ProcessRunner() event in your Gate class:
if (selectedRunner.hasFinished == false)
{
selectedRunner.hasFinished = true;
}
//You don't need to do anything if it isn't false.
I hope this is of use to you. Let me know if I need to be more specific.
Related
This question already has answers here:
Communicate between two windows forms in C#
(12 answers)
Closed 6 years ago.
I have a form with a label, also an external class. In my class, I have a for loop of 1 to 1000. How can I show the value of 1 to 1000 from my class to my form label?
//external class
public class TestClass
{
public void myLoop()
{
for (int i = 1; i <= 1000; i++)
{
// show value of i to label
}
}
}
Assuming you have a reference to your form as form1, and that form has a label named label1 that is public/accessible to TestClass:
public class TestClass
{
public void myLoop()
{
for (int i = 1; i <= 1000; i++)
{
// show value of i to label
form1.label1.Text = i.ToString();
// allow message pumping to redraw the label
Application.DoEvents();
// pause long enough to see it before the next one happens
System.Threading.Thread.Sleep(100);
}
}
}
I wouldn't recommend using Application.DoEvents for production code normally; but if you are running the UI thread and not using async code, this would be the "hacky" way to get all the window events pumping (mostly WM_PAINT to get the label to redraw itself) during your loop.
A better way is to use events:
public class TestClass {
public class ProgressEventArgs : EventArgs {
public int Value { get; set; }
}
public event EventHandler<ProgressEventArgs> Progress;
public void myLoop() {
for (int i = 0; i <= 1000; ++i) {
var evt = Progress;
if (evt != null) {
evt.Invoke(this, new ProgressEventArgs() { Value = i; });
}
}
}
}
and handle that event in the form:
public class TestForm : Form {
private somevent_click(object sender, EventArgs evt) {
var test = new TestClass();
test.Progress += test_Progress;
test.myLoop();
}
private void test_Progress(object sender, TestClass.ProgressEventArgs evt) {
label1.Text = evt.Value;
}
}
Note that these will happen in the same thread, so depending on what else you do in your loop, you may not get message pumping. Consider using a background thread or async code instead.
how can i update a second Window (It's a simple WPF Window with a Progressbar to show a progress) from within my MainWindow inside a loop?
Well, i am loading a couple of Files which can cost about 10 Seconds. While loading i want my ProgressWindow to show how many Files have been loaded.
Here is my MainThread:
private void loadFiles(String[] paths) {
// Show Progress Window
MyProgressWindow progWindow = new MyProgressWindow();
progWindow.ShowProgressWindow(0, paths.Length);
//Globals.MyProgressController.ShowProgressWindow(0, paths.Length);
foreach (String path in paths) {
// Load the file
loadFile(path);
// Refresh the Table
refreshTableAsync();
// Update Progress Window
progWindow.UpdateProgressWindow(1);
//Globals.MyProgressController.UpdateProgressWindow(1);
}
// Close Progress Window
progWindow.CloseProgressWindow();
//Globals.MyProgressController.CloseProgressWindow();
}
Here is the ProgressWindowController
public class MyProgressWindow {
private ProgressWindow progressWindow;
public void ShowProgressWindow(int startIndex, int maxIndex) {
progressWindow = new ProgressWindow();
progressWindow.Initialize(startIndex, maxIndex);
progressWindow.Show();
}
public void UpdateProgressWindow(int value) {
progressWindow.Update(value);
//progressWindow.Update();
}
public void CloseProgressWindow() {
progressWindow.Close();
}
}
And finally the ProgressWindow itself:
public partial class ProgressWindow : Window {
private int actualValue = 0;
private int maxValue = 0;
public ProgressWindow() {
InitializeComponent();
}
public void Initialize(int startValue, int maxValue) {
this.actualValue = startValue;
this.maxValue = maxValue;
}
public void Update(int value) {
actualValue += value;
lblPercentage.Content = actualValue + " / " + maxValue;
pbProgress.Value = ((double)value / maxValue) * 100;
}
}
I have read thati should do it via threading, but i cannot get it to work, and all code I've found which helped other people, doesn't do what i need.
Thanks for your help
Your question is posed incorrectly. You are updating the second window as per your question, however the suggested advice of using a separate thread is what you're really asking about.
I suggest reading about the BackgroundWorker class Here which even explains progress updates.
After reading online tutorials regarding events , I think I almost have an idea of whats going on. I developed the following extremely simple code to trigger an event in case a value is greater than 5.I know the code is pretty useless but I am using it to get my point across. (Instead of a main I just used a button event to trigger the code.)
//declare the delegate
public delegate void MyDelegate(string str);
public class SomeClass
{
public event MyDelegate MyEventFromDelegate;
private int i;
public int I
{
get
{ return i; }
set
{
if (value > 5)
{
MyEventFromDelegate("Value Greater than 5");
i = 0;
}
else
{
i = value;
}
}
}
}
public partial class Form1 : Form
{
public Form1()
{ InitializeComponent(); }
public void Method_To_Call(String rx)
{ MessageBox.Show("This method will be called if greater than 5");}
private void button1_Click(object sender, EventArgs e)
{
SomeClass a = new SomeClass();
a.MyEventFromDelegate +=new MyDelegate(Method_To_Call);
a.I = 12;
}
}
The only concern I have here is when we want to raise an event with the statement
MyEventFromDelegate("Value Greater than 5");
What point is passing a parameters to the event is at this point if later (at button click event) we are actually going to assign it a function to call every time an event is triggered.
In your very simple example - there is no point, because SomeClass instance "a" is very short-lived, and because you are not using rx parameter passed to Method_To_Call.
Your form method button1_Click is connected to the button's Click event through a delegate. Button does not know what code will execute when it is clicked. All it has to do is to signal that is has been clicked. That signal is implemented using a delegate.
Your could have defined your delegate as having an integer parameter where the checked value is passed. Then although the event method would be invoked only when value is greater than 5, inside the event method you could do things differently depending on the actual value.
//declare the delegate
public delegate void MyDelegate(int aValue);
public class SomeClass
{
public event MyDelegate MyEventFromDelegate;
private int i;
public int I
{
get
{ return i; }
set
{
if (value > 5)
{
MyEventFromDelegate(value);
i = 0;
}
else
{
i = value;
}
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void Method_To_Call(int aValue)
{
MessageBox.Show("This method signals that value is greater than 5. Value=" + aValue.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
SomeClass a = new SomeClass();
a.MyEventFromDelegate +=new MyDelegate(Method_To_Call);
a.I = 12;
}
}
I made a Windows Forms application that calculates the factorial of a number. All is fine, but now I have to do it using events. The concept of events is new to me and I've been trying to make it work for the past three days to no avail.
I have the form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
... some function declarations...
//public event EventHandler ProgressBarChanged;
public int OnProgressBarChanged()
{
progressBar1.Value++;
return progressBar1.Value;
}
public void button2_Click(object sender, EventArgs e)
{
initialize();
label3.Visible = false;
int wait_time = telltime();
int number = reading();
Facto mth;
if (checkBox1.Checked && checkBox2.Checked)
{
mth = new Facto(label3, wait_time, progressBar1);
}
else if(checkBox1.Checked==false && checkBox2.Checked)
{
mth = new Facto(label3,wait_time);
}
else if (checkBox1.Checked && checkBox2.Checked == false)
{
checkBox1.Checked = false;
mth = new Facto();
}
else
{
mth = new Facto();
}
mth.Subs += new Eventhandler(OnProgressBarChanged); // Error. I don't understand why
int result = mth.Factorial(number);
string display = result.ToString();
label3.Visible = true;
label3.Text = display;
}
And the Facto class:
public class Facto
{
public event EventHandler Subs;
System.Windows.Forms.Label label_for_output;
int wait_time;
System.Windows.Forms.ProgressBar bar;
public Facto()
{
}
public Facto(System.Windows.Forms.Label l, int time)
{
label_for_output = l;
wait_time = time;
}
public int Factorial(int number_to_calculate)
{
int Result;
if (Subs != null)
{
Subs(this, new EventArgs());
}
System.Threading.Thread.Sleep(wait_time);
if (number_to_calculate == 0)
{
return 1;
}
else
{
Result = (number_to_calculate * Factorial(number_to_calculate - 1));
if (label_for_output != null)
{
label_for_output.Visible = true;
label_for_output.Text = Result.ToString();
label_for_output.Update();
}
else
Console.WriteLine(Result);
}
System.Threading.Thread.Sleep(wait_time);
return Result;
}
}
}
The event should be triggered when the recursive function calls itself. When the event is triggered progressbar1.value from Form1 should be incremented with 1 (it should also decrement when it comes back from recursion, but I'm more interested in getting it to work first).
How can I fix this?
It's really confusing to me and I can only find examples which show messages or are very badly explained.
You have this error because your method signature are not equal:
.NET assumes that you have
public int OnProgressBarChanged() as
public void OnProgressBarChanged(object o, EventArgs e);
This is a standart signature of all .net events. First parameter - is object, that raised the event. Second parameter is event data.
You can create your custom class, inherited from EventErgs to pass data to event handler
For this kind of task I would suggest using a BackgroundWorker with a ProgressChanged event handler, so that the calculations and UI updates are carried out on separate threads. See the MSDN docs for a similar example with Fibonacci number calculation and a progress bar.
I have a WPF user control called TimerUserControl where contains a timer. And I have another user control where show questions, this one has a NextQuestion function.
The timer has 2 minutes like an interval, and I'd like to invoke the NextQuestion function when it has done. I think I have to use delegates, but I'm not sure.
UPDATE 1:
public partial class TimeUserControl : UserControl
{
public int _totalSeconds;
public int _secondsRemaining;
public DispatcherTimer timerSecondsLeft;
public TimeUserControl()
{
InitializeComponent();
timerSecondsLeft = new DispatcherTimer();
timerSecondsLeft.Tick += new EventHandler(timerSecondsLeft_Tick);
timerSecondsLeft.Interval = new TimeSpan(0, 0, 1);
}
public bool TimesUp
{
get;
set;
}
public void SetSeconds(int seconds)
{
timerSecondsLeft.Stop();
if (seconds == 0)
{
TimeTextBlock.Text = "There's no time! Hurray";
}
else
{
_totalSeconds = seconds;
_secondsRemaining = seconds;
TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _totalSeconds);
timerSecondsLeft.Start();
}
}
public void timerSecondsLeft_Tick(object sender, EventArgs e)
{
_secondsRemaining--;
if (_secondsRemaining <= 0)
{
timerSecondsLeft.Stop();
TimesUp = true;
TimeTextBlock.Text = "Time's up. Press Enter to next problem.";
// HERE WILL INVOKE NEXTQUESTION FUNCTION
}
else
{
TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _secondsRemaining);
}
}
}
Look in the code, the comment is this possible ussing delegates?
So you need to do a few things. You have to add some code to you're user control.
// Declare this outside your usercontrol class
public delegate void TimerExpiredEventHandler(object sender, EventArgs e);
This is what needs to be added to your code for the user control.
public partial class TimerUserControl : UserControl
{
public event TimerExpiredEventHandler Expired;
public void OnExpired(EventArgs e)
{
if (Expired != null)
Expired(this, e);
}
public void timerSecondsLeft_Tick(object sender, EventArgs e)
{
_secondsRemaining--;
if (_secondsRemaining <= 0)
{
timerSecondsLeft.Stop();
TimesUp = true;
TimeTextBlock.Text = "Time's up. Press Enter to next problem.";
// Fire the event here.
OnExpired(EventArgs.Empty);
}
else
{
TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _secondsRemaining);
}
}
}
Now you need to subscribe to this event inside whatever is calling this usercontrol in the first place.
public partial class ParentForm : Form
{
private void ParentForm_Load(object sender, EventArgs e)
{
var timer = new TimerUserControl();
//Subscribe to the expired event that we defined above.
timer.Expired += new EventArgs(Timer_Expired);
}
public void Timer_Expired(object sender, EventArgs e)
{
//Handle the timer expiring here. Sounds like you are calling another function, so do that here.
}
}
Use the TreeHelper to hunt up the tree for a shared Parent and then down the tree for the User Control you want. Something like this pseudo code:
this.Timer = new System.Windows.Threading.DispatcherTimer
{
Interval = new TimeSpan(0, 0, 1)
};
this.Timer.Tick += (s, e) =>
{
var _Control = s as MyFirstControl;
var _Other = LogicalTreeHelper.GetChildren(_Control.Parent)
.Cast<FrameworkElement>().Where(x => x.Name == "FindIt")
.First<MySecondControl>();
_Other.DoMethod();
};
Best of luck!
i would probably break out the functionality of the timer control here; something like this (note: i am writing this on-the-fly so let me know if it doesn't work as-is, and i will help correct any issues):
// a simple delegate to report the amount of time remaining
// prior to the expiration of the major tick interval; zero
// indicates that this major tick has elapsed.
public delegate void delegateMajorMinorTimerTick
(
int TimeRemaining_sec, ref bool CancelTimer
);
// you could use milliseconds for the interval settings to get
// better granularity, or you could switch to setting the major
// interval instead, however that approach would require a bit
// more checking to make sure the control has sane settings.
public class MajorMinorTimer
{
// this sets the interval in seconds between the
// "minor" ticks used for intermediate processing
// these are the "inner" intervals of the timer
private int myMinorTickInterval_sec;
public int MinorTickInterval_sec
{
get { return myMinorTickInterval_sec; }
}
// this sets the number of minor ticks between the
// expiration of the major interval of the timer.
// the "outer" interval of the timer
private int myMinorTicksPerMajorTick;
public int MinorTicksPerMajorTick
{
get { return myMinorTicksPerMajorTick; }
}
public MajorMinorTimer
(
int parMinorTickInterval_sec,
int parMinorTicksPerMajorTick
)
{
MinorTickInterval_sec = parMinorTickInterval_sec;
MinorTicksPerMajorTick = parMinorTicksPerMajorTick;
}
private DispatcherTimer myBackingTimer;
private int myMinorTickCount;
public void Start()
{
// reset the minor tick count and start the dispatcher
// timer with some reasonable defaults.
myMinorTickCount = 0;
myBackingTimer =
new DispatcherTimer
(
TimeSpan.FromSeconds(MinorTickInterval_sec),
DispatcherPriority.Normal,
new EventHandler(myBackingTimer_Tick),
Dispatcher.CurrentDispatcher
);
myBackingTimer.Start();
}
public event delegateMajorMinorTimerTick onTick;
private bool FireMajorMinorTimerTick(int TimeRemaining_sec)
{
// allows the timer sink to cancel the timer after this
// call; just as an idea, also could be handled with a
// call to Stop() during the event, but this
// simplifies handling a bit (at least to my tastes)
bool CancelTimer = false;
if (onTick != null)
onTick(TimeRemaining_sec, ref CancelTimer);
return CancelTimer;
}
private void myBackingTimer_Tick(object Sender, EventArgs e)
{
// since we are using a DispatchTimer with settings that should
// do not suggest the possibility of synchronization issues,
// we do not provide further thread safety. this could be
// accomplished in the future if necessary with a lock() call or
// Mutex, among other methods.
++myMinorTickCount;
int TicksRemaining = myMinorTickCount - MinorTicksPerMajorTick;
bool Cancel =
FireMajorMinorTimerTick(TicksRemaining * MinorTickInterval_sec);
if (TicksRemaining == 0)
myMinorTickCount = 0;
if (Cancel)
Stop();
}
public void Stop()
{
myBackingTimer.Stop();
}
}
then, assuming, say, a Quiz control, the timer is used like so:
public void QuestionTimerSetup()
{
// sets up a timer to fire a minor tick every second
// with a major interval of 5 seconds
MajorMinorTimer timerQuestion = new MajorMinorTimer(1, 5);
timerQuestion.onTick +=
new delegateMajorMinorTimerTick(QuestionControl_QuestionTimerTick);
}
// ...
public void QuestionControl_OnTick(int TimeRemaining_sec, ref bool CancelTimer)
{
if (TimeRemaining_sec > 0)
{
tblockQuizStatus.Text =
string.Format("There are {0} seconds remaining.", TimeRemaining_sec);
}
else
{
// just for an example
if (NoMoreQuestions)
{
CancelTimer = true;
HandleEndOfQuiz();
tblockQuizStatus.Text =
"Time's up! The quiz is complete!";
}
else
{
tblockQuizStatus.Text =
"Time's up! Press Enter to continue to the next problem.";
}
}
}
another option (rather than, or in addition to, events) in implementing this might be to add an Action taking the time remaining in the major interval for the minor interval action, an Action for the major interval action, and a Func<bool> that checks the stop condition, allowing the user to perform the desired actions in that way. like this:
public class MajorMinorTimer
{
public MajorMinorTimer
(
int parMinorTimerInterval_sec,
int parMinorTicksPerMajorTick,
Action<int> parMinorTickAction,
Action parMajorTickAction,
Func<bool> parShouldStopFunc
)
{
myMinorTimerInterval_sec = parMinorTimerInterval_sec;
myMinorTicksPerMajorTick = parMinorTicksPerMajorTick;
myMinorTickAction = parMinorTickAction;
myMajorTickAction = parMajorTickAction;
myShouldStopFunc = parShouldStopFunc;
}
private Action<int> myMinorTickAction;
private Action myMajorTickAction;
private Func<bool> myShouldStopFunc;
private void myBackingTimer_OnTick()
{
++myMinorTickCount;
int TicksRemaining = myMinorTickCount - MinorTicksPerMajorTick;
if (TicksRemaining == 0)
myMajorTickAction();
else
myMinorTickAction(TicksRemaining * MinorTickInterval_sec);
bool Cancel = myShouldStopFunc();
if (TicksRemaining == 0)
myMinorTickCount = 0;
if (Cancel)
Stop();
}
}
and then in the quiz code instead of hooking up the event do something like:
public void QuestionTimerSetup()
{
MajorMinorTimer timerQuestion =
new MajorMinorTimer
(
1,
5,
// major interval action
(SecsRemaining) =>
{
tblockQuizStatus.Text =
string.Format
(
"There are {0} seconds remaining.", SecsRemaining
);
},
// minor interval action
() =>
{
if (NoMoreQuestions)
{
tblockQuizStatus.Text =
"Time's up! This completes the quiz!";
HandleEndOfQuiz();
}
else
{
tblockQuizStatus.Text =
"Time's up! Press Enter to continue to next question.";
}
},
// timer cancel check function
() =>
IsEndOfQuizHandled()
);
}