So I have a basic WPF application, that OnStartup creates a DispatcherTimer and has a tick method that runs a basic SQL query to check the status of some arbitrary table.
It is a variable query in terms of how long it takes to execute.
If I am in the main application window, when the background DispatcherTimer runs (DispatcherTimer code being within the App.xaml, not the MainWindow.xaml), the window hangs while the query runs, and as the query runs every 5 seconds, the GUI is unresponsive.
It seems a new thread is the way to go, but I am lost on where to begin. I am relatively new to C#/WPF so it's all a learning curve at the moment. How would I go about invoking my dispatcherTimer_Tickwithin a new thread, to avoid this problem?
DispatcherTimer dispatcherTimer;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow = new MainWindow();
MainWindow.Closing += MainWindow_Closing;
_notifyIcon = new System.Windows.Forms.NotifyIcon();
_notifyIcon.DoubleClick += (s, args) => ShowMainWindow();
_notifyIcon.Icon = BackgroundApplication.Properties.Resources.GTL;
_notifyIcon.Visible = true;
CreateContextMenu();
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 5);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
// Console.WriteLine("Tick");
SqlConnection cnn;
connectionString = #"SOME_DATA_SOURCE";
cnn = new SqlConnection(connectionString);
cnn.Open();
// MessageBox.Show("Connection Open !");
// cnn.Close();
SqlCommand command;
SqlDataReader dataReader;
String sql = "";
Int16 output = 0;
sql = "SOME SQL STATEMENT";
command = new SqlCommand(sql, cnn);
dataReader = command.ExecuteReader();
while (dataReader.Read())
{
output = Int16.Parse(dataReader[""].ToString());
}
if(output > 0)
{
_notifyIcon.Icon = BackgroundApplication.Properties.Resources.RTL;
}
_notifyIcon.Text = "Current Issues: " + output;
}
Make the Tick handler async and await the long-running call:
private async void dispatcherTimer_Tick(object sender, EventArgs e)
{
await Task.Run(() =>
{
// make query here
});
// update the UI outside the Task
_notifyIcon.Text = "Current Issues: " + output;
}
Even better would be to directly call async methods, like
private async void dispatcherTimer_Tick(object sender, EventArgs e)
{
...
dataReader = await command.ExecuteReaderAsync();
...
}
Related
I have a windows application using SqlDependency. This application represents a log monitor UI get the latest rows added in a specific table in the database and view it in a DataGridView
The code is realize the change in SQL Server but the DataGridView show nothing (No errors, and no data, just as many line as in datatable)
Here is my code
public partial class Form1 : Form
{
private int changeCount = 0;
private const string tableName = "NhanVien";
private const string statusMessage = "Đã có {0} thay đổi.";
//' The following objects are reused
//' for the lifetime of the application.
private SqlConnection connection = null;
private SqlCommand command = null;
private DataSet dataToWatch = null;
public Form1()
{
InitializeComponent();
Form2 frm = new Form2();
frm.Show();
}
private bool CanRequestNotifications()
{
// In order to use the callback feature of the
// SqlDependency, the application must have
// the SqlClientPermission permission.
try
{
SqlClientPermission perm = new SqlClientPermission(PermissionState.Unrestricted);
perm.Demand();
return true;
}
catch (Exception ex)
{
return false;
}
}
private string GetConnectionString()
{
// To avoid storing the connection string in your code,
// you can retrive it from a configuration file.
// Return "Data Source=THU-PC\TINTIN;Initial Catalog=QLVT;Persist Security Info=True;User ID=sa;Password=kc;Pooling = false"
return "Data Source=MSI;Initial Catalog=ChuyenDeCNPM;User ID=sa;Password=123;";
}
private string GetSQL()
{
return "select manv as [Mã NV],Ho as [ Họ],Ten as [Tên],phai as [Phái],diachi as [ Địa chỉ] from dbo.NhanVien";
}
private void nhanVienBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
this.Validate();
this.nhanVienBindingSource.EndEdit();
this.tableAdapterManager.UpdateAll(this.chuyenDeCNPMDataSet);
}
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'chuyenDeCNPMDataSet.NhanVien' table. You can move, or remove it, as needed.
//this.nhanVienTableAdapter.Fill(this.chuyenDeCNPMDataSet.NhanVien);
if (CanRequestNotifications() == true)
BatDau();
else
MessageBox.Show("Bạn chưa kích hoạt dịch vụ Broker", "Cảnh báo", MessageBoxButtons.OK);
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// This event will occur on a thread pool thread.
// It is illegal to update the UI from a worker thread
// The following code checks to see if it is safe update the UI.
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
// If InvokeRequired returns True, the code is executing on a worker thread.
if (i.InvokeRequired)
{
// Create a delegate to perform the thread switch
OnChangeEventHandler tempDelegate = new OnChangeEventHandler(dependency_OnChange);
object[] args = new[] { sender, e };
// Marshal the data from the worker thread
// to the UI thread.
i.BeginInvoke(tempDelegate, args);
return;
}
// Remove the handler since it's only good
// for a single notification
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
// At this point, the code is executing on the
// UI thread, so it is safe to update the UI.
++changeCount;
this.label1.Text = string.Format(statusMessage, changeCount);
// Add information from the event arguments to the list box
// for debugging purposes only.
{
var withBlock = this.ListBox1.Items;
withBlock.Clear();
withBlock.Add("Info: " + e.Info.ToString());
withBlock.Add("Source: " + e.Source.ToString());
withBlock.Add("Type: " + e.Type.ToString());
}
// Reload the dataset that's bound to the grid.
GetData();
}
private void GetData()
{
// Empty the dataset so that there is only
// one batch worth of data displayed.
dataToWatch.Clear();
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
// Create and bind the SqlDependency object
// to the command object.
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataToWatch, tableName);
DataGridView1.DataSource = null;
DataGridView1.DataSource = dataToWatch;
DataGridView1.DataMember = tableName;
}
/*DataTable dt = new DataTable();
dt.Load(command.ExecuteReader(CommandBehavior.CloseConnection));
DataGridView1.DataSource = dt;*/
}
private void BatDau()
{
changeCount = 0;
// Remove any existing dependency connection, then create a new one.
SqlDependency.Stop(GetConnectionString());
try
{
SqlDependency.Start(GetConnectionString());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
if (connection == null)
{
connection = new SqlConnection(GetConnectionString());
connection.Open();
}
if (command == null)
// GetSQL is a local procedure that returns
// a paramaterized SQL string. You might want
// to use a stored procedure in your application.
command = new SqlCommand(GetSQL(), connection);
if (dataToWatch == null)
dataToWatch = new DataSet();
GetData();
}
private void button_update_Click(object sender, EventArgs e)
{
Update form = new Update();
form.Show();
}
private void Form1_FormClosed(System.Object sender, System.Windows.Forms.FormClosedEventArgs e)
{
SqlDependency.Stop(GetConnectionString());
if (connection != null)
connection.Close();
}
private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
}
private void button_exit_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
This is my datagridview after start
I tried debug and saw that the datasource is set. But it's still empty in GridView
I've just found out that i have TableAdapter and BindingSource connect with GridView [![like this][1]][1], so it's cause fault. If you delete all that, use datatable instead of dataset, the code will be like this:
private void GetData()
{
dt.Clear();
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
// Create and bind the SqlDependency object
// to the command object.
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
dt.Load(command.ExecuteReader(CommandBehavior.CloseConnection));
DataGridView1.DataSource = dt;
}
You should declare DataTable as global variable. Everything should work well. Good Luck!
[1]: https://i.stack.imgur.com/aODka.png
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
loadtest();
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Progress Bar Window close
pop.Close();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pop.prgTest.Value = e.ProgressPercentage;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Background Worker code///
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync();
//Progress Bar Window
pop.Show();
}
The load test here is...a method that picks up few images from database and displays them. The method works fine if the run the method on page initialisation but its not giving any output when i load them like this in background worker.....here is the method loadtest.
public void loadtest()
{
string query = "select*from question where id='" + 1 + "'";
MySqlConnection conDataBase = new MySqlConnection(constring);
MySqlCommand cmdDataBase = new MySqlCommand(query, conDataBase);
MySqlDataReader myReader;
try
{
conDataBase.Open();
myReader = cmdDataBase.ExecuteReader();
while (myReader.Read())
{
string qid = myReader.GetInt32("id").ToString();
byte[] imgg1q1 = (byte[])(myReader["question"]);
byte[] imgg2q1 = (byte[])(myReader["opt1"]);
byte[] imgg3q1 = (byte[])(myReader["opt2"]);
byte[] imgg4q1 = (byte[])(myReader["opt3"]);
byte[] imgg5q1 = (byte[])(myReader["opt4"]);
MemoryStream mstreamq1 = new MemoryStream(imgg1q1);
MemoryStream mstream1q1 = new MemoryStream(imgg2q1);
MemoryStream mstream2q1 = new MemoryStream(imgg3q1);
MemoryStream mstream3q1 = new MemoryStream(imgg4q1);
MemoryStream mstream4q1 = new MemoryStream(imgg5q1);
q1.BeginInit();
q1.StreamSource = mstreamq1;
q1.CacheOption = BitmapCacheOption.OnLoad;
q1.EndInit();
// Assign the Source property of your image
q_image.Source = q1;
q1opt1.BeginInit();
q1opt1.StreamSource = mstream1q1;
q1opt1.CacheOption = BitmapCacheOption.OnLoad;
q1opt1.EndInit();
option_1.Source = q1opt1;
q1opt2.BeginInit();
q1opt2.StreamSource = mstream2q1;
q1opt2.CacheOption = BitmapCacheOption.OnLoad;
q1opt2.EndInit();
option_2.Source = q1opt2;
q1opt3.BeginInit();
q1opt3.StreamSource = mstream3q1;
q1opt3.CacheOption = BitmapCacheOption.OnLoad;
q1opt3.EndInit();
option_3.Source = q1opt3;
q1opt4.BeginInit();
q1opt4.StreamSource = mstream4q1;
q1opt4.CacheOption = BitmapCacheOption.OnLoad;
q1opt4.EndInit();
option_4.Source = q1opt4;
}
conDataBase.Close();
}
catch
{
}
}
i am using a background worker to display a popup of loading progressbar until the page loads and popup is a function that will display a new popup window which says loading....
the loadtest method works properly everywhere but its not working with the backgroundworker...the funtion loadtest picks some images from database which takes time and i am displaying a popup until the images are getting loaded and the dispalying in option_2.source,option_2.source,option_3.source and option_4.source..........
Don't change control properties directly from a background thread. Instead send them over to the UI thread in order to use them. One way to do this would be via BackgroundWorker.ReportProgress Method (Int32, Object) see also Updating UI with BackgroundWorker in WPF
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
loadtest(sender as BackgroundWorker);
}
and
public void loadtest(BackgroundWorker bw)
{
//... your code
q1.BeginInit();
q1.StreamSource = mstreamq1;
q1.CacheOption = BitmapCacheOption.OnLoad;
q1.EndInit();
// by freezing the image, it will become available to the UI thread
q1.Freeze();
// Don't directly assign the Source property of your image
// q_image.Source = q1;
// Instead, report progress to the UI thread:
bw.ReportProgress.ReportProgress(25, new Tuple<Image, ImageSource>(q_image, q1));
//... your code
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pop.prgTest.Value = e.ProgressPercentage;
// assign the image source on the UI thread
var data = e.UserState as Tuple<Image, ImageSource>;
data.Item1.Source = data.Item2;
}
Edit: added the Freeze() call to image after initialization. This is necessary in order to allow access to the image outside the worker thread where it was created.
As a side note: Please replace your empty catch { } block with some actual error checking... I suppose you just suppressed the relevant exceptions there that would help in identifying the issues.
The backgroundworker has the ReportProgress method with which you can report periodic progress of the background to the UI.
Set the WorkerReportsProgress to true, as this defaults to false.
bw.WorkerReportsProgress = true;
Then hook up the Progress report event to your UI updating method, bw_ProgressChanged, which you are already doing.
bw.ProgressChanged += bw_ProgressChanged;
Then call in the ReportProgress method wherever you want to update the UI in the Do_Work method. Get the worker from the sender object and pass into the loadtest method and call like below.
bw.ReportProgress(progressPercentage); // This would be % completed you want to display.
This should work.
I have a traffic exchange website and I want to convert it into an windows application using C# winform with Awesomium 1.7.5.
The basic setup is ready but there is a problem with Awesomium.
After visiting a few websites slows down and the freezes entirely ( Not Responding ).
public Form1()
{
InitializeComponent();
Text = "Traffic Exchange";
WindowState = FormWindowState.Maximized;
timer1 = new System.Windows.Forms.Timer();
timer1.Tick += new EventHandler(timer1_Tick);
int user_id = Properties.Settings.Default.user_id;
string user_id_s = user_id.ToString();
toolStripLabel2.Text = user_id_s;
if (Properties.Settings.Default.user_id == 0)
{
toolStripLabel3.Visible = true;
toolStripButton3.Visible = false;
}
else
{
toolStripButton3.Visible = true;
toolStripLabel3.Visible = false;
}
}
private void toolStripButton3_Click_1(object sender, EventArgs e)
{
// starting the traffic traffic exchange
LoadUrl();
StartTimer();
}
private void LoadUrl()
{
try
{
string MyConnection2 = "*******";
string Query = "select * from ****** where status = 1 AND credits > 5 ORDER BY rand() LIMIT 1";
MySqlConnection MyConn2 = new MySqlConnection(MyConnection2);
MySqlCommand MyCommand2 = new MySqlCommand(Query, MyConn2);
MyConn2.Open();
using (MySqlDataReader DR = MyCommand2.ExecuteReader())
{
while (DR.Read())
{
string WebURL = Convert.ToString(DR.GetValue(*));
string WebSurfSec = Convert.ToString(DR.GetValue(*));
int result = Convert.ToInt32(WebSurfSec);
int sec_to_mil = result * 1000;
toolStripLabel5.Text = WebSurfSec;
//toolStripStatusLabel2.Text = result.ToString();
//toolStripStatusLabel3.Text = sec_to_mil.ToString();
webControl3.Source = new Uri(WebURL);
toolStripTextBox1.Text = WebURL;
toolStripLabel6.Text = toolStripTextBox1.Text;
timer1.Interval = sec_to_mil; // in miliseconds
}
}
MyConn2.Close();
// WebCore.ReleaseMemory();
// webControl3.Update();
// Thread.Sleep(500);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
LoadUrl();
}
private void StartTimer()
{
timer1.Start();
}
So LoadUrl() it's a loop. When the app is started loads in the traffic exchange website, a little bit slow but it works and you can go form a page to another without freezing but when the exchange is in action ( LoadUrl() ) after 5 minutes the app is dead.
I've been searching for a solution all day and ended up with nothing, couldn't find a solution the problem.
The timer should not be recreated each time you loop. What is happening is that you are creating multiple event handlers each time you loop. Creating the handler once in the constructor and starting the timer in the button click routine is the right way.
You can change the interval inside the loop, but avoid adding another Start() method call there.
I am running an application that loads too many data from the database, I don't have any wrong code or anything but every time I load too many data I get this exception
Timeout expired. The timeout period elapsed prior to completion of the
operation or the server is not responding.
Warning: Null value is eliminated by an aggregate or other SET operation.
Here is my code to load the data in my gridview
private void toolStripCompute_Click(object sender, EventArgs e)
{
//LoadData();
#region Threading Begin
//this.dgvCommision.Enabled = false;
oProgressBar.Visible = true;
lblProgress.Visible = true;
oProgressBar.Value = 0;
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
#endregion
// worker_DoWork();
//ComputeCommission();
}
This is worker_DoWork code from worker.DoWork += new DoWorkEventHandler(worker_DoWork)
void worker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_elapsed);
timer.Start();
Invoke(new MyDelegate(ShowLabel), "Loading...");
BACS.DateFrom = this.dtFrom.Value.ToShortDateString(); //this.dtFrom.Text;
BACS.DateTo = this.dtTo.Value.ToShortDateString(); //this.dtTo.Text;
BACS.BrokerAgent = BrokerXML;
BACS.Payor = PayorXML;
DS = BACS.GET_SP_BACS_ComputeCommission();
ReadData(DS, this.dgvCommision);
CheckGrid();
Thread.Sleep(1);
timer.Stop();
}
catch
{ }
}
And this is how I load my sp command from DS = BACS.GET_SP_BACS_ComputeCommission(); GET_SP_BACS_ComputeCommission is this
public DataSet GET_SP_BACS_ComputeCommission()
{
SqlParameter[] sqlParam = new SqlParameter[4];
sqlParam[0] = new SqlParameter("#DATEFROM", DateFrom);
sqlParam[1] = new SqlParameter("#DATETO", DateTo);
sqlParam[2] = new SqlParameter("#BROKERAGENT", BrokerAgent);
sqlParam[3] = new SqlParameter("#PAYOR",Payor);
DataSet ds = new DataSet();
if (this.InitDataLayerObject() == true)
{
ds = this.ExecuteQuery("dbo.[SP_BACS_COMMISSION_COMPUTATION]", sqlParam);
}
return ds;
}
Is there any solution in this? I've tried setting up my Connection Timeout=0 I even changed it to Connection Timeout=2500 Still it didn't worked.
I want to use a ProgressBar control in C# when my DataGridView is loading data, but when I run my program I get an error:
Cross-thread operation not valid: Control 'dataGridView1' accessed from a thread other than the thread it was created on.
Code:
private readonly BackgroundWorker _bw = new BackgroundWorker();
public workerbloks()
{
InitializeComponent();
progressBar1.MarqueeAnimationSpeed = 30;
// set Visible false before you start long running task
progressBar1.Visible = false;
_bw.DoWork += show_worked_bloks;
_bw.RunWorkerCompleted += BW_RunWorkerCompleted;
}
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Show();
_bw.RunWorkerAsync();
}
private void BW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Hide();
}
public void show_worked_bloks(object sender, DoWorkEventArgs doWorkEventArgs)
{
MYDBMS md = new MYDBMS();
DataTable dt = new DataTable();
clsWorkBloks clswb = new clsWorkBloks();
try
{
dt = md.ExecuteSelectSQL(clswb.show_all_worked_bloks());
dataGridView1.DataSource = dt;
}
catch
{
}
}
Execute the specified delegate asynchronously on the thread that the dataGridView1 underlying handle was created on.
dataGridView1.BeginInvoke((MethodInvoker)delegate
{
this.dataGridView1.DataSource = dt;
});
As the progressbar is just marquee display. Simply bring it to front before running background worker.
progressBar1.BringToFront();
progressBar1.Show();
_bw.RunWorkerAsync();