Windows Forms - Simple State Machine - c#

I've got a working test state machine in a console app - 3 states and 5 events.
Problem: How to run in Windows Forms ie do I have a main loop which is running all the time looking at state..and if so where...if am using events ie btnPress.
The goal is that the app can be in a number of different states/screens and it needs to be solid, so using a state machine to enforce where we are, and that there are no edge cases unhandled.
Working console app code:
namespace StateMachineTest {
class Program {
static void Main(string[] args) {
var fsm = new FiniteStateMachine();
while (true) {
if (fsm.State == FiniteStateMachine.States.EnterVoucherCode) {
Console.WriteLine("State: " + fsm.State);
Console.WriteLine("Enter Voucher Code:");
string voucherCode = Console.ReadLine();
Console.WriteLine("voucher is " + voucherCode);
Console.WriteLine();
fsm.ProcessEvent(FiniteStateMachine.Events.PressNext);
}
if (fsm.State == FiniteStateMachine.States.EnterTotalSale) {
Console.WriteLine("State: " + fsm.State);
Console.WriteLine("Enter Total Sale or x to simulate back");
string voucherSaleAmount = Console.ReadLine();
if (voucherSaleAmount == "x")
fsm.ProcessEvent(FiniteStateMachine.Events.PressBackToVoucherCode);
else {
Console.WriteLine("total sale is " + voucherSaleAmount);
Console.WriteLine();
fsm.ProcessEvent(FiniteStateMachine.Events.PressRedeem);
}
}
if (fsm.State == FiniteStateMachine.States.ProcessVoucher) {
Console.WriteLine("State: " + fsm.State);
Console.WriteLine("Press 1 to fake a successful redeem:");
Console.WriteLine("Press 2 to fake a fail redeem:");
Console.WriteLine("Press 3 to do something stupid - press the Next Button which isn't allowed from this screen");
Console.WriteLine();
string result = Console.ReadLine();
//EnterVoucherCode state
if (result == "1")
fsm.ProcessEvent(FiniteStateMachine.Events.ProcessSuccess);
if (result == "2")
fsm.ProcessEvent(FiniteStateMachine.Events.ProcessFail);
if (result == "3")
fsm.ProcessEvent(FiniteStateMachine.Events.PressNext);
}
//how to handle async calls?
//how to handle many many states.. matrix could get unwieldy
}
}
}
class FiniteStateMachine {
//first state is the default for the system
public enum States { EnterVoucherCode, EnterTotalSale, ProcessVoucher };
public enum Events { PressNext, PressRedeem, ProcessSuccess, ProcessFail, PressBackToVoucherCode };
public delegate void ActionThing();
public States State { get; set; }
private ActionThing[,] fsm;
public FiniteStateMachine() {
//array of action delegates
fsm = new ActionThing[3, 5] {
//PressNext, PressRedeem, ProcessSuccess, ProcessFail, PressBackToVoucherCode
{PressNext, null, null, null, null}, //EnterVoucherCode.... can pressnext
{null, PressRedeem, null, null, PressBackToVoucherCode}, //EnterTotalSale... can pressRedeem or pressBackToVoucherCode
{null, null, ProcessSuccess, ProcessFail, null} }; //moving from ProcessVoucher... can be a processSuccess or ProcessFail.. can't go back to redeem
}
public void ProcessEvent(Events theEvent) {
try {
var row = (int)State;
var column = (int)theEvent;
//call appropriate method via matrix. So only way to change state is via matrix which defines what can and can't happen.
fsm[row, column].Invoke();
}
catch (Exception ex) {
Console.WriteLine(ex.Message); //possibly catch here to go to an error state? or if do nothing like here, then it will continue on in same state
}
}
private void PressNext() { State = States.EnterTotalSale; }
private void PressRedeem() { State = States.ProcessVoucher; }
private void ProcessSuccess() { State = States.EnterVoucherCode; }
private void ProcessFail() { State = States.EnterVoucherCode; }
private void PressBackToVoucherCode() { State = States.EnterVoucherCode; }
}
}
Not working WinForms code:
//goal is to get a fsm demo working with 3 states and 5 events.
//need number buttons, redeem and back to work.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e) {
SystemSettings.ScreenOrientation = ScreenOrientation.Angle90;
var fsm = new FiniteStateMachine();
while (true)
{
if (fsm.State == FiniteStateMachine.States.EnterVoucherCode)
{
//Console.WriteLine("State: " + fsm.State);
//if next/redeem button is pressed
//fsm.ProcessEvent(FiniteStateMachine.Events.PressNext);
}
if (fsm.State == FiniteStateMachine.States.EnterTotalSale)
{
Console.WriteLine("State: " + fsm.State);
Console.WriteLine("Enter Total Sale or x to simulate back");
string voucherSaleAmount = Console.ReadLine();
if (voucherSaleAmount == "x")
fsm.ProcessEvent(FiniteStateMachine.Events.PressBackToVoucherCode);
else
{
Console.WriteLine("total sale is " + voucherSaleAmount);
Console.WriteLine();
fsm.ProcessEvent(FiniteStateMachine.Events.PressRedeem);
}
}
if (fsm.State == FiniteStateMachine.States.ProcessVoucher)
{
Console.WriteLine("State: " + fsm.State);
Console.WriteLine("Press 1 to fake a successful redeem:");
Console.WriteLine("Press 2 to fake a fail redeem:");
Console.WriteLine("Press 3 to do something stupid - press the Next Button which isn't allowed from this screen");
Console.WriteLine();
string result = Console.ReadLine();
//EnterVoucherCode state
if (result == "1")
fsm.ProcessEvent(FiniteStateMachine.Events.ProcessSuccess);
if (result == "2")
fsm.ProcessEvent(FiniteStateMachine.Events.ProcessFail);
if (result == "3")
fsm.ProcessEvent(FiniteStateMachine.Events.PressNext);
}
}
}
private void btn_0_MouseUp(object sender, MouseEventArgs e)
{
txtCode.Text += '0';
}
private void btn_1_MouseUp(object sender, MouseEventArgs e)
{
txtCode.Text += '1';
}
private void btn_2_MouseUp(object sender, MouseEventArgs e)
{
txtCode.Text += '2';
}
private void btn_del_MouseUp(object sender, MouseEventArgs e)
{
txtCode.Text = txtCode.Text.Substring(0, txtCode.Text.Length - 1);
}
private void btn_redeem_MouseUp(object sender, MouseEventArgs e)
{
txtCode.Visible = false;
txtStatus.Visible = true;
txtStatus.Text = "PROCESSING PLEASE WAIT";
}
Code from:
Simple state machine example in C#?

There is no need to have a polling event loop that's constantly checking the state, that's what a WinForm does automatically. You should have your UI elements wire up event handlers, and those event handlers should be responsible for checking/toggling state.
This is a very dirty implementation. If you apply the State Pattern (Chapter 9 of Head First Design Patterns has a really clean example), you should be able to use your Form as the Client that holds another object corresponding to the Context that is called by the event handlers of your UI elements.

Your code smells in many ways. First of all, winforms works using a single thread, therefore with your loop, you block the thread and hence the form. Secondly, you work with Console logic within your winforms app... did you even test this? Thirdly, you never set the statemachine to a different state. Are you intending to make the buttons set the next state?
A state machine should loop something like this.
public class StateManager
{
public void Transition(IState state)
{
state.Transition(CurrentState, StateManager);
}
public IState CurrentState { get; private set; }
public event EventHandler StateSwitched;
}
public class FirstState : IState
{
private Form _form;
public FirstState(Form form)
{
_form = form;
}
public void Transition(IState oldState, StateManager stateManager)
{
_form.Closing += (sender, e) =>
{
stateManager.Transition(new SecondState(_form));
};
}
}
public class SecondState : IState
{
...
}

Related

how to do a multithreaded text search using background worker and display the entire line in a listview in c#

So, I have a win form where I have to search for a string in a text file and display the line number and the entire line if I found the string. The search has to be multithreaded and all the line numbers and the lines must be on a listview. For example if the word "language" is in line number 60 , the listview must display:
60 "the line has the word language"
I have used the background worker in this regard but I am not being able to display the correct line number and the lines. Firstly, one line is being displayed multiple times and secondly, the line number is always coming to be 0. However, when I output the result in Console, the result is correct. I think I am making some error in putting the result into the listview.
Here' s my main form.cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// private bool start_cancel;
bool bln = true;
private void StartCancelbtn_Click(object sender, EventArgs e)
{
if (bln)
text2();
else
text1();
bln = !bln;
}
private void text1()
{
StartCancelbtn.Text = "Start";
this.backgroundWorker1.CancelAsync();
}
private void text2()
{
StartCancelbtn.Text = "Cancel";
StartThread();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
System.ComponentModel.BackgroundWorker worker;
worker = (System.ComponentModel.BackgroundWorker)sender;
// Get the Words object and call the main method.
main_work WC = (main_work)e.Argument;
WC.CountWords(worker, e);
if (worker.CancellationPending)
{
e.Cancel = true;
// break;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
main_work.ReportState state =
(main_work.ReportState)e.UserState;
ListViewItem l1 = new ListViewItem();
l1.Text = state.LinesCounted.ToString();
l1.SubItems.Add(state.line);
listView1.Items.Add(l1);
}
private void backgroundWorker1_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
MessageBox.Show("Error: " + e.Error.Message);
else if (e.Cancelled)
MessageBox.Show("Word counting canceled.");
else
MessageBox.Show("Finished counting words.");
}
private void StartThread()
{
main_work WC = new main_work();
WC.CompareString = this.searchtext.Text;
WC.SourceFile = this.filenametextbox.Text;
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync(WC);
}
private void browsebtn_Click(object sender, EventArgs e)
{
using (OpenFileDialog brw = new OpenFileDialog())
{
if (brw.ShowDialog() == DialogResult.OK)
{
using (StreamReader sr = new StreamReader(brw.FileName))
{
filenametextbox.Text = brw.FileName.ToString();
}
}
}
}
}
}
Here is my main_work class where the actual comparison is happening:
class main_work
{
public class ReportState
{
public int LinesCounted;
public string line;
}
Queue q = new Queue();
public string SourceFile;
public string CompareString;
public string line2 ;
public int linenumber=0;
public int linenumber1 = 0;
int LinesCounted1;
// public string line2;
public void CountWords(
System.ComponentModel.BackgroundWorker worker,
System.ComponentModel.DoWorkEventArgs e)
{
// Initialize the variables.
ReportState state = new ReportState();
string line1 = "";
int elapsedTime = 1;
DateTime lastReportDateTime = DateTime.Now;
if (CompareString == null ||
CompareString == System.String.Empty)
{
MessageBox.Show("Please Enter a string to be searched");
}
else
{
// Open a new stream.
using (System.IO.StreamReader myStream = new System.IO.StreamReader(SourceFile))
{
// Process lines while there are lines remaining in the file.
while (!myStream.EndOfStream)
{
if (worker.CancellationPending)
{
e.Cancel = true;
// break;
}
else
{
line1 = myStream.ReadLine();
line2 = (CountInString(line1, CompareString));
LinesCounted1 = (linenumbercount(line1, CompareString, linenumber1));
// Raise an event so the form can monitor progress.
int compare = DateTime.Compare(
DateTime.Now, lastReportDateTime.AddSeconds(elapsedTime));
if (compare > 0)
{
state.LinesCounted = LinesCounted1;
state.line = line2;
worker.ReportProgress(0, state);
lastReportDateTime = DateTime.Now;
}
System.Threading.Thread.Sleep(1);
}
}
// Report the final count values.
state.LinesCounted = LinesCounted1;
state.line = line1;
worker.ReportProgress(0, state);
lastReportDateTime = DateTime.Now;
}
}
}
private string CountInString(
string SourceString,
string CompareString)
{
if (SourceString.Contains(CompareString))
{
line2 = SourceString;
Console.WriteLine(SourceString);
}
return line2;
}
private int linenumbercount(
string SourceString,
string CompareString,
int linenumber)
{
// Lines = 0;
if (SourceString == null)
{
return 0;
}
if (SourceString.Contains(CompareString))
{
Console.WriteLine(linenumber);
}
linenumber++;
return linenumber;
}
}
Any feedback will be helpful. Please let me know what I am doing wrong and where is it that I am making a mistake as I am new to background worker and multithreading. Thanks.

C# Form App - stop execution of all code from a helper method that resides in a helper class

I just learned how to pass arguments into methods, so I'm refactoring my code to make it cleaner. I have created a new "ValidateInput" class which holds a ValidateFinancialsInput method which I pass a string into. It then checks the string to see if it is correct, if it's not I want to show a messageBox, then stop execution of ALL of the code. If i use "return;", it just resumes execution of the Parent method. How do I stop execution of all of the code within the ValidateFinancialsInput method? I tried researching this for a while to no avail. Here is my code:
Class Parent
{
private void button2_Click(object sender, EventArgs e)
{ var CompanyVar = comboBox1.Text;
ValidateInput vi = new ValidateInput();
vi.ValidateFinancialsInput(CompanyVar);
//the rest of my code for the application is here
//the rest ...
//the rest...
}
}
class ValidateInput
{
public void ValidateFinancialsInput(string Co)
{
string[] validCompany = { "BVV", "LWDO" };
if (validCompany.Contains(Co) == false)
{
MessageBox.Show("You have entered an invalid company.");
//what do I put here to stop all code execution?
}
}
}
You should try and use return values state intent to calling methods
Class Parent
{
private void button2_Click(object sender, EventArgs e)
{ var CompanyVar = comboBox1.Text;
ValidateInput vi = new ValidateInput();
if(!vi.ValidateFinancialsInput(CompanyVar))
{
MessageBox.Show("You have entered an invalid company.");
return;
}
//the rest of my code for the application is here
//the rest ...
//the rest...
}
}
class ValidateInput
{
public bool ValidateFinancialsInput(string Co)
{
string[] validCompany = { "BVV", "LWDO" };
if (validCompany.Contains(Co) == false)
{
return false;
}
return true;
}
}
What I'm doing here is returning a true|false value to indicate whether the validation has passed, if it has not passed then I display the MessageBox, else it continues the execution of the "other" code.
Hope this helps
The simplest way is with an exception:
class Parent
{
private void button2_Click(object sender, EventArgs e)
{
try
{
var CompanyVar = comboBox1.Text;
ValidateInput vi = new ValidateInput();
vi.ValidateFinancialsInput(CompanyVar);
//the rest of my code for the application is here
//the rest ...
//the rest...
}
catch (ValidationException ex)
{
MessageBox.Show(ex.Message);
}
}
}
class ValidationException : Exception
{
public ValidationException(string message) : base(message)
{
}
}
class ValidateInput
{
public void ValidateFinancialsInput(string Co)
{
string[] validCompany = { "BVV", "LWDO" };
if (validCompany.Contains(Co) == false)
{
throw new ValidationException("You have entered an invalid company.");
}
}
}
This will stop execution of ValidateFinancialsInput and in button2_Click move execution inside the catch (ValidationException ex) where you can decide what to do with the validation error
You have a class that it's whole purpose is to validate, So you could add a public method IsValidated
You could add much more with the class, for example have a list of all business rules it violates and return them through another method or property.
class ValidateInput
{
public bool IsValidated {get; private set}
public bool ValidateFinancialsInput(string Co)
{
string[] validCompany = { "BVV", "LWDO" };
this.IsValidated = validCompany.Contains(Co)
}
}
This class should only know about the validation process and should do nothing else.
You have a few options. It looks like you have buttons in your program so I would guess this is not a console application. If you want the application to completely stop you can use Application.Exit or check out Environment.Exit https://msdn.microsoft.com/en-us/library/system.environment.exit(v=vs.110).aspx
However, I would suggest using exceptions so you do not terminate your entire program:
try
{
var CompanyVar = comboBox1.Text;
ValidateInput vi = new ValidateInput();
vi.ValidateFinancialsInput(CompanyVar);
//the rest of my code for the application is here
//the rest ...
//the rest...
}
catch (ValidationException ex)
{
MessageBox.Show(ex.Message);
}
}
public void ValidateFinancialsInput(string Co)
{
string[] validCompany = { "BVV", "LWDO" };
if (validCompany.Contains(Co) == false)
{
throw new ValidationException("You have entered an invalid company.");
}
}

How to run in main thread from worker thread that start by Nancyfx? C#

I have a problem with using nancyfx in my winform application (I make a winform app and use a nancyfx inside the application) So i can use some API url to make change in the winform without additional server or services (because i attached the nancy in the winform apps)
Here is my Form1.cs
public partial class Form1 : Form
{
public Form1(bool test)
{
InitializeComponent();
textBox1.Text += "Apps Method "+ Environment.NewLine;
}
public bool startTestAPI()
{
textBox1.Text += "Api Worked" + Environment.NewLine);
}
private void button2_Click(object sender, EventArgs e)
{
HostingAPI s = new HostingAPI();
s.Start();
textBox1.Text += "Api Running" + Environment.NewLine);
}
}
public class ModuleCDM : NancyModule
{
public ModuleCDM()
{
try
{
Thread th2 = Thread.CurrentThread;
Get["/Start"] = parameters =>
{
Form1 form = new Form1(false);
Thread testthread = Form1.curthread;
bool res = form.startTestAPI();
if (res == true)
{
var feeds = new string[] { "Success" };
return Response.AsJson(feeds);
}
else
{
var feeds = new string[] { "Failed" };
return Response.AsJson(feeds);
}
};
}
}
}
and this is my HostingAPI.cs
public class HostingAPI
{
private NancyHost hostNancy;
private string hostUrl;
public void Start()
{
hostUrl = ConfigModule.ModuleAddress;
if (hostUrl == null) hostUrl = "http://localhost:5005";
hostNancy = new NancyHost(new Uri(hostUrl));
hostNancy.Start();
}
public void Stop()
{
hostNancy.Stop();
}
}
And it successfully run without error, but when i call api (localhost:5005/Start) the textbox in winform apps not add the text i wanted ("Api Worked"). I noticed it is because Nancyfx create another thread when there is API call, and i can use invoke/begininvoke because !invokerequired always comes with value false. So how can i access the main thread or maybe another solution to update the UI when i call the API.
Thanks
You have 2 issues in here.
You start host api service from Form1 instance then within Nancy Module you create a different Form1 instance which is invisible and you try to do access certain methods within that class
Cross thread issue as you rightfully guessed . You are trying to write from another thread context than the UI thread
Look at the code at below to achieve this. Bear in mind that you can create Singleton Form or find another way to access the instance of Form1
public class HostingAPI
{
private NancyHost hostNancy;
private string hostUrl;
public HostingAPI()
{
}
public void Start()
{
var hostConfig = new HostConfiguration
{
UrlReservations = new UrlReservations
{
CreateAutomatically = true
},
};
//hostUrl = ConfigModule.ModuleAddress;
if (hostUrl == null) hostUrl = "http://localhost:5005";
hostNancy = new NancyHost(hostConfig,new Uri(hostUrl));
hostNancy.Start();
}
public void Stop()
{
hostNancy.Stop();
}
}
public partial class Form1 : Form
{
delegate void SetTextCallback(string text);
public static Form1 Instance;
public Form1(bool test)
{
InitializeComponent();
textBox1.Text += "Apps Method " + Environment.NewLine;
Instance = this;
}
private void button1_Click(object sender, EventArgs e)
{
HostingAPI s = new HostingAPI();
s.Start();
textBox1.Text += "Api Running" + Environment.NewLine;
}
public void startTestAPI()
{
SetText("Api Worked" + Environment.NewLine);
}
private void SetText(string text)
{
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text += text;
}
}
}
public class ModuleCDM : NancyModule
{
public ModuleCDM()
{
try
{
Thread th2 = Thread.CurrentThread;
Get["/Start"] = parameters =>
{
var form1 = Form1.Instance;
form1.startTestAPI();
var feeds = new[] {"Success"};
return Response.AsJson(feeds);
};
}
catch
{
}
}
}

Correct Event handling in C#

this is basically a follow up to a previous question (Triggering an event in c# from c++ and declaring LPCWSTR). I've revised my code based on the answers and comments I have received and I solved the initial issue, which was passing the event to the GpioSetupInterruptPin from a gpio api. I don't have a lot of documentation on the api but what i'm trying to achieve is: have a form with a white label; after pressing a switch, the label turns yellow.
The problem i'm having now is the event seems to trigger as soon as it's created (the "execute" message is passed to the debug dialog and the label turns yellow) but it doesn't do anything when i toggle the switch. I was told in the last question to use WaitForSingleObject but i'm not really sure where to call it and this article only added to my confusion.
public partial class Form1 : Form
{
// P/Invoke CreateEvent and WaitForSingleObject
private void GPIO_Open() //get handle for gpio
private void GPIO_Output() //output pin declaration
private void button1_Click(object sender, EventArgs e)
{
Interrupt_Setup();
}
private void Interrupt_Setup()
{
hGPIO = GPIOapi.GpioOpenHandle(); //returns a handle to the gpio
GIPO_ON = true;
Debug.WriteLine("Driver open \n" + hGPIO);
GPIO_Output(); //set output pins
GPIO_Interrupt(Trigger); //configure interrupt
}
private void GPIO_Interrupt(string trigger)
{
bool ok;
_Main();
//INTERRUPT DECALRATION
ok = GPIOapi.GpioSetupInterruptPin(hGPIO, port6, 4, GPIOapi.INT_TRIGGER_MODE.TRIGGER_MODE_EDGE,
GPIOapi.INT_TRIGGER_POLARITY.TRIGGER_POL_HIGH_RISING, trigger, true);
Thread waitThread=new Thread(WaitForTrigger);
waitThread.Start();
if (!ok)
Debug.WriteLine("NO interrupt");
else
Debug.WriteLine("Interrupt set for:" + port6 + "04" + " at " + hGPIO);
}
public static string Trigger = "InputProcessUpdateHandler";
public static IntPtr handle = CreateEvent(IntPtr.Zero, false, false, Trigger); //used P/Invoke
private static InputProcessor inputProcessor = null;
public Color[] color =
{
Color.Orchid, Color.DarkOrchid, Color.GreenYellow, Color.CornflowerBlue, Color.SteelBlue,Color.Crimson
};
public int i = 0;
public void WaitForTrigger()
{
while(true)
{try
{
if (WaitForSingleObject(handle, 0xFFFFFFFF) == false)
{
BeginInvoke(((System.Action)(() =>label2.BackColor = color[i])));
i++;
if (i > 4)
i = 0;
}
Thread.Sleep(300);
}
catch (Exception e)
{ Debug.WriteLine("exception: " + e); }}
}
}
private void _Main()
{
inputProcessor = new InputProcessor();
ShowToggle showToggle = new ShowToggle(inputProcessor);
inputProcessor.Process(label1);
}
public class ShowToggle
{
private InputProcessor _inputProcessor = null;
public ShowToggle(InputProcessor inputProcessor)
{
_inputProcessor = inputProcessor;
_inputProcessor.updateHandledBy += InputProcessUpdateHandler;
}
private void InputProcessUpdateHandler(Label label)
{
label.BackColor = Color.Yellow;
Debug.Write("execute");
}
}
public class InputProcessor
{
public delegate void InputProcessUpdateHandler(Label label);
public event InputProcessUpdateHandler updateHandledBy = null;
public void Process(Label label)
{
if (updateHandledBy != null)
updateHandledBy(label);
}
}
If anyone could help me with this, I would be very grateful.
*** I got it working but it looks a right mess. Could anyone help me straighten it out?
You code is really confusing to me. I think what you want is something like this. Bear in mind I'm typing this into the SO text editor, so don't expect it to compile and just work - it's a guide. Consider it a step above pseudocode.
public class DeviceInterrupt
{
IntPtr m_gpio;
string m_eventName;
public event EventHandler OnInterrupt;
public DeviceInterrupt(int port)
{
// get a driver handle
m_gpio = GPIO_Open();
// generate some unique event name
m_eventName = "GPIO_evt_" + port;
// wire up the interrupt
GpioSetupInterruptPin(m_gpio, port, m_eventName, ...);
// start a listener
new Thread(EventListenerProc)
{
IsBackground = true,
Name = "gpio listener"
}
.Start();
}
public void Dispose()
{
// TODO: release the handle
}
private void EventListenerProc()
{
// create the event with the name we sent to the driver
var wh = new WaitHandle(false, m_eventName);
while (true)
{
// wait for it to get set by the driver
if (wh.WaitOne(1000))
{
// we have an interrupt
OnInterrupt.Fire(this, EventArgs.Empty);
}
}
}
}
Usage would then be something like this:
var intr = new DeviceInterrupt(4);
intr.OnInterrupt += MyHandler;
....
void MyHandler(object sender, EventArgs a)
{
Debug.WriteLine("Interrupt occurred!");
}
Note
The Compact Framework doesn't support actual named system events, so the named WaitHandle I use in my code above is not a CF-supplied WaitHandle. Instead I'm using the one from the Smart Device Framework. You could also P/Invoke to CreateEvent and WaitForSingleObject yourself.

Factorial of a number using events in C#

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.

Categories

Resources