Sorry, this may be a duplicate question, but I couldnot understand the solutions already provided in different answers.
I have created a mp3 player in a different manner, it plays one mp3 file at a time but one listbox have the chapters, which is not only handling to move position of that particular mp3 but also changes a picturebox image. Now somewhere I need to change the selection of the listbox from a seekbar but dont want to fire the following event of;
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
Please guide.
One way to inhibit your selection indexed change event doing its normal way is to use a boolean flag. Also, make sure that this inhibition does not stay around when some exception is raised:
private bool inhibit = true;
private void doSomeProcessWithInhibit()
{
try
{
inhibit = true;
// processing comes here
}
// if something goes wrong, make sure other functionality is not blocked
finally
{
inhibit = false;
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
// fast return to reduce nesting
if (inhibit)
return;
// do event handling stuff here
}
P.S. Try to use meaningful names for controls (check listBox1). You will thank yourself when revisiting the code and/or others have to.
Add a Boolean with class scope called something like isProcessing. Set it to true. Do your work, then set it to false. Warp your event in the Boolean:
bool isProcessing = true;
private void switchControls(){
isProcessing = true;
//do work;
isProcessing = false;
}
private void MyControl.OnEvent(object sender, EventArgs e){
if(!isProcessing){
//what you would normally do
}
}
OR....
Deregister the event, the re-register it
private void switchControls(){
myButton1.OnClick -= myButtonClick;
//do work
myButton1.OnClick += myButtonClick;
}
Related
I've been trying so many different ways to try and trigger events based on clicks within my datagridview.
First I'd like to put up a basic example from MDN, then I'd like to put something I am using for another click event that does work, and hopefully someone can explain what I'm doing wrong and why one way is working versus another not working.
public event DataGridViewCellMouseEventHandler CellMouseClick;
private void DataGridView1_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
System.Text.StringBuilder cellInformation = new System.Text.StringBuilder();
cellInformation.AppendFormat("{0} = {1}", "ColumnIndex", e.ColumnIndex);
cellInformation.AppendLine();
cellInformation.AppendFormat("{0} = {1}", "RowIndex", e.RowIndex);
cellInformation.AppendLine();
MessageBox.Show(cellInformation.ToString(), "CellMouseClick Event");
}
Mind you, I have also tried removing this public event call as well. Additionally, I get a tooltip that shows up on the CellMouseClick portion of the public event call that says I never use the CellMouseClick item.
For another Mouse Click event I wanted to detect, the below managed to work, but it seems to take more to get it to work and the above seems like it's supposed to work so seamlessly, so I'd prefer to get the above to work as it is intended.
Here is the working version.
public Form1()
{
InitializeComponent();
this.dataGridView1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.dataGridView_MouseDown);
this.toolStripMenuItem1.Click += new System.EventHandler(this.toolStripMenuStrip1_Click);
}
private void dataGridView_MouseDown(object sender, MouseEventArgs e)
{
var hti = dataGridView1.HitTest(e.X, e.Y);
if (e.Button == MouseButtons.Right)
{
try
{
dataGridView1.ClearSelection();
dataGridView1.Rows[hti.RowIndex].Selected = true;
this.dataGridView1.CurrentCell = this.dataGridView1.Rows[hti.RowIndex].Cells[1];
this.contextMenuStrip1.Show(this.dataGridView1, e.Location);
contextMenuStrip1.Show(Cursor.Position);
}
catch (Exception)
{
}
}
}
the above code works. I tried it. Since i do not have the full code, it is hard to know exactly where goes wrong.
one thing to note is, the catch(exception) does not really do anything, which cause any exceptions just pass without notice. You may have had some exceptions. Try to print out exception information or handle any exception gracefully.
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Well, after some time looking around, I managed to find the actual answer.
I don't quite remember where I found it, but what I was missing was this call:
dataGridView1.CellMouseClick += dataGridView1_CellMouseClick;
the MSDN website seemed to indicate that this was the call to be made before the event handler:
public event DataGridViewCellMouseEventHandler CellMouseClick;
The ladder of these 2 does not work. If the above person who tried to answer this did get it to work, I would imagine the user added something they knew to add due to experience and probably assumed I was doing it. So, just for clarity here is the end product that causes the event to work:
public Form1()
{
InitializeComponent();
dataGridView1.CellMouseClick += dataGridView1_CellMouseClick;
}
private void dataGridView1_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
//whatever you want to happen when the mouse is clicked in a cell.
}
I have a form with two text fields, A and B that are supposed to behave in the following way:
Typing something into A should set B.Text = f(A.Text)
Typing something into B should set A.Text = g(B.Text)
...for some arbitrary and potentially unrelated functions f and g.
The problem I'm facing is that the naive implementation of simply throwing the above code into each field's handler will create an infinite loop as A's handler will update B's value and call B's handler, which will update A, etc.
What would be the correct (and preferably thread-safe) way to handle this? Either somehow determining whether a change was done manually or programmatically, somehow suppressing events firing when changing the value, or some other way.
Use a flag to signal that you are doing changes
private bool updating;
private void A_TextChanged(object sender, EventArgs e)
{
if (!updating) {
updating = true;
B.Text = f(A.Text);
updating = false;
}
}
private void B_TextChanged(object sender, EventArgs e)
{
if (!updating) {
updating = true;
A.Text = g(B.Text);
updating = false;
}
}
You don't have to care about thread-safety as this all happens in the unique UI-thread. UI events never create new threads; i.e. an event (click, text changed, etc.) never interrupts another one!
If you want to be sure that the flag is reset, you can use the try-finally statement. The finally block is ensured to run, even if an exception should occur within the try block (unless the application is terminated unexpectedly).
if (!updating) {
updating = true;
try {
A.Text = f(B.Text);
} finally {
updating = false;
}
}
I assume you're using TextChanged event, try this then:
private bool callB=true;
private bool callA=false;
private void A_Click(object sender, System.EventArgs e)
{
callB=true;
callA=false;
}
private void B_Click(object sender, System.EventArgs e)
{
callB=false;
callA=true;
}
private void A_textchanged(object sender, RoutedEventArgs e)
{
if(callB)
B.text=f(A.text);
}
private void B_textchanged(object sender, RoutedEventArgs e)
{
if(callA)
A.text=g(B.text);
}
Anyway, a better way to just edit A when the user is finished with B(finished whatever he wanted to write in it), that's because if expression will be evaluated at every character the user inputs.
By the way, changing a text while the user writes might be surprising to him, so better to avoid textchanged event in this case.
I was wondering about this problem for a while, but couldn't really come up with a solution. I have 2 different event handlers calling each other recursively. As soon as event A is fired, it triggers event B which triggers event A again and so on...
Basically I want to be able to select text in a RichTextBox and show the corresponding font size in a combo box. When I choose a different font size from the ComboBox, I want it's value to be applied to the selected text.
The 2 events are:
1) The selection changed event of text inside a RichTextBox:
private void MyRTB_SelectionChanged(object sender, RoutedEventArgs e)
{
//Get the font size of selected text and select the concurrent size from the ComboBox.
}
2) The selected index changed event of a Combobox:
private void CmbFont_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//Apply the chosen font size to the currently selected text of the RichTextBox.
}
What would be the best solution to make sure they each only "do their thing" and do not fire the other event in doing so?
Sometimes changing a property of a control in code fires an event unintentionally. Changing the data source of a ListBox or a ComboBox will fire the SelectedIndexChanged event, for example. Use a flag to handle this case
private bool _loading;
...
_loading = true;
// Fill the ComboBox or ListView here
_loading = false;
In the event handler do this
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (_loading) return;
...
}
Refactor your code so that A calls DoSomethingA() and B calls DoSomethingB(). This way, if you want A to do the functionality of B you can just call DoSomethingB() and not have any recursive calls.
Just use a bool (maybe called dontFireA) and set it in A just before calling B
notifying properties (used in order to enable binding from WPF to non-WPF properies) use this technique:
public object MyProperty
{
get
{
return myField;
}
set
{
if (value != myField)
{
myField = value;
NotifyProperyChanged("MyProperty"); // raise event
}
}
}
The if (value != myField) condition prevents infinite recursion (stackoverflowexception).
In some cases (e.g. floating point numbers and inaccurate value transfers) if (Math.Abs(value - myField) > someConstant) is used instead to break the recursion.
Could you apply a similar technique to your problem?
If both events are on the same object or the owners have references to each other, you could also store a flag on each e.g.
private void OnEvent()
{
DoSomething();
}
private void DoSomething()
{
this.IsBusy = true;
// do work
// raise event
if (!other.IsBusy)
RaiseEvent();
}
I am going to make the educated guess that you are not raising Event A or Event B yourself; let's say Event A is the TextBox1.TextChanged event and Event B is the TextBox2.TextChanged event, and they have handlers like:
public void Textbox1_TextChanged(object sender, EventArgs e)
{
...
TextBox2.Text = someString;
}
public void Textbox2_TextChanged(object sender, EventArgs e)
{
...
TextBox1.Text = someOtherString;
}
In this case, the handlers are each going to raise the other textbox's TextChanged event by virtue of changing the text, leading to infinite recursion.
The first thing you can do, if you want both to run once and once only, is to mark that they're already running (changing the text of the other textbox results in that textbox's event handler running within the same call stack:
public void Textbox1_TextChanged(object sender, EventArgs e)
{
if(handler1Running) return; //the second time through we exit immediately
handler1Running = true;
...
TextBox2.Text = "Something"; //the other event handler is invoked immediately
handler1Running = false;
}
public void Textbox2_TextChanged(object sender, EventArgs e)
{
if(handler2Running) return; //the second time through we exit immediately
handler2Running = true;
...
TextBox1.Text = "Something Else"; //the other event handler is invoked immediately
handler2Running = false;
}
Now, the deepest it will go is three levels; 1's handler invokes 2's handler which invokes 1's handler again, which sees that 1's handler is already running and quits before doing anything that would deepen the recursion. Same thing if you start by changing TextBox2.
The other thing you can do is make sure you aren't trying to set the textbox to the same value that's already there. Changing from one string reference to another, even if both references are the same string value, will fire the TextChanged event. If the recursion must continue naturally but will reach a steady state, this is actually the first thing to try:
public void Textbox1_TextChanged(object sender, EventArgs e)
{
StringBuilder builder = new StringBuilder();
... //build string
//now, even though the builder's ToString will produce a different reference,
//we're making sure we don't unnecessarily change the text.
if(builder.ToString != TextBox2.Text)
TextBox2.Text = builder.ToString();
}
public void Textbox2_TextChanged(object sender, EventArgs e)
{
StringBuilder builder = new StringBuilder();
... //build string
//now, even though the builder's ToString will produce a different reference,
//we're making sure we don't unnecessarily change the text.
if(builder.ToString != TextBox1.Text)
TextBox1.Text = builder.ToString();
}
I'm new to C# and Windows Form but if I have a radiobutton and I call radiobutton1.Checked=true, is there a way for it to not fire the CheckedChange event? I want to distinguish between the user clicking on the radiobutton and me setting the radiobutton programmatically. Is this possible?
Stop trying to defeat the design of the CheckedChanged event. It's specifically supposed to include programmatic changes.
If you want user-triggered changes and not programmatic changes, use the Click event instead. (You may be thinking that you don't want to restrict yourself to mouse clicks, don't worry, there's a MouseClick event for that, Click includes keyboard changes as well.)
Here's a straightforward method of using the event when you feel like it.
private bool SuppressRadioButton1Event { get; set; }
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
if (!this.SuppressRadioButton1Event)
{
MessageBox.Show("Not suppressed!");
}
}
private void button1_Click(object sender, EventArgs e)
{
this.SetRadioButton1(false);
}
private void SetRadioButton1(bool checkedOn)
{
this.SuppressRadioButton1Event = true;
radioButton1.Checked = checkedOn;
this.SuppressRadioButton1Event = false;
}
A very easy way:
public void radio_OnCheckChanged(object sender, EventArgs e)
{
RadioButton r = sender as RadioButton;
bool isUserChange = r.Tag.Equals(1);
if (isUserChange) blabla
else blabla
r.Tag = null;
}
public void MyMethod()
{
radio1.Tag = 1;
radio.Checked = true;
}
You can use any kind of flag which users can't do by their clicking.But you can do via your code.
Why should your code care who checked the radiobutton?
EDIT: There are ways around this (subclass, flag), but don't. The only "legit" reason I can think of for wanting this is to prevent some side-effect from happening when the value is initially (programatically) displayed, and even that is suspect. Rethink the side-effect, does it really belong on the change-event, or the commit?
More info one why/what would help. On the surface, this looks like a design error.
One (hackish) way to do it would be to subclass RadioButton and override the OnCheckChanged virtual method, suppressing the event if the Checked property has been set programmatically.
However, since radio-buttons belong to a group, the event always fires in pairs (oen for the uncheck, one for the check). You will therefore want to suppress the event for the entire group when you choose the selected button programmatically. Here's an example implementation:
public class CustomRadioButton : RadioButton
{
private bool _suppressCheckedEvent;
public void SetChecked(bool value, bool suppressCheckedEvent)
{
if (!suppressCheckedEvent)
Checked = value;
else
{
SetSupressModeForGroup(true);
Checked = value;
SetSupressModeForGroup(false);
}
}
private void SetSupressModeForGroup(bool suppressCheckedEvent)
{
foreach (var crb in Parent.Controls.OfType<CustomRadioButton>())
crb._suppressCheckedEvent = suppressCheckedEvent;
}
protected override void OnCheckedChanged(EventArgs e)
{
if (!_suppressCheckedEvent)
base.OnCheckedChanged(e);
}
}
In this implementation, changing the checked-state through the Checked property will always fire the event. When you call the SetChecked method, you have the choice to suppress the event.
You could try to attach the event programmatically. Based on my application configuration I check several radio buttons but I don't want to fire events.
To attach an event programmatically:
chbOptionX.CheckedChanged += new System.EventHandler(this.chbShowStockBySizeAndColor_CheckedChanged);
I would like to use the following code in C# but I just can't seem to get out of it. I would like to terminate the application if the user presses a key or moves the rodent (aka mouse). Here is my code (no laughing!).
private void frmDots_KeyDown(object sender, KeyEventArgs e)
{
bgNotClicked = false;
Close();
}
private void frmDots_Click(object sender, EventArgs e)
{
bgNotClicked = false;
Close();
}
while (bgNotClicked)
{
// Clear the first element in our XY position. This is the reverse of the way I normally create the dots application
System.Drawing.Rectangle clearDots = new System.Drawing.Rectangle(Dots.PositionX[iCounter], Dots.PositionY[iCounter], 8, 8);
// Create the black color and brush to clear dots
Color clearDotsColor = Color.Black;
SolidBrush clearDotsBrush = new SolidBrush(clearDotsColor);
// Finally clear the dot
e.Graphics.FillEllipse(clearDotsBrush, clearDots);
GetRandomPosition(iCounter);
// Fill the elements to display colors on the displays canvas
System.Drawing.Rectangle colorDots = new System.Drawing.Rectangle(Dots.PositionX[iCounter], Dots.PositionY[iCounter], 8, 8);
// Create the color and brush to show dots
Color colorRandom = GetRandomColor();
SolidBrush colorBrush = new SolidBrush(colorRandom);
// Finally show the dot
e.Graphics.FillEllipse(colorBrush, colorDots);
Thread.Sleep(5);
iCounter++;
if (iCounter == 399)
{
iCounter = 0;
}
}
}
Your "busy waiting" strategy is poor design. Instead, you should use event handlers that are fired:
On keypress.
When the mouse is moved.
In either case, you can respond by terminating the application.
Edit:
After seeing your edit, this is definitely your problem. The issue is that your while loop blocks the main UI thread, so it never handles the Windows Messages which trigger your key press/mouse/etc handlers.
You have a couple of options - you can either move some of this onto a separate thread, do what I suggested below, or add a call to Application.DoEvents in your while loop. This would allow your event handlers to run, which would in turn set bgNotClicked = false;. Right now, that's never occurring because your UI is blocked entirely.
Original Post:
If you're doing this loop in your UI thread, you're going to need to rethink the design a bit.
Setting bgNotClicked = false; somewhere in an event handler will work, but only if your event handler is able to run. If you're doing the above code in the UI thread, it will block your UI thread indefinitely, preventing the event handler from firing.
I would recommend reworking this to be based off a timer (so it runs repeatedly on regular intervals), instead of locked into a while loop. This would allow your other UI events to fire between runs, and instead of setting bgNotClicked = false;, your event handler could just set the timer to be not enabled.
Your bgNotClicked variable needs to be set to false by your event handler for key-press.
If the rodent is moved by your mouse, you would need a similar mouse event handler.
The break keyword will terminate a loop. In this case, when you hit the case where you want to stop the loop, you would just use break;.
If you're looping like that you need to give the application a moment to process the events that you're hoping will cause the interruption. This is the job of the DoEvents method.
private bool WeAreDone = false;
private void DoingIt()
{
while (true)
{
Application.DoEvents();
if (WeAreDone)
{
break;
}
}
}
private void InterruptButton_Click(object sender, EventArgs e)
{
WeAreDone = true;
}
I think using a Timer fits the Windows event-driven model better than the busy wait while loop. You might try something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int iCounter = 0;
private void Draw()
{
// ....
}
private void timer1_Tick(object sender, EventArgs e)
{
Draw();
iCounter++;
if(iCounter == 399)
{
iCounter = 0;
}
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 5;
timer1.Enabled = true;
}
private void Form1_Click(object sender, EventArgs e)
{
timer1.Enabled = false;
Close();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
timer1.Enabled = false;
Close();
}
}
This does not seems to be the correct way. .Net Framework has provided you with the events to handle the KeyPress and MouseMove/Click actions. Why are you not using them?
Try moving the loop into a BackgroundWorker's DoWork event handler. Then your GUI will still be responsive and instead of that nasty variable, you can just call the CancelAsync method to stop the loop.
You can exit the loop using the break statement.
EDIT: OK, I take back the flag thing!
Use Environment.Exit(2); (or Environment.Exit(1) it really doesn't make a difference) to exit out of the application.
Exit While
...................