Access dynamic control from class in a thread - c#

I'm working on a small project which deals with polling devices to check states of I/O controls. I had implemented a small project dealing with a particular device, but have decided that i would like to eventually implement different devices, and so have moved over to an class : interface approach. This has caused a few problems however, since i moved a lot of code around.
Before i moved the code around and such, i was accessing dynamic form controls by using a delegate as such;
if (result != null)
{
this.Invoke((MethodInvoker)delegate
{
txtOutput1.Text = (result[4] == 0x00 ? "HIGH" : "LOW"); // runs on UI thread
if (result[4] == 0x00)
{
this.Controls["btn" + buttonNumber].BackColor = Color.Green;
}
else
{
this.Controls["btn" + buttonNumber].BackColor = Color.Red;
}
});
}
This worked fine until i moved certain methods to a new class which inherits from an interface. I don't want to just set the dynamic buttons to public, and i'm not sure i can create get;set; for dynamic buttons, considering theres lots of them and are created on startup. Another problem is the this.invoke" command. I believe the invoke command doesn't work unless it's placed on a form...and now it's been moved to a class, so i need to look at another way of doing this.
Does anyone have any ideas as to where i should be heading with this?
EDIT 1:
The program is designed as a monitoring system for hardware devices that handle inputs/outputs. using these i can check if, for example, a door alarm has been triggered and such. The program itself in terms of forms / design is very simple. Currently i have a single form, which generates buttons based on information in a database, for example if there are 10 devices configured, there are 10 buttons. each of these shows green / red dependant on the hardware state.
My main form triggers a thread for each device which monitors it, but because i wished to have multiple types of device i moved them to different classes and an interface which handles all of the common methods. Currently i have a device class, which implements an interface. With regards to this question, i need to now access an instance of the single main form from which i am updating, rather than creating a new instance, so that i can use the new method i created when i moved said logic into the form itself.
EDIT 2:
IdeviceInterface bfdeviceimp = new bf2300deviceimp();
// some other declarations and initialize components
private void btnConnect_Click(object sender, EventArgs e)
{
updateUI();
}
public void updateUI()
{
DBConnector mDBConnector = new DBConnector();
int count = mDBConnector.Count() - 1;
DataTable dataTable = mDBConnector.Select("SELECT * FROM devices");
int x = 12;
int y = 65;
for (int i = 0; i <= count && i < 25; i++)
{
Button btnAdd = new Button();
btnAdd.Text = dataTable.Rows[i]["deviceDescription"].ToString();
btnAdd.Location = new Point(x, y);
btnAdd.Tag = i;
btnAdd.Name = "btn" + i.ToString();
btnAdd.BackColor = Color.Green;
var temp = i + 1;
this.Controls.Add(btnAdd);
this.Controls[btnAdd.Name].MouseClick += (sender, e) =>
{
int index = temp;
generalMethods.generatePopup(sender, e, index);
};
string address = dataTable.Rows[i]["deviceIP"].ToString();
int port = int.Parse(dataTable.Rows[i]["devicePort"].ToString());
ThreadStart workerThread = delegate { start(address, port, i); };
new Thread(workerThread).Start();
x = (x + 75);
if (i != 0 && (i % 5) == 0)
{
x = 12;
y = y + 30;
}
if (i == 25)
{
Button btnPreviousPage = new Button();
btnPreviousPage.Text = "<";
btnPreviousPage.Location = new Point(150, 350);
btnPreviousPage.Tag = "left";
this.Controls.Add(btnPreviousPage);
Button btnNextPage = new Button();
btnNextPage.Text = ">";
btnNextPage.Location = new Point(225, 350);
btnNextPage.Tag = "right";
this.Controls.Add(btnNextPage);
}
}
}
public void start(string address, int port, int i)
{
if (timer == null)
{
timer = new System.Timers.Timer(1000);
timer.Elapsed += delegate(object sender, ElapsedEventArgs e) { timerElapsed(sender, e, address, port, i); };
}
timer.Enabled = true;
// MessageBox.Show("Thread " + i + " Started.");
}
public void timerElapsed(object sender, ElapsedEventArgs e, string address, int port, int i)
{
bfdeviceimp.newconnect(address, port, i);
}
and then finally my device class:
class bf2300deviceimp : IdeviceInterface
{
public void newconnect(string address, int port, int buttonNumber)
{
//send data
byte[] bData = new byte[71];
bData[0] = 240;
bData[1] = 240;
bData[2] = 0;
bData[3] = 1;
bData[68] = 240;
bData[69] = 240;
bData[70] = this.newCalculateCheckSum(bData);
try
{
byte[] result = this.newSendCommandResult(address, port, bData, 72);
//form1.setAlarmColour(result, buttonNumber);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Where would you suggest i put the statechanged handler?

You should to use an events-based approach for solving this problem, as is often the case when it comes to passing information between forms. Each of your devices should have a custom event that they define which is fired when the state of that device changes. The event should probably just be defined in the interface for interacting with that device. The form, when it creates the various device classes should subscribe to the event and in the event handler it should update the button/textbox appropriately.
This might be a fair bit to take in if you're not used to this style of programming. Feel free to ask for more details in the comments and I can elaborate on why I did something the way I did or what it actually does.
public Form1()
{
InitializeComponent();
//not sure if this is on initialization or in a button click event handler or wherever.
IDevice device = new SomeDevice();
device.StatusChanged += GetHandlerForDevice(1);
device.DoStuff();
IDevice device2 = new SomeDevice(); //could be another class that implements IDevice
device.StatusChanged += GetHandlerForDevice(2);
device.DoStuff();
}
/// <summary>
/// The handlers for device status changed only vary based on the button number for each one.
/// This method takes a button number and returns an event handler that uses that button number.
/// </summary>
/// <param name="buttonNumber"></param>
/// <returns></returns>
private EventHandler<StatusChangedEventArgs> GetHandlerForDevice(int buttonNumber)
{
//use currying so that the event handler which doesn't have an appropriate signature
//can be attached to the status changed event.
return (sender, args) => device_StatusChanged(sender, args, buttonNumber);
}
private void device_StatusChanged(object sender, StatusChangedEventArgs args, int buttonNumber)
{
this.Invoke((MethodInvoker)delegate
{
txtOutput1.Text = (args.CurrentStatus == IDevice.Status.Green ? "HIGH" : "LOW"); // runs on UI thread
if (args.CurrentStatus == IDevice.Status.Green)
{
this.Controls["btn" + buttonNumber].BackColor = Color.Green;
}
else
{
this.Controls["btn" + buttonNumber].BackColor = Color.Red;
}
});
}
public interface IDevice
{
event EventHandler<StatusChangedEventArgs> StatusChanged;
Status CurrentStatus { get; }
public enum Status
{
Green,
Red
}
void DoStuff();
// rest of interface ...
}
public class StatusChangedEventArgs : EventArgs
{
public IDevice.Status CurrentStatus { get; set; }
//can add additional info to pass from an IDevice to a form if needed.
}
public class SomeDevice : IDevice
{
public event EventHandler<StatusChangedEventArgs> StatusChanged;
private IDevice.Status _currentStatus;
/// <summary>
/// Gets the current status of the device this object represents.
/// When set (privately) it fires the StatusChanged event.
/// </summary>
public IDevice.Status CurrentStatus
{
get { return _currentStatus; }
private set
{
_currentStatus = value;
if (StatusChanged != null)
{
StatusChangedEventArgs args = new StatusChangedEventArgs();
args.CurrentStatus = value;
StatusChanged(this, args);
}
}
}
public void DoStuff()
{
//... do stuff
CurrentStatus = IDevice.Status.Green; //will fire status changed event
}
}

Move all the logic inside a method in the form and use it externally.

In your form create a property
public SynchronizationContext SyncContext { get; set;}
In form constructor add:
this.SyncContext = WindowsFormsSynchronizationContext.Current;
Make interface:
public Interface IChangeClient
{
void Process(<some_type> result); // place your logic here
}
(or somethng like that) and implement it in your Form to change your buttons and text.
Extend your original interface to use (maybe as a parameter) SynchronizationContext and IBackgroundChangeClient.
And than your code would look like this:
if (result != null)
{
oSyncContext.Post(new System.Threading.SendOrPostCallback(
delegate(object state)
{
IBackgroundChangeClient client = (state as object[])[0] as IBackgroundChangeClient
//i dont konw the type of this
var innerResult= (state as object[])[1];
client.Process(innerResult);
}), new object[] { oBackgroundChangeClient, result[4]});
}

Related

Background worker is not reporting progress Winforms

I have a background worker running, which is dynamically making form fields from an xml file. Depending on the size of the xml, it takes some time to load, so I am using a loading bar to report the progress to use so they won't exit out of the program. The program works as intended, it hides the loading panel and shows the form fields when the worker finishes, but while loading, the loading bar won't load. I received no errors.
This is where the report progress is being called:
if (!retrievePath.Equals(""))
{
// create the template with the data from the file
XDocument filledDoc = templateCreator.CreateTemplateWithGivenData2(retrievePath, fileName2);
tempDoc = filledDoc;
XElement root = tempDoc.Root;
// get child forms of return data state and sections
IDataInterface dataInterface = new DataInterfaceImplementation();
IEnumerable<XElement> sections = dataInterface.GetSections(filledDoc);
// Grab forms that aren't empty
IEnumerable<XElement> forms = XmlClass.GetMefForms(filledDoc).Where(u => u.Value != "").ToList();
IEnumerable<XElement> extra = dataInterface.GetSections(filledDoc).Where(u => u.Value != "").ToList();
// get the return header state
elemForms = dataMiddleman.GetSections(filledDoc);
foreach (XElement el in elemForms)
{
if (el.Name.LocalName.Equals("ReturnHeaderState"))
{
createForms(el, 3);
}
}
foreach (XElement el in forms)
{
i = i + 1;
i = (i / forms.Count()) * 100;
if (i == 100)
{
i = (i / (forms.Count() - 1)) * 100;
}
createForms(el, i);
}
private void createForms(XElement x, int i)
{
this.Invoke((MethodInvoker)delegate {
backgroundWorker1.ReportProgress(i);
var pLabel = new ParentLabel(x);
this.leftGroup.Controls.Add(pLabel);
var parentPanel = new CustomPanel(x);
parentPanel.SendToBack();
this.thebox.Controls.Add(parentPanel);
RecursiveTraverse(x, parentPanel);
pLabel.Click += (sender, e) => PLabel_Click(sender, e);
pPanels.Add(parentPanel);
});
}
This is my background worker code:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
loadingPanel.BringToFront();
populateNewFields();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
loadingBar.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
loadingBar.Value = 100;
Thread.Sleep(100);
loadingPanel.SendToBack();
loadingBar.Value = 0;
}
Your question is about Background worker is not reporting progress Winforms and I hope it's ok if I use a Minimal Reproducible Example to demo how to successfully fire an event when progress occurs on the background thread (which is is one way to achieve the outcome you want) and reducing the complex Xml operations to a "time-consuming black box" to be dealt with as a separate issue.
This Form will provide a means to test the notification using the MockCreateForm method which mimics a form creation by blocking the background worker for 5 ms. I believe your design spec is to send a notification every 100 operations.
Generic event lacks the needed properties so inherit EventArgs to customize the info received (declaring it outside the MainForm class).
public delegate void ProgressEventHandler(ProgressEventArgs e);
public class ProgressEventArgs : EventArgs
{
public ProgressEventArgs(int count, int total)
{
Count = count;
Total = total;
}
public int Count { get; }
public int Total { get; }
}
When the button (actually a CheckBox where Appearance=Button) state is toggled, it calls this worker Task using a CancellationTokenSource and CancellationToken so it can be halted. Every 100 times, the Progress event is fired:
private void btnWorker_CheckedChanged(object sender, EventArgs e)
{
if(btnWorker.Checked)
{
_cts = new CancellationTokenSource();
Task.Run(() =>
{
var formCount = 10000;
for (int i = 0; i < formCount; i++)
{
if(_cts.IsCancellationRequested)
{
return;
}
// Notify every 100 times.
if((i % 100) == 0)
{
Progress?.Invoke(new ProgressEventArgs(count: i, total: formCount));
}
MockCreateForm();
}
Progress?.Invoke(new ProgressEventArgs(count: formCount, total: formCount));
}, _cts.Token);
}
else
{
_cts.Cancel();
labelStatus.Text = "Idle";
}
}
CancellationTokenSource _cts = null;
The only thing left is to consume the event in the MainForm. The only thing that needs to be marshalled back onto the UI thread is the brief moment that Label.Text is being updated.
public MainForm()
{
InitializeComponent();
Progress += (e) =>
{
Invoke((MethodInvoker)delegate
{
labelStatus.Text = $"{e.Count} of {e.Total}";
});
};
}
public event ProgressEventHandler Progress;
What you do on the background thread is up to you. Just put it here:
public void MockCreateForm()
{
Task.Delay(5).Wait();
}
I hope this gets you closer to what you are trying to achieve.

WPF ProgressBar update from another Class

The premise is really simple - I have a class that is running some expensive computation and I would like to show a ProgressBar to inform the user. Since I might have many computation cycles, I want to have a simple ProgressBar form that I can use many times in my code. That form does not need to know anything else but it's title, the maximum value for the ProgressBar and when to perform a step and increment. The update call is done from the Other Class, the form just displays that.
The following code is from a Windows Forms version of a ProgressBar (exported as a dll) that achieves the goal. I am trying to recreate the same functionality with WPF. From my research so far, this cannot be done. I would love if someone can prove me wrong and demonstrate a pseudo code for both the WPF codebehind and the implentation in the Other Class.
Windows Form
public partial class ProgressForm : Form {
private bool abortFlag;
string _format;
/// <summary>
/// Set up progress bar form and immediately display it modelessly.
/// </summary>
/// <param name="caption">Form caption</param>
/// <param name="format">Progress message string</param>
/// <param name="max">Number of elements to process</param>
public ProgressForm( string caption, string format, int max )
{
_format = format;
InitializeComponent();
Text = caption;
label1.Text = (null == format) ? caption : string.Format( format, 0 );
progressBar1.Minimum = 0;
progressBar1.Maximum = max;
progressBar1.Value = 0;
Show();
Application.DoEvents();
}
public void Increment()
{
++progressBar1.Value;
if( null != _format )
{
label1.Text = string.Format( _format, progressBar1.Value );
}
Application.DoEvents();
}
public bool getAbortFlag()
{
return abortFlag;
}
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Aborting...";
abortFlag = true;
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (Form.ModifierKeys == Keys.None && keyData == Keys.Escape)
{
button1.Text = "Aborting...";
abortFlag = true;
return true;
}
return base.ProcessDialogKey(keyData);
}
public void SetText(string text)
{
label1.Text = text;
System.Windows.Forms.Application.DoEvents();
}
public void SetProgressBarMinMax(int min, int max)
{
progressBar1.Minimum = min;
progressBar1.Maximum = max;
progressBar1.Value = 0;
}
public void IncrementProgressBar()
{
progressBar1.Value++;
System.Windows.Forms.Application.DoEvents();
}
public void HideProgressBar()
{
progressBar1.Visible = false;
System.Windows.Forms.Application.DoEvents();
}
public void ShowProgressBar()
{
progressBar1.Visible = true;
System.Windows.Forms.Application.DoEvents();
}
}
Some Method in Other Class
public class OtherClass(){
int counter = 0;
int n = numberOfIterations;
string s = "{0} of " + n.ToString() + String.Format(" elements processed.", counter.ToString());
string caption = "Duplicating ..";
using (ProgressForm pf = new ProgressForm(caption, s, n)){
//Something heavy to compute
foreach (var sheet in sheetsToDuplicate)
{
if (pf.getAbortFlag())
{
return;
}
counter ++;
pf.Increment();
}
}
}
I think this should be pretty straightfoward but I cannot find a single source out there to show how this can be achieved. For me, the important points to hightlight are:
The WPF (Window) ProgressBar control does not have reference to the Other Class. (because it will be shipped as a dll and should be able to adopt any scenario)
The loop is run in the Other Class.
Thank you.

C# how to make function for all buttons using object sender

I´m using WindowsForm and working with flat design. In the program there are 6 buttons, these buttons are made of a label and a panel. The label controls all the actions that the button can do. when i started writing the program i made one function for each button, now i like to use one function that controls all buttons. I have tried to make that work but I´m stuck and can´t find a way to solve it.
Been looking around at the forum for solutions but i think that i might not know what i´m looking for.
This is what i made so far.
Buttons[] cobra = new Buttons[5];
private class Buttons
{
private bool position;
private string name;
public bool Position
{
get { return position; }
set { position = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
}
private void SetButtons()
{
cobra[0].Name = "label3";
cobra[0].Position = false;
cobra[1].Name = "label4";
cobra[1].Position = false;
}
private void CheckStatusButtons(object import)
{
for (int i = 0; i < cobra.Length; i++)
{
}
}
private class ToggelFunction
{
private bool hawk;
public bool Hawk
{
get { return hawk; }
set { hawk = value; }
}
}
ToggelFunction tiger = new ToggelFunction();
private void label3_Click(object sender, EventArgs e)
{
if (tiger.Hawk == false)
{
button1.BackColor = Color.PaleGreen;
label3.Text = "ON";
if (myport.IsOpen)
{
send(new byte[] { 16, 128, 32, 16, 1 });
}
tiger.Hawk = true;
return;
}
if (tiger.Hawk == true)
{
button1.BackColor = Color.DarkSeaGreen;
label3.Text = "2";
if (myport.IsOpen)
{
send(new byte[] { 16, 128, 32, 8, 1 });
}
tiger.Hawk = false;
return;
}
}
"label3_Click" this is my function for button 1, all buttons look the same just different variables.
As I found on the forum, you can use object sender to i identify which button that made the click and from there use that in the function to make action.
So all buttons will use this functions, i´m not sure how to compare values in the if statement, if button 1 is click then it should check what values button 1 has.
My idea was to make a class "Buttons" and an array to store all the values of each button, it´s not completed yet. When a button is clicked it checks
with the array what values that button has and compare that in the function depending on what the action is. The first action would be to check if the button is on or off. If it´s off then it enters that if statement and there some actions will happen, change of color and the text, these values also have to be stored in the array i guess.
I have tried to compare the array with object sender, but i get some error saying that you can´t compare bool with object i think.
So i wonder if some one might have a solution or suggestions?
I have made a simple example of what i think you want to do. bare in mind this does not abide by all coding best practises but its not a mess either. You will need to put your safety null checks in.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class ButtonVariables
{
public int value1 { get; set; }
public int value2 { get; set; }
}
Dictionary<string, ButtonVariables> bv = new Dictionary<string, ButtonVariables>();
private void ProcessClick(object sender, EventArgs e)
{
ButtonVariables vars = GetVariables(sender);
//Do stuff with your variable set here
}
private ButtonVariables GetVariables(object sender)
{
ButtonVariables returnValue = new ButtonVariables();
switch (((Button)sender).Name.ToLower())
{
case "buttona":
return bv["A"];
case "buttonb":
return bv["B"];
case "buttonc":
return bv["C"];
default:
break;
}
return null;
}
private void ButtonA_Click(object sender, EventArgs e)
{
ProcessClick(sender, e);
}
private void ButtonB_Click(object sender, EventArgs e)
{
ProcessClick(sender, e);
}
private void ButtonC_Click(object sender, EventArgs e)
{
ProcessClick(sender, e);
}
}
I've basically added two methods to handle your method. One to identify the button and get its related values from a dictionary that you will have to populate with your Buttons class. and one to carry out the logic.
EDIT
As requested in the comments here's an easy (but not the only way) to point your event listeners towards the same method.
Initially you need to set up with one button and double click it or do some other way to create the forms Button_Click() Event Method. At this point an event listener delegate has been added to your Form.Designer.cs File. Open that file and you will see something like this:
//
// ButtonA
//
this.ButtonA.Location = new System.Drawing.Point(12, 12);
this.ButtonA.Name = "ButtonA";
this.ButtonA.Size = new System.Drawing.Size(75, 23);
this.ButtonA.TabIndex = 0;
this.ButtonA.Text = "button1";
this.ButtonA.UseVisualStyleBackColor = true;
this.ButtonA.Click += new System.EventHandler(this.ButtonA_Click);
What you need to do is create your other buttons and add this code in for them with a change to the last line new System.EventHandler(this.ButtonA_Click); This line basically states which method to call when the ButtonA.Click event is invoked. At this point you can add what ever method you want (as long as you name is nicely for good convention). So your example would be this :
//
// ButtonA
//
this.ButtonA.Location = new System.Drawing.Point(12, 12);
this.ButtonA.Name = "ButtonA";
this.ButtonA.Size = new System.Drawing.Size(75, 23);
this.ButtonA.TabIndex = 0;
this.ButtonA.Text = "button1";
this.ButtonA.UseVisualStyleBackColor = true;
this.ButtonA.Click += new System.EventHandler(this.ProcessClick);
//
// ButtonB
//
this.ButtonB.Location = new System.Drawing.Point(12, 12);
this.ButtonB.Name = "ButtonB";
this.ButtonB.Size = new System.Drawing.Size(75, 23);
this.ButtonB.TabIndex = 0;
this.ButtonB.Text = "B";
this.ButtonB.UseVisualStyleBackColor = true;
this.ButtonB.Click += new System.EventHandler(this.ProcessClick);
//
// ButtonC
//
this.ButtonC.Location = new System.Drawing.Point(12, 12);
this.ButtonC.Name = "ButtonC";
this.ButtonC.Size = new System.Drawing.Size(75, 23);
this.ButtonC.TabIndex = 0;
this.ButtonC.Text = "C";
this.ButtonC.UseVisualStyleBackColor = true;
this.ButtonC.Click += new System.EventHandler(this.ProcessClick);
Remember you need to physically create the buttons on the form itself.
Lets say you have three labels and a panel for each label. You can add the event handler to all of them and whenever that event fires the event handler will use that label as the sender. To keep the panel associated with the label, you could add the panel to the label's tag property. Then, in the event handler you can then get the panel from the label.
label1.Click += label_Click;
label2.Click += label_Click;
label3.Click += label_Click;
label1.Tag = panel1;
label2.Tag = panel2;
label3.Tag = panel3;
In the event handler, just cast sender to Label and there you have your label object to do whatever you want with and like I said, the panel is in the Tag property. I did a little refactoring to your code to make it cleaner looking.
private void label_Click(object sender, EventArgs e)
{
// this is what itsme86 was suggesting in the comments
var label = (Label)sender;
var panel = (Panel)label.Tag;
label.BackColor = tiger.Hawk ? Color.DarkSeaGreen : Color.PaleGreen;
label.Text = tiger.Hawk ? "2" : "ON";
if (myport.IsOpen)
send(new byte[] { 16, 128, 32, 8, 1 });
tiger.Hawk = !tiger.Hawk;
}
Let me know if you have any questions about this.

C# - Passing custom arguments to events

So I was trying to send custom arguments to an event, but it never worked, I tried so many different methods, but I never got it to work,
So basically!
public void CreateEmojiList()
{
CreateAllEmojis();
int btnCount = 0;
foreach(Emoji emoji in emojiList)
{
Button btnEmoji = new Button();
btnEmoji.Size = new Size(40, 36);
btnEmoji.FlatStyle = FlatStyle.Flat;
btnEmoji.FlatAppearance.MouseDownBackColor = Color.Cyan;
btnEmoji.Cursor = Cursors.Hand;
btnEmoji.Font = new Font("Bahnschrift", 6.75f);
btnEmoji.Text = emoji.EmojiText;
btnEmoji.Top = (panel_main.Controls.OfType<Button>().Count<Button>() / 4) * (1 + btnEmoji.Height) + 6;
btnEmoji.Left = (btnEmoji.Width + 1) * btnCount + 6;
panel_main.Controls.Add(btnEmoji);
btnEmoji.Click += //What do I do here?
; btnCount++;
if (btnCount == 4)
btnCount = 0;
}
}
protected virtual void OnEmojiClick(EmojiClickEventArgs e)
{
if (this.EmojiClick != null)
EmojiClick(e);
}
this is the class I want to use to pass my arguments:
public class EmojiClickEventArgs : EventArgs
{
private string emojiText;
private string emojiName;
public EmojiClickEventArgs(string EmojiText, string EmojiName)
{
this.EmojiText = EmojiText;
this.EmojiName = EmojiName;
}
public string EmojiText { get { return emojiText; } set { emojiText = value; } }
public string EmojiName { get { return emojiName; } set { emojiName = value; } }
}
I want to get those two values from
emoji.EmojiText and emoji.EmojiName
You can take advantage of closures to "package up" the additional event data for each button's event handler. Just make sure not to close over the loop variable.
public void CreateEmojiList()
{
CreateAllEmojis();
int btnCount = 0;
foreach(Emoji emoji in emojiList)
{
Button btnEmoji = new Button();
btnEmoji.Size = new Size(40, 36);
btnEmoji.FlatStyle = FlatStyle.Flat;
btnEmoji.FlatAppearance.MouseDownBackColor = Color.Cyan;
btnEmoji.Cursor = Cursors.Hand;
btnEmoji.Font = new Font("Bahnschrift", 6.75f);
btnEmoji.Text = emoji.EmojiText;
btnEmoji.Top = (panel_main.Controls.OfType<Button>().Count<Button>() / 4) * (1 + btnEmoji.Height) + 6;
btnEmoji.Left = (btnEmoji.Width + 1) * btnCount + 6;
panel_main.Controls.Add(btnEmoji);
var emojiCopy = emoji; //don't close on the loop variable!
btnEmoji.Click += (sender,args) => OnEmojiClick(emojiCopy);
btnCount++;
if (btnCount == 4)
btnCount = 0;
}
}
protected virtual void OnEmojiClick(Emoji emoji)
{
//do something
}
One way is to inherit from Button and create a class called EmojiButton. You then declare a delegate that matches the signature of the your event handler. After that, declare an event using the delegate in the EmojiButton class, add property like EmojiText and EmojiName to the button subclass as well. Finally you need to link the button click event with your custom event. Whenever the button is clicked, raise your event and pass your arguments i.e. this.EmojiText, this.EmojiName.
Another way is to assign your Emoji objects to the Tag property. You can then write the event handler with the normal EventHandler signature (object sender, EventArgs e), and look at what the sender's Tag is. You then cast the Tag to an Emoji and access its properties.
The fastest solution that comes to my mind and that doesn't imply the definition of custom UserControl classes, the subclassing of Button and other similar practices is:
btnEmoji.Click += (sender, e) =>
{
Button b = (Button)sender;
// let's suppose that the button name corresponds to the emoji name
String emojiName = b.Name;
// let's suppose that the button tag contains the emoji text
String emojiText = (String)b.Tag;
Emoji_Clicked(sender, e, (new EmojiClickEventArgs(emojiText, emojiName)));
};
private void Emoji_Clicked(Object sender, EventArgs e, EmojiClickEventArgs ee)
{
// Your code...
}
First and foremost thing you need to define delegate ,then create an instance.
class Emojis
{
// public delegate void EmojiClickEventHandler(object sender,EventArgs args);
//public event EmojiEventHandler EmojiClicked;
//you can use above two lines or replace them instead below code.
public event EventHandler<EmojiClickEventArgs> EmojiClicked;
public void CreateEmojiList()
{
CreateAllEmojis();
int btnCount = 0;
//rest of the code
panel_main.Controls.Add(btnEmoji);
btnEmoji.Click += OnEmojiClick(btnEmoji);
btnCount++;
}
protected virtual void OnEmojiClick(Button emoji)
{
//Here null check to handle if no subscribers for the event
if(EmojiClicked!=null)
{
//there is no name property define for emoji but only text hence passing only text.
EmojiClicked(this ,new EmojiClickEventArgs(emoji.Text,emoji.Text){ });
}
}
private void Emoji_Clicked(Object sender, EmojiClickEventArgs args)
{
Button mybutton = sender as Button;
Console.WriteLine("emoji text "+ args.Text);
}
}

C# Raise Event from List of Controls

WARNING: Embedded software delevoper trying to build PC software!
I'm trying to interface a piece of hardware that communicates with the PC via serial interface. The PC software (C#) periodicaly sends a byte array, which I would like to adjust using some trackbars.
Instead of adding 8 trackbars on the design view, I add one to help me align it and then I create a List which I populate on load like so:
public partial class FormDmxTemplate : Form
{
// Controls
// Create a list of tracbars.
List<TrackBar> trackBarDmx = new List<TrackBar>();
public FormDmxTemplate()
{
InitializeComponent();
}
private void FormDmxTemplate_Load(object sender, EventArgs e)
{
// Add first instance on the list
trackBarDmx.Add(trackBarDmx1);
// Generate 7 more, 8 total, of each
// Copy settings, and place them next to each other
for (int i = 1; i < 8; i++)
{
// Trackbars
trackBarDmx.Add(new TrackBar());
trackBarDmx[i].TickStyle = trackBarDmx[0].TickStyle;
trackBarDmx[i].Orientation = trackBarDmx[0].Orientation;
trackBarDmx[i].Minimum = trackBarDmx[0].Minimum;
trackBarDmx[i].Maximum = trackBarDmx[0].Maximum;
trackBarDmx[i].Size = new System.Drawing.Size(trackBarDmx[0].Size.Width, trackBarDmx[0].Size.Height);
trackBarDmx[i].Location = new System.Drawing.Point(trackBarDmx[i-1].Location.X + 60, trackBarDmx[0].Location.Y);
this.Controls.Add(trackBarDmx[i]);
}
}
}
Is it possible to have events for all the List members like this one?
private void trackBarDmx1_Scroll(object sender, EventArgs e)
{
}
Which means I'd like update the relevant byte in my byte array to match the TrackBar value, using events if possible.
NOTE: This is a form template which I load and close via another form.
You can subscribe to the events when creating the TrackBars. All can have the same event handler:
trackBarDmx[i].Scroll += trackBarDmx1_Scroll;
Then in the handler you can find out which is this TrackBar and at which index it is (if necessary)
private void trackBarDmx1_Scroll(object sender, EventArgs e)
{
TrackBar bar = sender as TrackBar;
int trackBarIndex = this.trackBarDmx.IndexOf(bar);
}
Not sure if I understood what you are trying to achieve. Does this do what you need:
for (int i = 0; i < 7; i++)
{
TrackBar trackBar = new TrackBar();
trackBar.Tag = i;
// Other properties
trackBar.Scroll += new EventHandler(trackBar_Scroll);
}
In the handler:
void trackBar_Scroll(object sender, EventArgs e)
{
// Get the trackbar
TrackBar current = sender as TrackBar;
// Do something here. Use tag property to identify which byte array should be changed
}
BTW, do you really need to retain the list of TrackBar?
You can do this :
for (int i = 1; i < 8; i++)
{
// Trackbars
trackBarDmx.Add(new TrackBar());
trackBarDmx[i].TickStyle = trackBarDmx[0].TickStyle;
trackBarDmx[i].Orientation = trackBarDmx[0].Orientation;
trackBarDmx[i].Minimum = trackBarDmx[0].Minimum;
trackBarDmx[i].Maximum = trackBarDmx[0].Maximum;
trackBarDmx[i].Size = new System.Drawing.Size(trackBarDmx[0].Size.Width, trackBarDmx[0].Size.Height);
trackBarDmx[i].Location = new System.Drawing.Point(trackBarDmx[i-1].Location.X + 60, trackBarDmx[0].Location.Y);
this.Controls.Add(trackBarDmx[i]);
// Notice no number in the handler name
trackBarDmx[i].Scroll += trackBarDmx_Scroll;
}
Now in the handler the simplest thing to do would be :
private void trackBarDmx_Scroll(object sender, EventArgs e)
{
var tb = sender as TrackBar;
if(sender == null)
{return;}
switch (sender.Name)
{
case "trackBarDmx1_Scroll" :
// handle changes to bar 1
break;
// and so on
}
}

Categories

Resources