I have a listview which I populate from a webservice query. Problem is that if the http query is slow due to network or the URL is wrong then the query freezes the UI for a bit. So my question is how do I throw this off to a side process and populate the list in the background?
Here is how I get the data and it usually sits for 30 secs or more at request.GetResponse(); I get JSON back and parse that but this function is the hold up and would like to wrap it in something. So how would I do that specifically?
public static String get(String url) {
StringBuilder sb = new StringBuilder();
// used on each read operation
byte[] buf = new byte[32768];
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.Timeout = 10000;
HttpWebResponse response;
try {
response = (HttpWebResponse)request.GetResponse();
} catch (WebException we) {
return "ERROR!"
}
Stream resStream = response.GetResponseStream();
... read from stream and parse
}
#### Using suggestion of Worker Thread ############
This doesn't work either.
// create new thread
new Thread(new ThreadStart(myTable) ).Start();
inside the myTable method using an anonymous delegate I thought might be the best way.
private void myTable() {
if (this.InvokeRequired) {
this.Invoke(new EventHandler(delegate(object obj, EventArgs a) {
// do work here
String url = "http://someurl....";
// Set the view to show details.
listView1.View = View.Details;
// Display check boxes.
listView1.CheckBoxes = false;
// Select the item and subitems when selection is made.
listView1.FullRowSelect = true;
listView1.Items.Clear();
listView1.Columns.Clear();
listView1.Columns.Add(Resources.MainColumn0, 20, HorizontalAlignment.Left);
listView1.Columns.Add(Resources.MainColumn1a, 250, HorizontalAlignment.Left);
listView1.Columns.Add(Resources.MainColumn2, 150, HorizontalAlignment.Left);
try {
// Call the function mentioned above to get the data from the webservices....
string users = get(urlUsers);
.............
Use a worker thread to make the query, the use Control.Invoke to update the UI or use an async call to get the data from the service. Without knowing more about exactly how you're getting the data and doing population, that's about as detailed as I can get.
EDIT
Your attempt at offloading the work to a thread is subverted by your call to Invoke, which moves everything back to the UI thread. You need to do something along these lines:
private void MyThreadProc()
{
// get the data from the web service here
this.Invoke(new EventHandler(delegate
{
// update your ListView here
}));
}
Note that the long-running part of the command is not contained within the Invoke call.
Related
A method was created to be initialized with my Windows Form Application when I start it. However, when I call my method just below the InitializeComponent();, my whole Windows Form Application doesn't start, and it doesn't throw me any error.
Myclass mc = new Myclass();
public Interceptor()
{
InitializeComponent();
mc.myMethod();
>rest of the code
}
This is the class with the method:
public class Listener
{
public void myMethod() {
//Recieving the parameters to listen
Int32 port = 5000;
IPAddress ip = IPAddress.Parse("1.1.1.1");
TcpListener server = new TcpListener(ip, port);
//starting the server
server.Start();
//Accepting connection from the Client
Socket socket = server.AcceptSocket();
//Storing the data
byte[] bytes = new byte[4096];
int k = socket.Receive(bytes);
//???
ASCIIEncoding asen = new ASCIIEncoding();
string data = null;
data = Encoding.ASCII.GetString(bytes, 0, k);
//Reading all the data using the TextReader()
TextReader reader = new StringReader(data);
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
MyObject obj = (MyObject)serializer.Deserialize(reader);
string json = JsonConvert.SerializeObject(icx, Formatting.Indented, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
});
string path = #"c:/temp";
string name = "test";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
string fileName = $"{path}\\{name}.json";
File.WriteAllText(fileName, json);
}
}
As you guys can see, my method is deserializing a XML object and serializing to JSON object. When I add this method to a button, it works perfectly. I basically need this method to be initialized with my application, and the user doesn't need to press any button to run it. I spent thre days without any progress.
Hope I made myself clear.
Cheers!
Your Interceptor() method is the constructor of (what appears to be) the Form. So this is called when you create the form and the job of the constructor is initialisation of the form, settings defaults etc.
By placing your mc.myMethod() within the constructor you are actually forcing it to start your business logic already. This means, it will be performed before the form is finished being created (and shown).
So this is not good and as your code in this method blocks the UI thread then this is why you have this problem, because you don't allow the form to finished being created before you are processing data and blocking the UI.
You should instead use Form.Load event, or optionally the Form.Activate or Form.Shown events.
To understand the difference and which one suits you best, see this:
https://learn.microsoft.com/en-us/dotnet/desktop/winforms/order-of-events-in-windows-forms?view=netframeworkdesktop-4.8
Even though this would be a better approach, you still have the issue that the code is blocking the UI thread.
So then you need to look at using other methods to avoid this.
There are different ways to do this, but one is to use a backgroundworker.
//Initialise the worker and set the worker method
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
//Start the worker
bw.RunWorkerAsync();
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//Put your myMethod code here
}
See this for more details and the full capability of the backgroundworker and better examples:
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=net-6.0
I have an ASP.NET WebForms application that mimics a help desk system. The application works fine, but recently, they asked me to make it so that it can text message everyone in the system whenever a new help desk ticket is opened.
I am using Twilio to do this and it is working just fine. The only problem is, there are like 15 people in the system that should be getting this text message and when the ticket is submitted, the application takes about 15-20 seconds to repost from the submit. In the future, there could be more then 15 people, double that even.
What I am wondering is if there is a way to send these messages in the background, so that the page will come back from the submit right away. Here is my relevant code:
This is my main method I wrote for sending the text message. Its in a Utility class:
public static string SendSms(string phoneNumber, string message)
{
var request = (HttpWebRequest)WebRequest.Create("https://api.twilio.com/2010-04-01/Accounts/" + Constants.TwilioId + "/Messages.json");
string postData = "From=" + Constants.TwilioFromNumber + "&To=+1" + phoneNumber + "&Body=" + HttpUtility.HtmlEncode(message);
byte[] data = Encoding.ASCII.GetBytes(postData);
string authorization = string.Format("{0}:{1}", Constants.TwilioId, Constants.TwilioAuthToken);
string encodedAuthorization = Convert.ToBase64String(Encoding.ASCII.GetBytes(authorization));
string credentials = string.Format("{0} {1}", "Basic", encodedAuthorization);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
request.Headers[HttpRequestHeader.Authorization] = credentials;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
string responseString;
using (var response = (HttpWebResponse) request.GetResponse())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
responseString = reader.ReadToEnd();
}
}
return responseString;
}
And here is how I'm calling it:
public void BtnSubmit_Click(object sender, EventArgs e)
{
//
// This is more code here, but its irrelevant
//
var employees = new Employees();
employees.GetAll();
foreach (Employee employee in employees)
{
string number = employee.CellPhoneAreaCode + employee.CellPhonePrefix +
employee.CellPhoneSuffix;
if (!string.IsNullOrEmpty(number) && number.Length == 10)
{
Utility.SendSms(number, "A new Help Desk Ticket is in the System!");
}
}
}
The only other idea I can come up with is to create a WCF service, but that seemed like over kill. Any suggestions are welcome!
Any asynchronous approach should do the trick. For example, using a Task or (if you're on .NET 4.5+) an async method. (Remember to handle the asynchronous errors by supplying a callback with something like .ContinueWith() to examine the task for errors and respond accordingly.)
Meaningfully responding to errors in this case might be complex, though. It sounds like the sort of operation where you want to keep re-trying in the event of a failure (with logging in case of constant failures), and definitely want to continue with the loop even if one message fails. So something a little more manual might be in order.
For that I would recommend persisting the messages themselves to a simple database table from the application and continuing with the UI as you want. Then have a separate application, such as a Windows Service, which periodically polls that database table and sends the messages in a simple loop over the records.
A good approach for something like this would be to keep a simple status flag on the message records. Queued, sent, error (with an error message), etc. The Windows Service can update the records as it sends the messages in the loop. As any given message errors, just update that record and continue with the loop. Re-try error-ed messages as appropriate.
So in my app I have a button that talks to a lib that downloads some data from the internet and filters it. When the app is doing this the screen freezes and it looks to the user like the app crashed. But this is not the case because its downloading data.
Here is my code:
GetDetailsBtn.TouchUpInside += (sender, e) => {
var defaults = NSUserDefaults.StandardUserDefaults;
if (RefNr.Text != string.Empty && RefNr.Text != null) {
FilteredDataRef = _FetchingData.getTrackTraceData (defaults.StringForKey ("SecurityToken"), RefNr.Text);
if (FilteredDataRef == null) {
UIAlertView InvalidAlert = new UIAlertView ("Reference number invalid", "The reference number that you have entered is not linked to the current security code. You can change your security code in the settings.", null, "OK", null);
InvalidAlert.Show ();
} else {
FilteredDataReceived = _FetchingData.FilteringOnReceived (FilteredDataRef);
FilteredDataPlanned = _FetchingData.FilteringOnPlanned (FilteredDataRef);
FilteredDataLoadingETA = _FetchingData.FilteringOnLoadingETA (FilteredDataRef);
FilteredDataLoadingFinal = _FetchingData.FilteringOnLoadingFinal (FilteredDataRef);
FilteredDataUnloadingETA = _FetchingData.FilteringOnUnloadingETA (FilteredDataRef);
FilteredDataUnloadingFinal = _FetchingData.FilteringOnUnloadingFinal (FilteredDataRef);
this.PerformSegue (MoveToTrackTraceDetailsSegue, this);
//foreach (string s in FilteredDataPlanned)
// Console.WriteLine (s);
}
} else {
UIAlertView InvalidAlert = new UIAlertView ("Reference number cannot be empty", "You did not provide a reference number. We need your reference number to trace identify the shipment you would like to trace.", null, "OK", null);
InvalidAlert.Show ();
}
};
Downloading of the data:
public IEnumerable<string> getTrackTraceData (string securityCode, string referenceNumber)
{
WebRequest request = WebRequest.Create ("http://plex.janssen1877.com/app/life/" + securityCode);
HttpWebResponse response = (HttpWebResponse)request.GetResponse ();
Stream dataStream = response.GetResponseStream ();
StreamReader reader = new StreamReader (dataStream);
string FetchedData = reader.ReadToEnd ();
reader.Close ();
dataStream.Close ();
response.Close ();
var FetchingDataItems = FetchedData.Split (new char[] { '\n' });
if (FetchingDataItems != null) {
var filteredResult = FetchingDataItems.Where (x => x.Contains (referenceNumber));
return filteredResult;
} else {
return null;
}
}
Now I want to use a component called BTProgressHUD. This is just a fancy spinner. I thought that if I would put BTProgressHUD.show(); to the top of the button action and BTProgressHUD.Dismiss(); to the button it would show when the loading starts and dismiss when its done loading.
This is not the case. It shows very quickly in the new view controller and dismisses again within a second. What am I doing wrong?
Edit for exemple:
public IEnumerable<string> getTrackTraceData (string securityCode, string referenceNumber)
{
string url = string.Format ("http://plex.janssen1877.com/app/life/" + securityCode);
HttpWebRequest HttpRequest = (HttpWebRequest)WebRequest.Create (url);
string FetchedData = new StreamReader (HttpRequest.GetResponse ().GetResponseStream ()).ReadToEnd ();
var FetchingDataItems = FetchedData.Split (new char[] { '\n' });
if (FetchingDataItems != null) {
var filteredResult = FetchingDataItems.Where (x => x.Contains (referenceNumber));
return filteredResult;
} else {
return null;
}
}
Florian,
According to .NET documentation and HttpWebRequest GetResponse. How to wait for that response? you need to perform the download (and maybe the parsing) in a async fashion.
The behavior of your actual application is correct. When you perform a sync request in the main thread you freeze the application and hence the UI elements are not updated. The main thread processes execution in a serial fashion.
To avoid this you have two different solutions. On the one hand, you need to move to an async request. On the other hand, you can create a background thread (a different path of execution) with a sync request. I prefer the former. So, for example, after starting the async request, show the indicator. When you have finished (in the callback), dismiss the indicator and perform the segue.
For example, you can follow the following discussion on how to achieve this: How to use HttpWebRequest (.NET) asynchronously?.
To understand how the main thread (and the run loop) works, I suggest to read about The pogo stick of NSRunLoop.
Hope that helps.
Edit
You should use a pattern like the following (source How to use HttpWebRequest (.NET) asynchronously?):
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
It sounds like you're attempting to do all this in the same thread as your UI, which is why your app freezes and waits for processing to finish without showing anything. I would perform this download operation in a backgroundworker of some sort. Then I am not sure in Mono but access the main thread before and after and show and dismiss your loading component.
Sorry if the title is not clear or correct, dont know what title should i put. Please correct if wrong.
I have this code to download images from IP camera and it can download the images.The problem is how can i do the images downloading process at the same time for all cameras if i have two or more cameras?
private void GetImage()
{
string IP1 = "example.IPcam1.com:81/snapshot.cgi;
string IP2 = "example.IPcam2.com:81/snapshot.cgi;
.
.
.
string IPn = "example.IPcamn.com:81/snapshot.cgi";
for (int i = 0; i < 10; i++)
{
string ImagePath = Server.MapPath("~\\Videos\\liveRecording2\\") + string.Format("{0}", i, i + 1) + ".jpeg";
string sourceURL = ip;
WebRequest req = (WebRequest)WebRequest.Create(sourceURL);
req.Credentials = new NetworkCredential("user", "password");
WebResponse resp = req.GetResponse();
Stream stream = resp.GetResponseStream();
Bitmap bmp = (Bitmap)Bitmap.FromStream(stream);
bmp.Save(ImagePath);
}
}
You should not run long-running code like that from an ASP.NET application. They are meant to simply respond to requests.
You should place this code in a service (Windows Services are easy), and control the service through a WCF service running inside of it.
You're also going to get into trouble because you don't have your WebResponse and Stream in using blocks.
There are several methods that will depend on how you want to report feedback to the user. It all comes down to multi-threading.
Here is one example, using the ThreadPool. Note that this is missing a bunch of error checking throughout... It is here as an example of how to use the ThreadPool, not as a robust application:
private Dictionary<String, String> _cameras = new Dictionary<String, String> {
{ "http://example.IPcam1.com:81/snapshot.cgi", "/some/path/for/image1.jpg" },
{ "http://example.IPcam2.com:81/snapshot.cgi", "/some/other/path/image2.jpg" },
};
public void DoImageDownload() {
int finished = 0;
foreach (KeyValuePair<String, String> pair in _cameras) {
ThreadPool.QueueUserWorkItem(delegate {
BeginDownload(pair.Key, pair.Value);
finished++;
});
}
while (finished < _cameras.Count) {
Thread.Sleep(1000); // sleep 1 second
}
}
private void BeginDownload(String src, String dest) {
WebRequest req = (WebRequest) WebRequest.Create(src);
req.Credentials = new NetworkCredential("username", "password");
WebResponse resp = req.GetResponse();
Stream input = resp.GetResponseStream();
using (Stream output = File.Create(dest)) {
input.CopyTo(output);
}
}
This example simply takes the work you are doing in the for loop and off-loads it to the thread pool for processing. The DoImageDownload method will return very quickly, as it is not doing much actual work.
Depending on your use case, you may need a mechanism to wait for the images to finish downloading from the caller of DoImageDownload. A common approach would be the use of event callbacks at the end of BeginDownload to notify when the download is complete. I have put a simple while loop here that will wait until the images finish... Of course, this needs error checking in case images are missing or the delegate never returns.
Be sure to add your error checking throughout... Hopefully this gives you a place to start.
Following program will connect to the web and get html content of “msnbc.com” webpage and print out the result. If it takes longer than 2 seconds to get data from the webpage, I want my method to stop working and return. Can you please tell me how can I do this with an example?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
gethtml();
MessageBox.Show("End of program");
}
public void gethtml()
{
HttpWebRequest WebRequestObject = (HttpWebRequest)HttpWebRequest.Create("http://msnbc.com/");
WebResponse Response = WebRequestObject.GetResponse();
Stream WebStream = Response.GetResponseStream();
StreamReader Reader = new StreamReader(WebStream);
string webcontent = Reader.ReadToEnd();
MessageBox.Show(webcontent);
}
}
Two seconds is far too long to block the UI. You should only block the UI if you are planning on getting the result in, say fifty milliseconds or less.
Read this article on how to do a web request without blocking the UI:
http://www.developerfusion.com/code/4654/asynchronous-httpwebrequest/
Note that this will all be much easier in C# 5, which is in beta release at present. In C# 5 you can simply use the await operator to asynchronously await the result of the task. If you would like to see how this sort of thing will work in C# 5, see:
http://msdn.microsoft.com/en-us/async
Set the Timeout property of your WebRequest object. Documentation
MSDN Example:
// Create a new WebRequest Object to the mentioned URL.
WebRequest myWebRequest=WebRequest.Create("http://www.contoso.com");
Console.WriteLine("\nThe Timeout time of the request before setting is : {0} milliseconds",myWebRequest.Timeout);
// Set the 'Timeout' property in Milliseconds.
myWebRequest.Timeout=10000;
// This request will throw a WebException if it reaches the timeout limit before it is able to fetch the resource.
WebResponse myWebResponse=myWebRequest.GetResponse();
As stated above .Timeout
public void gethtml()
{
HttpWebRequest WebRequestObject = (HttpWebRequest)HttpWebRequest.Create("http://msnbc.com/");
WebRequestObject.Timeout = (System.Int32)TimeSpan.FromSeconds(2).TotalMilliseconds;
try
{
WebResponse Response = WebRequestObject.GetResponse();
Stream WebStream = Response.GetResponseStream();
StreamReader Reader = new StreamReader(WebStream);
string webcontent = Reader.ReadToEnd();
MessageBox.Show(webcontent);
}
catch (System.Net.WebException E)
{
MessageBox.Show("Fail");
}
}
You can use the TimeOut property on HttpWebRequest
Consider switching to asynchronous downloading of the content. You will stop blocking UI thread and will be able to handle multiple requests easily. You will be able to increase timeout significantly without impact on UI, and can decide upon receiving response if you still want to fetch data.