C# Windows Forms, Combobox MouseClick event getting fired multiple times - c#

I have a windows form with many controls. A tiny part of it is logging in to an SQL server and fetching the list of database names and assigning the collection to a combobox.
private void InitializeComponent()
{
//...
//...
this.ServerTB = new System.Windows.Forms.TextBox();
this.UserNameTB = new System.Windows.Forms.TextBox();
this.PasswordTB = new System.Windows.Forms.TextBox();
//...
//...
this.ServerTB.TextChanged += new System.EventHandler(this.OnSQLServerChanged);
this.UserNameTB.TextChanged += new System.EventHandler(this.OnSQLServerChanged);
this.PasswordTB.TextChanged += new System.EventHandler(this.OnSQLServerChanged);
this.DatabaseCmbBox = new System.Windows.Forms.ComboBox();
//...
//...
this.DatabaseCmbBox.MouseClick += new System.Windows.Forms.MouseEventHandler(this.DatabaseCmbBox_Click);
//....
}
private void DatabaseCmbBox_Click(object sender, MouseEventArgs e)
{
MessageBox.Show(sender.GetType().ToString());
this.Cursor = Cursors.IBeam;
List<string> dbList = new List<string>();
dbList = GetDatabaseList();
DatabaseCmbBox.DataSource = dbList;
DatabaseCmbBox.SelectedIndex = -1;
if (dbList.Count > 0)
{
DatabaseCmbBox.MouseClick -= DatabaseCmbBox_Click;
}
DatabaseCmbBox.Focus();
this.Cursor = Cursors.Default;
}
protected List<string> GetDatabaseList()
{
List<string> list = new List<string>();
string conString = "server=" + ServerTB.Text + ";uid=" + UserNameTB.Text + ";pwd=" + PasswordTB.Text + "; database=master";
try
{
using (SqlConnection con = new SqlConnection(conString))
{
con.Open();
using (SqlCommand cmd = new SqlCommand("select name from sys.databases where name not in ('master', 'model', 'tempdb', 'msdb') ", con))
{
using (IDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
list.Add(dr[0].ToString());
}
dr.Close();
}
cmd.Dispose();
}
con.Close();
}
}
catch(SqlException ex)
{
DatabaseCmbBox.MouseClick -= DatabaseCmbBox_Click;
MessageBox.Show(ex.Message);
ServerTB.Focus();
}
return list;
}
private void OnSQLServerChanged(object sender, EventArgs e)
{
DatabaseCmbBox.MouseClick += DatabaseCmbBox_Click;
}
I don't have a problem in fetching the list of Databases. It takes about 10 seconds to do so. With the messagebox in the event handler, I found out that the MouseClick event, for no apparent reason, gets fired like 38 times. The same thing happens even if I use the Enter event or Click event, instead of the MouseClick event.
Why does this happen, and how does one go about using these kind of events?
Thanks in advance,
RPS.

If you want to get list of Databases, why don't you do so on Combobox value changed event?
Anyways,I see that you are doing
private void OnSQLServerChanged(object sender, EventArgs e)
{
DatabaseCmbBox.MouseClick += DatabaseCmbBox_Click;
}
This should solve your problem
private void OnSQLServerChanged(object sender, EventArgs e)
{
DatabaseCmbBox.MouseClick -= DatabaseCmbBox_Click;
DatabaseCmbBox.MouseClick += DatabaseCmbBox_Click;
}

This is a potential error: you should subscribe DatabaseCmbBox.MouseClick only once, not every time OnSQLServerChanged. Correct your code correspondingly, and the issue with multiple click events will be fixed:
private void OnSQLServerChanged(object sender, EventArgs e)
{
DatabaseCmbBox.MouseClick += DatabaseCmbBox_Click;
}

Related

how to call timer in UC Control in Form Main?

Timer1 not start when add new User Control.I have 1 User Control (UC_Machine).And when load Form Main I using timer execute SQL query in query variable to get data from table [tbl_FF_Trigger]
I have 1 User Control (UC_Machine).And when load Form Main I using timer execute SQL query in query variable to get data from table [tbl_FF_Trigger]
-Code in UC_Machine:
private void UC_Machine_Load(object sender, EventArgs e)
{
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
string query = "SELECT CRDName, PartNumber ,DefectName FROM [tbl_FF_Trigger] where Machine = '" + Machine + "'";
db.fillDataGridView(query, dataGridView1);
dataGridView1.BackgroundColor = Color.White;
dataGridView1.RowHeadersVisible = false;
dataGridView1.Update();
dataGridView1.Refresh();
}
Code in form MAIN. Will get the information of the machines from the function Machine_Infor.GetMachine_Infors(). It will then create a UC_Machine array from that information. If the machine in the array does not already exist in the panellayout, it will be added to the panellayout.
In addition, it also checks if the machine exists in the tbl_FF_Trigger table. Otherwise it will delete the machine from the panellayout.
private void Main_Load(object sender, EventArgs e)
{
Get_Infor();
}
private void Get_Infor()
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 3000; // Thời gian chạy (5000 milliseconds = 5 second)
timer.Elapsed += Timer_Elapsed;
timer.Start();
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
// Add new item to panellayout if machine have falsecall
var data = Machine_Infor.GetMachine_Infors();
var list = new UC_Machine[data.Count];
int i = 0;
itemFoods = new List<UC_Machine>();
itemFoodsFilter = new List<UC_Machine>();
HashSet<string> existingMachine = new HashSet<string>();
foreach (var item in data)
{
if (!existingMachine.Contains(item.Machine))
{
bool isExisting = false;
foreach (UC_Machine uc in myFlowLayoutPanel1.Controls)
{
if (uc.Machine == item.Machine)
{
isExisting = true;
break;
}
}
if (!isExisting)
{
list[i] = new UC_Machine();
list[i].CRDName = item.CRDName;
list[i].Model = item.Model;
list[i].Machine = item.Machine;
list[i].PartNumber = item.PartNumber;
list[i].Workcell = item.Workcell;
list[i].CRDName = item.CRDName;
list[i].Date = item.Date;
itemFoods.Add(list[i]);
itemFoodsFilter.Add(list[i]);
existingMachine.Add(item.Machine);
i++;
}
}
}
if (myFlowLayoutPanel1.InvokeRequired)
{
myFlowLayoutPanel1.Invoke((MethodInvoker)delegate
{
myFlowLayoutPanel1.Controls.AddRange(list.Where(x => x != null).ToArray());
});
}
else
{
myFlowLayoutPanel1.Controls.AddRange(list.Where(x => x != null).ToArray());
}
// Remove machine if Falsecall not found
string maincon = ConfigurationManager.ConnectionStrings["Connstring"].ConnectionString;
// Connect to the SQL Server database and execute the query to check for the existence of "vnhcmsleaoi05" in the "tbl_FF_Trigger" table.
using (SqlConnection connection = new SqlConnection(maincon))
{
connection.Open();
foreach (UC_Machine uc in myFlowLayoutPanel1.Controls)
{
string query = "SELECT COUNT(*) FROM tbl_FF_Trigger WHERE Machine = '"+ uc.Machine+"'";
using (SqlCommand command = new SqlCommand(query, connection))
{
int count = (int)command.ExecuteScalar();
if (count == 0)
{
if (myFlowLayoutPanel1.InvokeRequired)
{
myFlowLayoutPanel1.Invoke((MethodInvoker)delegate
{
myFlowLayoutPanel1.Controls.Remove(uc);
itemFoods.Remove(uc);
uc.Dispose();
});
}
else
{
myFlowLayoutPanel1.Controls.Remove(uc);
itemFoods.Remove(uc);
uc.Dispose();
}
break;
}
}
}
}
}
Code working OK when load Form. when tbl_FF_Trigger add new value myFlowLayoutPanel1 new UC_Machine
but timer1 not start. I know this because the datagirdview has no data in the newly added UC_Machine.I want when myFlowLayoutPanel1 adds 1 UC_Machine, timer1 will start
Anyone have any advise or solution for this case, please help me. Thanks a lot!
Your question is how to call timer in UC Control in Form Main. The main form is trying to GetInfo from the UC but consider that this might be "backwards" and that the UC Control should be notifying Form Main when it gets the new info instead. Here's what I mean:
Your UC has its own timer1 (we'll get timer1 started don't worry). What main form needs is to be notified when a new query is available in one of the UCs. Giving main form its own timer will interfere with an otherwise-orderly process. Consider making an event in your UC as shown and fire it whenever UC is done getting a new query.
UserControlMachine
Example: UC starts its own timer1 and fires RecordsChanged event when new query completes.
public partial class UserControlMachine : UserControl
{
public UserControlMachine()=>InitializeComponent();
public BindingList<Record> Records { get; } = new BindingList<Record>();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
timer1 = new Timer { Interval = 2500 };
timer1.Tick += onTimer1Tick;
timer1.Start();
dataGridView1.DataSource = Records;
Records.Add(new Record()); // <- Auto-generate columns
dataGridView1.Columns[nameof(Record.DefectName)].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
private System.Windows.Forms.Timer timer1;
private void onTimer1Tick(object sender, EventArgs e) => queryDB();
private void queryDB()
{
// S I M U L A T E D Q U E R Y R E S U L T
Records.Clear();
for (int i = 0; i < Rando4Test.Next(1,5); i++)
{
Records.Add(new Record
{
CRDName = $"CRD-{(char)Rando4Test.Next(65,70)}{Rando4Test.Next(100, 200)}"
});
}
// Notify the Main Form that something has changed.
RecordsChanged?.Invoke(this, EventArgs.Empty);
}
public event EventHandler RecordsChanged;
// For testing purposes
public static Random Rando4Test { get; } = new Random();
}
Main Form
All the main form has to do is listen for RecordsChanged events.
public partial class Form1 : Form
{
public Form1() => InitializeComponent();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// Simulation: Add 2 machines
for (int i = 0; i < 2; i++)
{
var machine = new UserControlMachine
{
Size = new Size(myFlowLayoutPanel1.Width - SystemInformation.VerticalScrollBarWidth, 200),
BorderStyle = BorderStyle.FixedSingle,
};
myFlowLayoutPanel1.Controls.Add(machine);
// Listen for new query results
machine.RecordsChanged += onAnyUCRecordsChanged;
}
}
private void onAnyUCRecordsChanged(object sender, EventArgs e)
{
int totalDefects = 0;
foreach (var machine in myFlowLayoutPanel1.Controls.OfType<UserControlMachine>())
{
totalDefects += machine.Records.Count;
}
Text = $"Main Form - Total Defects: {totalDefects}";
}
}
WHERE Record class represents a DataGridViewRow
public class Record
{
public string PartNumber { get; set; } =
Guid.NewGuid().ToString().Substring(0, 13).ToUpper();
public string CRDName { get; set; }
public string DefectName { get; set; } = "Unknown Error";
}

C#_Timer/Stopwatch unable to continue from time extracted from database

I have below code to start and stop timer/stopwatch and load value into database.
The stopwatch works fine, and time is able to load into database and extract from database into textbox.
the problem is after i extract the time from database and insert into text box, then i start timer, it didnt start from current time. It always start from 0.
there is no error when running the program.
Could anyone help for troubleshoot please? Thanks in advance!
public partial class TestWindow : Window
{
String SNconnectionstring = (#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\SNDB.mdf;Integrated Security=True;");
DispatcherTimer dtSN = new DispatcherTimer();
Stopwatch swSN = new Stopwatch();
string currentTimeSN = string.Empty;
public TestWindow()
{
InitializeComponent();
}
private void SNCreate_Click(object sender, RoutedEventArgs e)
{
using (SqlConnection sqlConSN = new SqlConnection(SNconnectionstring))
{
sqlConSN.Open();
SqlCommand sqlCmdSN = new SqlCommand("SNadd", sqlConSN);
sqlCmdSN.CommandType = System.Data.CommandType.StoredProcedure;
sqlCmdSN.Parameters.AddWithValue("#SN", sNtxtbox.Text);
sqlCmdSN.Parameters.AddWithValue("#Time", SNTime.Text.Trim());
sqlCmdSN.ExecuteNonQuery();
}
private void Open_Click(object sender, RoutedEventArgs e)
{
using (SqlConnection sqlConSN = new
SqlConnection(SNconnectionstring))
{
sqlConSN.Open();
String sqlSelectQuerySN = ("Select * FROM tblSN WHERE SN = #SN");
SqlCommand sqlCmdSNLoad = new SqlCommand(sqlSelectQuerySN, sqlConSN);
sqlCmdSNLoad.Parameters.Add("#SN", System.Data.SqlDbType.VarChar).Value = sNtxtbox.Text;
SqlDataReader drSNReader = sqlCmdSNLoad.ExecuteReader();
if (drSNReader.Read())
{
SNTime.Text = (drSNReader["Time"].ToString());
}
else
{
SNTime.Text = "";
}
}
private void sNbtn_Click(object sender, RoutedEventArgs e)
{
TabControl.SelectedIndex = 1;
dtSN.Tick += new EventHandler(dtSN_Tick);
dtSN.Interval = new TimeSpan(0, 0, 0, 0, 1);
}
void dtSN_Tick(object sender, EventArgs e)
{
if (swSN.IsRunning)
{
TimeSpan tsSN = swVASN.Elapsed;
currentTimeSN = string.Format("{0:00}:{1:00}:{2:00}", tsSN.Hours, tsSN.Minutes, tsSN.Seconds);
txtSN.Text = currentTimeSN;
}
}
private void btnTimeStartSN_Click(object sender, RoutedEventArgs e)
{
swSN.Start();
dtSN.Start();
}
private void btnTimeStopSN_Click(object sender, RoutedEventArgs e)
{
if (swSN.IsRunning)
{
swSN.Stop();
}
}

Refresh DataGridView after INSERT into SQL

I got my form "InsertClient" and I have the button click method
public void Insert_Button_Click(object sender, EventArgs e)
{
MyClass.InsertNewClient(fullNametxt.Text, shortNametxt.Text);
fullNametxt.Clear();
shortNametxt.Clear();
fullNametxt.Focus();
GridView.Update();
GridView.Refresh();
}
My class recieve this:
public static void InsertNewClient (String fullName, String shortName)
{
SqlConnection conn = DBClass.ConnectionString.GetConnection();
SqlCommand cmd = new SqlCommand("MyStoredProcedure", conn);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#fullName", fullName);
cmd.Parameters.AddWithValue("#shortName", shortName);
int i = cmd.ExecuteNonQuery();
if (i == 0)
{
MessageBox.Show("Can't save data");
}
if (i > 0)
{
MessageBox.Show("Data saved!");
}
}
The thing is, the data is saved but that the DataGridView does not refresh after each INSERT (button click). I have to close the form and re-open it and appears refreshed.
Please help, I want the DataGridView refresh after INSERT data
cCUSTOMERBindingSource Is the object of my BindingSource generated using ToolBox.
public void ReloadGrid()
{
Cursor.Current = Cursors.WaitCursor;
cCUSTOMERBindingSource.DataSource = bd.C_CUSTOMER.ToList();
Cursor.Current = Cursors.Default;
}
this where I called the method
private void bunifuThinButton21_Click(object sender, EventArgs e)
{
C_CUSTOMER cst = new C_CUSTOMER();
C_ACCOUNT acc = new C_ACCOUNT();
cst.FIRST_NAME = txtFname.Text;
cst.MIDDLE_NAME = txtMname.Text;
cst.LAST_NAME = txtLname.Text;
cst.LOCATION = txtlocation.Text;
cst.DATE_OF_BIRTH = Convert.ToDateTime(dateTimePicker1.Text);
acc.ACCOUNT_BALANCE = Convert.ToInt32(txtInitAmoun.Text);
acc.ACCOUNT_NUMBER = Convert.ToInt32(txtAccNumber.Text);
cst.TELEPHONE = Convert.ToInt32(txtTelephone.Text);
cst.DATE_CREATE = DateTime.Now;
int newID = cstDao.insertCustomer(cst.FIRST_NAME, cst.MIDDLE_NAME, cst.LAST_NAME, cst.LOCATION, cst.DATE_OF_BIRTH, cst.TELEPHONE, cst.MODIFY_BY, cst.DATE_MODIFY, cst.DATE_CREATE);
acc.CUSTOMER_ID = newID;
acc.DATE_CREATED = DateTime.Now;
acc.CREATED_BY = 1;
int newAccID = cstDao.insertAccount(acc);
if(newID != 0 && newAccID != 0) {
MessageBox.Show("Insert Succefull", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show("Error during the insertion", "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
ReloadGrid();
}
On Form load
private void Form3_Load(object sender, EventArgs e)
{
bd = new Customer_DBEntities();
cCUSTOMERBindingSource.DataSource = bd.C_CUSTOMER.ToList();
}
You should have separate method for example Refresh() which in your case will obtain data from sql using sqlDataReader and after your insertBtn call that method and initialize dataGridView DataSource to it
Something like that #mmking, but not exactly
I already solved it.. the problem was exactly what #Fabio said...
Inside of the button click method I fill again de DataGridView with the DataSet.
public void Insert_Button_Click(object sender, EventArgs e)
{
MyClass.InsertNewClient(fullNametxt.Text, shortNametxt.Text);
this.tblClientTableAdapter.Fill(this.DataSetClient.tblClient);
}
Just to get clear... I use the DataGridView of toolbox, select "Choose data source" and select the table that I want to use to fill DataGrid...
I use the DataSetName that gave me by default and put it right like I show it in the code
Hope it helps for future questions

SqlDependency onchange event infinite loop

I have a simple query, and the event fires at the correct time. However, once fired, the property, .HasChanges, of the SqlDependency object is always set as true.
The first time OnChange is fired, the SqlNotificationEventArgs Info property is "Inserted". The second time the event is fired it's "Already Changed".
I commented all of my code in the OnChange event out to verify that my code wasn't causing the change.
Servicebroker is enabled in the database
Is there a reason the following code causes an infinite loop of onChange events?
static void Main()
{
SqlDependency.Stop(Properties.Settings.Default.DEVConnectionString);
SqlDependency.Start(Properties.Settings.Default.DEVConnectionString);
using (SqlConnection cn = new SqlConnection(Properties.Settings.Default.DEVConnectionString))
{
cn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT UserPageActionLogID, PageActionID FROM dbo.UserPageActionLog WHERE PageActionID != 3 ORDER BY UserPageActionLogID ASC", cn))
{
cmd.Notification = null;
SqlDependency dep = new SqlDependency(cmd);
dep.OnChange += dep_onchange;
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
//Do nothing on first run
}
}
}
}
Application.Run(); //Prevents the application from closing
}
private static void dep_onchange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;
dependency.OnChange -= dep_onchange;
//Do stuff for the function. I commented this out and still had an issue
//Resubscribe to the event to continue catching future changes
dependency.OnChange += dep_onchange;
}
It appears that the both the OnChange handler and the SqlDependency instance are only good for ONE event. After the event is fired and you unsubscribe the handler, you need to register your handler to a NEW SqlDependency object.
Please see the link here for full details: http://msdn.microsoft.com/en-us/library/a52dhwx7(v=vs.80).aspx
Here is how I handled it...
public class ClientClass
{
public ClientClass()
{
var watchForChange = new WatchForChange(connectionString);
watchForChange.TableChanged += WatchForChange_TableChanged;
watchForChange.StartWatching();
}
private void WatchForChange_TableChanged(object sender, EventArgs e)
{
// Some COde
}
}
public class WatchForChange
{
// Should implement IDisposable
private string _connectionString;
private SqlDependency _sqlWatcher;
public WatchForChange(string connectionString)
{
_connectionString = connectionString;
}
public void StartWatching()
{
using (var sqlConnection = new SqlConnection(_connectionString))
{
sqlConnection.Open();
using var sqlCommand = new SqlCommand("select somefield from dbo.sometable", sqlConnection);
{
SqlDependency.Start(_connectionString);
_sqlWatcher = new SqlDependency(sqlCommand);
_sqlWatcher.OnChange += _sqlWatcher_OnChange;
}
// Notifies SQL Server that something is listening for changes to this table
using var sqlDataReader = sqlCommand.ExecuteReader();
}
}
private void _sqlWatcher_OnChange(object sender, SqlNotificationEventArgs e)
{
// Unsubscribe and set to null
_sqlWatcher.OnChange -= _sqlWatcher_OnChange;
_sqlWatcher = null;
SqlNotificationInfo sqlNotificationInfo = e.Info;
// Raise the event on Inserts and Updates
if (sqlNotificationInfo.Equals(SqlNotificationInfo.Insert) || sqlNotificationInfo.Equals(SqlNotificationInfo.Update))
{
OnTableChanged(e);
}
// Create a new instance of _sqlWatcher (SqlDependency)
StartWatching();
}
protected virtual void OnTableChanged(EventArgs e)
{
TableChanged?.Invoke(this, e);
}
public event EventHandler TableChanged;
}

associate button with enter key

i got a WinForm Project in C# 4.0.
i want to when the user click the button enter then it call onclick event of this button.
my code:
public XtraForm_Main()
{
InitializeComponent();
...
this.AcceptButton = (Button)this.Controls["button_Valider"];
}
private void Main_Load(object sender, EventArgs e)
{
this.AcceptButton = (Button)this.Controls["button_Valider"];
}
private void button_Valider_Click(object sender, EventArgs e)
{
try
{
using (var connectionWrapper = new Connexion())
{
var connectedConnection = connectionWrapper.GetConnected();
string SqlSyntax = "SELECT * FROM ORDRE WHERE REF_EXPED = #REFERENCE";
SqlCommand comm_InsUpt = new SqlCommand(SqlSyntax, connectionWrapper.conn);
comm_InsUpt.Parameters.AddWithValue("#REFERENCE", textEdit_ref.Text);
SqlDataAdapter adapt_SelectAll = new SqlDataAdapter();
adapt_SelectAll.SelectCommand = comm_InsUpt;
DataSet dSet_SelectAll = new DataSet();
adapt_SelectAll.Fill(dSet_SelectAll, "BON_ETIKET");
var xtraReport_Pricipal = new Zebra_Web();
xtraReport_Pricipal.Parameters["Count_Ordre"].Value = 1;
xtraReport_Pricipal.Parameters["IdPacket"].Value = 1;
xtraReport_Pricipal.DataSource = dSet_SelectAll;
xtraReport_Pricipal.DataMember = dSet_SelectAll.Tables[0].TableName;
xtraReport_Pricipal.CreateDocument();
xtraReport_Pricipal.PrintingSystem.ShowMarginsWarning = false;
xtraReport_Pricipal.PrintingSystem.ContinuousPageNumbering = true;
//xtraReport_Pricipal.ShowPreviewDialog();
xtraReport_Pricipal.Print(Properties.Settings.Default.Zebra);
dSet_SelectAll.Dispose();
adapt_SelectAll.Dispose();
}
}
catch (Exception excThrown)
{
throw new Exception(excThrown.Message, excThrown);
}
}
i have tried to put this line:
this.AcceptButton = (Button)this.Controls["button_Valider"];
in constructor and onLoad From event but still not work.
when the user click the button it nothing happen. i have to clic it with the mouse.
You need to set KeyPreview Property of your Form to True.
and then write a KeyDown Event to Handle Enter Key on Form
as Below:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
button_Valider_Click(sender,e);
}
}

Categories

Resources