I have made an windows form, with an initializing (form) which later redirects to the main form. The problem is it takes about three seconds to load, which make the user interface look really bad. The buttons are white, until they load, then they show the text and the colors. Is there any way to pre-load the form, but hide it, until the initializing (form) is finished?
For those who ask why it takes so long, I have a web browser which imports local HTML and it has InvokeText and addBase, addMaths and other items
This is the load script, how it loads the web browser
private async void TextEdit_Load(object sender, EventArgs e)
{
WebClient wc = new WebClient();
wc.Proxy = null;
try
{
RegistryKey registryKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", true);
string friendlyName = AppDomain.CurrentDomain.FriendlyName;
bool flag2 = registryKey.GetValue(friendlyName) == null;
if (flag2)
{
registryKey.SetValue(friendlyName, 11001, RegistryValueKind.DWord);
}
registryKey = null;
friendlyName = null;
}
catch (Exception)
{
}
webBrowser1.Url = new Uri(string.Format("file:///{0}/Files/TextEditor/Editor.html", Directory.GetCurrentDirectory()));
The next bit is the functionality of the web browser
await Task.Delay(500);
webBrowser1.Document.InvokeScript("SetTheme", new string[]
{
"Dark"
});
addBase();
addMath();
addGlobalNS();
addGlobalV();
addGlobalF();
webBrowser1.Document.InvokeScript("SetText", new object[]
{
""
});
}
I guess it is the problem with the webBrowser (text editor) because when I delete it, it no longer takes 3 seconds loading time.
For those who say use This.Hide(); and This.Show();, it does not work, because the web browser won't load at all.
if the main issue is that it takes about three seconds to load, then consider Threading on your form's Load event (provided you placed all pre-requisite loading there). This way you can initially disable controls prior to the form being displayed and enable controls once the entire process is finished. See below example:
private void Form1_Load(object sender, EventArgs e)
{
#region Disable controls here
textbox1.Enabled = false;
button1.Enabled = false;
combobox1.Enabled = false;
#endregion
Task.Factory.StartNew(() => {
try
{
// Do Long running processing of form prerequisites here.
...
// Enable controls here once processing is sucessful and complete.
Invoke((Action) (() => {
textbox1.Enabled = true;
button1.Enabled = true;
combobox1.Enabled = true;
}));
}
catch(Exception e)
{
Invoke((Action) (() => {
MessageBox.Show(e.Message);
}));
}
});
}
Related
I have a FileSystemWatcher watching for newly created files.
When it sees one, I would like it to open a child window.
Using this:
private void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
TableWindow win = new TableWindow();
win.Owner = this;
win.Text = "xxx";
win.ShowInTaskbar = false;
win.Show();
}
The error I'm getting is:
Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on
After some googling. I ended up with this
TableWindow win = new TableWindow();
win.Owner = this;
win.Text = "xxx";
win.ShowInTaskbar = false;
win.Invoke((MethodInvoker)delegate
{
win.Show();
});
which gives me a different error:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
Here's the scenario. on a game, each time a new table is opened, a new file is created. When that file is created, I want to open a child window to display statistics on that table.
Is this even possible?
What I've done in the past to work with InvokeRequired is to place it within an if statement that will call the method on the UI thread if it hasn't been called from the UI thread.
private void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
ShowWindow();
}
private void ShowWindow()
{
if (this.InvokeRequired)
{
var del = new MethodInvoker(ShowWindow);
this.BeginInvoke(del);
return;
}
TableWindow win = new TableWindow();
win.Owner = this;
win.Text = "xxx";
win.ShowInTaskbar = false;
win.Show();
}
I am working on a C# project and i need the file to deleted after 30 seconds. So once the file sent to the machine i need the software to count till 30 seconds and at same time show a splash form and once 30 seconds crossed close the splash screen and then delete the file.
I have added a splash screen called "image". So now what happens is, the data is only sent to the printer after the splash screen is closed. I need to multi thread the job. I mean the data should print in one side while the splash screen should show at the same time. Is there a way i can come out!!.. Please help me out.
So in my case i am copying the file to the bin/debug folder. then sending data to the machine simultaneously show the splash screen for 30 seconds and close the splash screen and then i need to delete the file..
codes:
private void button4_Click(object sender, EventArgs e)
{
//string filePath = image_print();
// MessageBox.Show(filePath, "path");
string s = image_print() + Print_image();
if (String.IsNullOrEmpty(s) || img_path.Text == "")
{
return;
}
else
{
//here its coming to the splash screen code, But data is transferred to the machine only after the splash screen is close :-(
this.Hide();
omg = new image();
omg.ShowDialog();
this.Show();
//splash screen closed and then data is transferred.. which i don't need.. i need simultaneous job to be done at the same time..
PrintFactory.sendTextToLPT1(s);
}
}
private string image_print()
{
OpenFileDialog ofd = new OpenFileDialog();
string path = "";
string full_path = "";
string filename_noext = "";
ofd.InitialDirectory = #"C:\ZTOOLS\FONTS";
ofd.Filter = "GRF files (*.grf)|*.grf";
ofd.FilterIndex = 2;
ofd.RestoreDirectory = true;
if (ofd.ShowDialog() == DialogResult.OK)
{
filename_noext = System.IO.Path.GetFileName(ofd.FileName);
path = Path.GetFullPath(ofd.FileName);
img_path.Text = filename_noext;
//MessageBox.Show(filename_noext, "Filename"); - - -> switching.grf
// MessageBox.Show(full_path, "path");
//move file from location to debug
string replacepath = #"\\bin\Debug";
string fileName = System.IO.Path.GetFileName(path);
string newpath = System.IO.Path.Combine(replacepath, fileName);
// string newpath = string.Empty;
if (!System.IO.File.Exists(filename_noext))
System.IO.File.Copy(path, newpath);
filename_noext = img_path.Text;
MessageBox.Show(filename_noext, "path");
}
if (string.IsNullOrEmpty(img_path.Text))
return "";//
StreamReader test2 = new StreamReader(img_path.Text);
string s = test2.ReadToEnd();
return s;
}
private string Print_image()
{
//some codes
return s;
}
In image form: I have the following codes
public partial class image : Form
{
string filePath;
public image()
{
InitializeComponent();
// this.filePath = FileToDeletePath;
System.Timers.Timer timer1 = new System.Timers.Timer();
timer1.Interval = 30000;
timer1.Elapsed += timer1_Elapsed;
timer1.Start();
}
private void image_Load(object sender, EventArgs e)
{
}
void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
//delete the file using "filePath"
string Filename = img_path.Text; // here i cannot pass the old string file name with extension to this form.. Any ways please help me out
if (string.IsNullOrEmpty(Filename))
return;
if (Filename.ToCharArray().Intersect(Path.GetInvalidFileNameChars()).Any())
return;
File.Delete(Path.Combine(#"\\bin\Debug", Filename));
}
}
something like this????
Task waitfordelete = Task.Run(() =>
{
image im = new image();
});
Assumptions: window image should be shown as a dialog (modal), and only while the call to PrintFactory.sendTextToLPT1 is in progress.
If that's correct, then something like this could work for you:
// Don't forget, you need to dispose modal dialogs
image omg = new image();
// Ensure the dialog has been shown before starting task. That
// way the task knows for sure the dialog's been opened and can
// be closed.
omg.Loaded += (sender, e) =>
{
// Run the print task in a separate task
Task.Run(() =>
{
PrintFactory.sendTextToLPT1(s);
// But get back onto the main GUI thread to close the dialog
Dispatcher.Invoke(() => omg.Close());
});
};
this.Hide();
omg.ShowDialog();
this.Show();
Apologies in advance for any typos/syntax errors/etc. Hopefully the above is sufficient to express the general idea.
The answer given by Narzul and Peter both are correct. You can implement any one. But, I know your next question will be how to implement that method in your code.
you can use Thread or Task class object to separate the process. So when one process is running then other process can perform their taks at that time. There are two process in your login. The first one is send the file to the printer and the second one is the show dialog for 30 seconds and then delete the file. You should create the another thread to invoke the any one of the process so other process can perform asynchronously.
1st: make the seperate process for Print file.
Task waitfordelete = Task.Run(() =>
{
PrintFactory.sendTextToLPT1(s);
});
this.Hide();
omg = new image();
omg.ShowDialog();
this.Show();
2nd: make the seperate process for show dialog and delete the file. But, I think you may get the error in this method. You cannot change the UI from other thread
Task waitfordelete = Task.Run(() =>
{
Dispatcher.Invoke(() => this.ShowSplashScreen());
});
PrintFactory.sendTextToLPT1(s);
private void ShowSplashScreen()
{
this.Hide();
omg = new image();
omg.ShowDialog();
this.Show();
}
if you don't want to use the thread or task then just simply handle the close event of Image form
this.Hide();
omg = new image();
omg.Show();
PrintFactory.sendTextToLPT1(s);
omg.FormClosed += (object sender, EventArgs e) => {
File.Delete(Path.Combine(Application.StartupPath, Path.GetFileName(img_path.Text));
this.Show();
};
and modify the code in timer_tick event in Image form and add the this.Close() after delete file statement.
void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
....
//File.Delete(Path.Combine(#"\\bin\Debug", Filename)); comment this line
this.Close();
}
Another hidden question I have found here. here i cannot pass the old string file name with extension to this form.. Any ways please help me out
void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
//delete the file using "filePath"
string Filename = img_path.Text; // here i cannot pass the old string file name with extension to this form.. Any ways please help me out
for that, you can create the property in Image class and assign the file name from the parent form.
Image omg = new Image()
omg.FileName = Path.Combine(Application.StartupPath, Path.GetFileName(img_path.Text));
omg.Show();
and the property in Image form will be created like this
public class Image : Form
{
public string FileName { get; set; }
public Image()
{
}
void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
....
File.Delete(Path.Combine(Application.StartupPath, this.Filename));
this.Close();
}
}
NOTE: Use the Application.StartupPath istead of \\bin\debug
I have a test web connection form in c#. I want to show a loading window while my connection is being checked, and then show the result of checking.
This is my code for testing the web connection:
public bool ConnectionAvailable(string strServer)
{
try
{
HttpWebRequest reqFP = (HttpWebRequest)HttpWebRequest.Create(strServer);
HttpWebResponse rspFP = (HttpWebResponse)reqFP.GetResponse();
if (HttpStatusCode.OK == rspFP.StatusCode)
{
// HTTP = 200 - Internet connection available, server online
rspFP.Close();
return true;
}
else
{
// Other status - Server or connection not available
rspFP.Close();
return false;
}
}
catch (WebException)
{
// Exception - connection not available
return false;
}
}
And this:
private void button1_Click(object sender, EventArgs e)
{
string url = "Web-url";
label1.Text = "Checking ...";
button1.Enabled = false;
if (ConnectionAvailable(url))
{
WebClient w = new WebClient();
w.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
label1.Text = w.UploadString(url, "post", "SN=" + textBox1.Text);
button1.Enabled = true;
}
else
{
label1.Text = "Conntion fail";
button1.Enabled = true;
}
}
On a windows forms application the user interface runs on one thread, if you try to run a long running process, which checking the web connection might end up being this will cause the form to freeze until it completes the work.
So, I'd start a new thread that does the check. then raise an event to return the result. while all that's happening you can do what you like with the user interface, such as a loading graphic, or even allow the user to continue using features that don't require the internet connection.
Create EventArgs class of your own so you can pass back the result:
public class ConnectionResultEventArgs : EventArgs
{
public bool Available { get; set; }
}
Then in your form class, create your event, handlers and the method to action when the event arrives
//Create Event and Handler
public delegate void ConnectionResultEventHandler(object sender, ConnectionResultEventArgs e);
public event ConnectionResultEventHandler ConnectionResultEvent;
//Method to run when the event has been receieved, include a delegate in case you try to interact with the UI thread
delegate void ConnectionResultDelegate(object sender, ConnectionResultEventArgs e);
void ConnectionResultReceived(object sender, ConnectionResultEventArgs e)
{
//Check if the request has come from a seperate thread, if so this will raise an exception unless you invoke.
if (InvokeRequired)
{
BeginInvoke(new ConnectionResultDelegate(ConnectionResultReceived), new object[] { this, e });
return;
}
//Do Stuff
if (e.Available)
{
label1.Text = "Connection Good!";
return;
}
label1.Text = "Connection Bad";
}
Subscribe to the event when your form loads:
private void Form1_Load(object sender, EventArgs e)
{
//Subscribe to the the results event.
ConnectionResultEvent += ConnectionResultReceived;
}
and then setup the worker thread:
//Check the connection
void BeginCheck()
{
try
{
HttpWebRequest reqFP = (HttpWebRequest)HttpWebRequest.Create("http://google.co.uk");
HttpWebResponse rspFP = (HttpWebResponse)reqFP.GetResponse();
if (HttpStatusCode.OK == rspFP.StatusCode)
{
// HTTP = 200 - Internet connection available, server online
rspFP.Close();
ConnectionResultEvent(this, new ConnectionResultEventArgs {Available = true});
}
else
{
// Other status - Server or connection not available
rspFP.Close();
ConnectionResultEvent(this, new ConnectionResultEventArgs { Available = false });
}
}
catch (WebException)
{
// Exception - connection not available
//Raise the Event - Connection False
ConnectionResultEvent(this, new ConnectionResultEventArgs { Available = false });
}
}
private void button1_Click(object sender, EventArgs e)
{
//loading graphic, screen or whatever
label1.Text = "Checking Connection...";
//Begin the checks - Start this in a new thread
Thread t = new Thread(BeginCheck);
t.Start();
}
I am thinking of threading! One thread checks the connection while the other one is showing the loading window. If for example the connection has been established you can notify the other thread and show the result.
I am currently writing a simple WPF 3.5 application that utilizes the SharePoint COM to make calls to SharePoint sites and generate Group and User information. Since this process takes awhile I want to show a ProgressBar while the groups are being generated. The desired process is as follows:
User enters url and clicks button to fetch site data.
ProgressBar begins animation
Groups are generated and names are added to a ListView
Upon completion ProgressBar animation ends
The problem I am running into is that the UI is never updated. Neither the ProgressBar or the ListView makes any changes. If anyone has any ideas to help with the code below it would be greatly appreciated.
private void GetGroupsAndUsersButton_Click(object sender, RoutedEventArgs e)
{
siteUrl = "";
if (SiteURLTextBox.Text.Length > 0)
{
FetchDataProgressBar.IsIndeterminate = true;
mWorker = new BackgroundWorker();
mWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
mWorker.WorkerSupportsCancellation = true;
mWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync();
}
else
{
System.Windows.MessageBox.Show("Please enter a URL for the SharePoint site you wish to retrieve data");
}
}
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
siteUrl = SiteURLTextBox.Text;
GroupListView.ItemsSource = null;
try
{
using (SPSite site = new SPSite(siteUrl))
{
SPWeb web = site.OpenWeb();
SPGroupCollection collGroups = web.SiteGroups;
if (GroupNames == null)
GroupNames = new List<string>();
foreach (SPGroup oGroup in collGroups)
{
GroupListView.Items.Add(new ListViewItem() { Content = oGroup.Name });
}
foreach (ListViewItem item in GroupListView.Items)
{
item.MouseLeftButtonUp += item_MouseLeftButtonUp;
}
}
}
catch (Exception ex)
{
System.Windows.MessageBox.Show("Unable to locate a SharePoint site at: " + siteUrl);
}
}
private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
FetchDataProgressBar.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
FetchDataProgressBar.IsIndeterminate = false;
}
));
}
At first you need to support ProgressChanged events.
Update your BackgroundWorker initialization to:
GroupListView.ItemSource = null;
mWorker = new BackgroundWorker();
mWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
mWorker.WorkerSupportsCancellation = true;
mWorker.WorkerReportsProgress = true;
mWorker.ProgressChanged += OnProgressChanged;
mWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync(SiteURLTextBox.Text);
After that you have to add a OnProgressChanged handler:
private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
FetchDataProgressBar.Value = e.ProgressPercentage;
ListViewItem toAdd = (ListViewItem)e.UserState;
toAdd.MouseLeftButtonUp += item_MouseLeftButtonUp;
GroupListView.Items.Add(toAdd);
}
Therefore you have to change your DoWork:
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
try
{
using (SPSite site = new SPSite((String)e.Argument))
{
SPWeb web = site.OpenWeb();
SPGroupCollection collGroups = web.SiteGroups;
if(GroupNames == null)
GroupNames = new List<string>();
int added = 0;
foreach(SPGroup oGroup in collGroups)
{
added++;
ListViewItem tmp = new ListViewItem() {
Content = oGroup.Name
};
worker.ReportProgress((added * 100)/collGroups.Count,tmp);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Unable to locate a SharePoint site at: " + siteUrl);
}
}
That's because you're not allowed to change GUI on DoWork.
After that, each ListViewItem is added separately to your ListView. I would also recommend, that your URL is passed as an argument to RunWorkerAsync.
Edit: Add percentage to OnProgressChanged.
In your DoWork method, you are manipulating WPF controls in code on a background thread, which you are not supposed to do. Actually, you should receive errors like "Cannot access control from other thread". Probably those exceptions are caught by your catch-all error handler, and maybe even the MessageBox doesn't work from the background thread.
As a quick fix, you would have to make siteURL and collGroups class fields, move everything before the using block to your GetGroupsAndUsersButton_Click method, and everything starting with the first foreach loop to the RunworkerCompleted event, so that all code which accesses controls runs on the UI thread.
Another thing you should change is that you should not create ListViewItems in code, but use a DataTemplate instead... this is not connected to your problem, though.
You'll need:
mWorker.WorkerReportsProgress = true;
mWorker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
Then in your DoWork you'll need to call:
var worker = (BackgroundWorker)sender;
worker.ReportProgress(progressAmount);
Good worked example here: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
Iam Unable to do this from past one week. I want to click on multiple links n multiple web pages using webBrowser in C# Following is the code please help me in this regard.
public void DoDelete()
{
int count = 0;
if (corruptList.Count > 0)
{
foreach (string listItem in corruptList)
{
var th = new Thread(() =>
{
try
{
WebBrowser webBrowser = new WebBrowser();
webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBroswer_DocumentCompleted);
webBrowser.Navigate(listItem);
Thread.Sleep(100);
webBrowser.Dispose();
}
catch (Exception ex)
{
throw ex;
}
this.Invoke(new MethodInvoker(delegate
{
dataGridView_CorruptLinks.Rows[count].Cells[2].Value = "Deleted";
}));
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
Thread.Sleep(100);
}
count++;
}
}
void webBroswer_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
try
{
WebBrowser webBrowser = sender as WebBrowser;
HtmlElementCollection ec = webBrowser.Document.GetElementsByTagName("a");
foreach (HtmlElement item in ec)
{
if (item.InnerHtml == "Delete this invalid field")
{
item.InvokeMember("Click");
break;
}
}
}
catch (Exception exp)
{
}
}
Navigate is an asynchronous action and you're only giving it 1/10 of a second to complete before you call Dispose on the web browser object. Your navigation and clicks are probably taking longer than that to complete and so there is no web browser to act against... You're also "swallowing" all exceptions in the document complete handler. This is a very bad thing to do. You should at the very least be doing some debug logging there to help yourself diagnose the problem.
But, to keep the similar logic you should create a collection of web browsers at class level. Something like:
private List<WebBrowser> _myWebBrowsers;
Then add to this list in your loop but do not call Dispose. You should only dispose of the browser when you're done with it.
That should get you closer though there are a few other potential issues with your code. You're allocating a borser object and thread for every time through a loop. This could quickly become unwieldy. You should use a thread management mechanism to throttle this process.
Simplified class:
class WebRunner
{
private List<string> _corruptList = new List<string>();
private List<WebBrowser> _browsers = new List<WebBrowser>();
public void Run()
{
_corruptList.Add("http://google.com");
_corruptList.Add("http://yahoo.com");
_corruptList.Add("http://bing.com");
DoDelete();
Console.ReadKey();
}
public void DoDelete()
{
if (_corruptList.Count < 1) return;
int counter = 1;
foreach (string listItem in _corruptList)
{
WebBrowser webBrowser = new WebBrowser();
_browsers.Add(webBrowser);
webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBroswer_DocumentCompleted);
webBrowser.Navigated += new WebBrowserNavigatedEventHandler(webBrowser_Navigated);
webBrowser.Navigate(listItem);
if (counter % 10 == 0) Thread.Sleep(3000); // let app catch up every so often
counter++;
}
}
void webBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
Console.WriteLine("NAVIGATED: " + e.Url);
}
void webBroswer_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Console.WriteLine("COMPLETED!");
try
{
WebBrowser webBrowser = sender as WebBrowser;
HtmlDocument doc = webBrowser.Document;
var button = doc.Body.Document.GetElementById("button");
button.InvokeMember("Click");
_browsers.Remove(webBrowser);
}
catch (Exception exp)
{
Console.WriteLine(exp.StackTrace);
MessageBox.Show(exp.Message);
}
}
}
You can access the WebBrowser document content using the following (you are missing body and need to type document to dynamic).
dynamic doc = browser.Document;
var button = doc.body.document.getElementById("button");
button.Click();
I found the solution very next day. Sorry for the late post by processing threads one by one by putting the statement after thread.sleep()
if (th.ThreadState == ThreadState.Aborted || th.ThreadState == ThreadState.Stopped)