I'm using a WebBrowser control to automate management of a web page. When I start my WPF application, it shows the webpage ant lets me login. Then my application starts to do some automated tasks by going to a page, filling a form and submitting (it’s a POST-form). It submits the same form ~100 times with different values.
Right now my code is as follows:
void webBrowser1_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
var doc = (HTMLDocument) webBrowser1.Document;
var form = doc.getElementById("theForm");
SetValueOfFormInput(doc, "id", Foo.ID);
SetValueOfFormInput(doc, "item1", Foo.Item1);
SetValueOfFormInput(doc, "item2", Foo.Item2);
form.all.item("submit").click();
}
private static void SetValueOfFormInput(HTMLDocument doc, string name, string value)
{
var item = doc.all.item(name);
item.setAttribute("value", value);
}
Can I do this on a better way and can I do it in a MVVM way?
And no, I can't modify the the webpage to do the management easier :-(
Edit:
Ideally, I would be able to do this without having to use the WebBrowser control. The program logon to the website and performs all tasks without having to modify the forms in the html pages
Why not using the WebClient or WebRequest classes?
For the webclient, you can use the UploadValues method which will do exactly what you want (http://msdn.microsoft.com/en-us/library/9w7b4fz7.aspx) and you can also simply addapt the class to use cookies so your login will be "permanent" (http://stackoverflow.com/questions/1777221/c-using-cookiecontainer-with-webclient-class)
If you like to do it even more model driven, i would use the WebRequest (has allready a cookiecontainer) and have some class with the needed data. This one would derive from a class, which can serialize all needed properties into a simple string you would post to the server - AFAIK it's same like the getter-parameters (param1=val1¶m2=val2&...)
so basically:
class Data : Postable { public string Param1{get;set;} public string Param2{get;set;} ...}
class Postable
{
public override string ToString()
{
StringBuilder ret = new StringBuilder();
foreach(Property p in GetType().GetProperties())
{
ret.Append("{0}={1}&", p.Name, p.<GetValue>);
}
return ret.ToString();
}
}
Related
I do almost all of my programming in VB.net (all flavors). I am now been assigned a task to make a new routine in an existing C# application. What I want to be able to do is pass a string variable to a class where I can figure out device type of a symbol handheld and figure out where an executable resides on device.
I am trying to keep the class to contain changes we make going forward in one place.
so a brief description is on a screen there will be a button. on that button click I want pass the text of the button to a (what would be a module in VB) a class and depending on text being passed and device type call a separate executable that lives on the device.
Everything I have tried so far has thrown errors.
On my button click i have
String Reponse = clsCallAction("Activity");
but that gets a message that clsCallAction is a type but is used like a variable.
here is the smaple of clsCallaction
internal static partial class clsCallAction
{
public static object GetPath(object lAppName)
{
string resp = "";
if (lAppName.Equals("Activity"))
{
resp = #"\application\activity.exe";
}
return resp;
}
}
If I put new in front of the clsCallAction("Activity") on button click I get a
cannot create instance of the static class 'clsCalACtion'
appreciate any pointers. very new at C#
It would look something like this:
public static class CallAction
{
public static object GetPath(object lAppName)
{
string resp = "";
if (lAppName.Equals("Activity"))
{
resp = #"\application\activity.exe";
}
return resp;
}
}
And would be used like this:
String Reponse = CallAction.GetPath("Activity");
Don't prefix classes with cls
Avoid using object if possible - it just makes everything harder work than it needs to be.. Kinda like calling everything "thing" - ("Put the thing in the thing and open the thing" is harder to understand than "put the key in the lock and open the door")
I have a multilingual web-forms web application which I am using resource files and a BasePage class which sets the culture based on the QueryString that was included in the page which is inheriting from this class. This is all working well, if a tad clunky as I am having to do this sort of thing for every button which takes the user to a different page to maintain the culture:
if (Thread.CurrentThread.CurrentCulture.ToString() == "cy-GB")
{
return "~/Secure/Details.aspx?lang=cy-GB&PersonId=" + currentPersonId;
}
else
{
return "~/Secure/Details.aspx?PersonId=" + currentPersonId;
}
I knew there was probably a better way of doing this but being a novice as it worked I simply made do.
This was until I had to implement an asp:SiteMapPath control. I initially assumed that I could simply create a resource entry for the url property like I had done for the title:
<siteMapNode
title="$resources:SiteMapLocalizations,HomePageTitle"
description="Home"
url="~$resources:SiteMapLocalizations,HomePageUrl">
However this resulted in a server error:
A potentially dangerous Request.Path value was detected from the client (:).
I've done some reading and I believe I need to somehow store the current culture to a session variable which will follow the user around so when they click 'Home' on the breadcrumb it will be consistent with the culture and grab the appropriate text from the resource files, I'm also hoping this will allow me to remove all of the IF ELSE statements I've had to write to maintain the current language throughout the application.
My question is however, where do I start with this, I cannot find any guide step by step to follow in order to achieve this, can anyone provide some instructions?
Make sure you have a button of some sort which triggers the change of language. In my case I have two versions of the header, one with an English link which will append the Query String to English and one for Welsh, something like:
ASP
<a id="languagelink" runat="server" title="Cymraeg">Cymraeg</a>
C#
if (Thread.CurrentThread.CurrentCulture.ToString() == "en-GB")
{
Uri uri = new Uri(currentPage);
languagelink.HRef = String.Format(uri.GetLeftPart(UriPartial.Path)+"?lang=cy-GB");
}
Every page which requires the language switch needs to inherit from my custom BasePage like so:
Inheritance
public partial class Secure_CustomerSummary : BasePage
Base_Page
public partial class BasePage : System.Web.UI.Page
{
protected override void InitializeCulture()
{
if (Session["language"] == null)
{
Session["language"] = "en-GB";
}
else
{
if (Request.QueryString["lang"] == null)
{
SetSessionCulture();
}
if (Request.QueryString["lang"] != null)
{
string qs = Request.QueryString["lang"];
Session["language"] = qs;
}
SetSessionCulture();
}
SetSessionCulture();
}
private void SetSessionCulture()
{
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(Session["language"].ToString());
Thread.CurrentThread.CurrentUICulture = new CultureInfo(Session["language"].ToString());
base.InitializeCulture();
}
}
i have one page i.e default.aspx and its in the main root and i have another folder which has a file with name of test.aspx . it means my test file is => Folder->test.aspx .
Now can you help me that how can i declare a string variable in my main file ( default.aspx ) and call or change its value in my test.aspx file .
I tryed this code but i didnt get result :-
Thank you
in default.cs
public static class globalvar
{
public static string test="null";
}
in folder->test.cs
class program
{
public static void main()
{
globalvar.test = "arash";
}
}
Why don´t you use Session state?
in default.cs
public void SetSessionValue (string value)
{
Session["test"] = value;
}
And on the other page:
in folder->test.cs
public string GetSessionValue ()
{
return Session["test"];
}
There are numerous ways in accomplishing the above said tasks, selecting an appropriate way for an appropriate situation is vested in the hands of programmer. Some of the most prominent techniques are:
QueryString.
PreviousPage.FindControl() and Request.Form[]
Session State
Cookies
Application Variables
Context.Items[]
See link with examples http://www.intstrings.com/ramivemula/asp-net/data-transfer-between-two-asp-net-pages/
session
You can use session instead using global .
In your main page you should :
Session["test"] = "arash";
and if you want to access its value just do this :
string test = Session["test"];
For more information take a look :
http://www.codeproject.com/Articles/32545/Exploring-Session-in-ASP-Net
Url
You can use urlto transfer your value between the pages
here is an example :
http://www.website.com/test/names.asp?test=arash
And to get the values:
<%
Response.Write(Request.QueryString("test"))
%>
The file names.asp would display the following:
arash
I'm making bot for online game.
It works, but it is singlethread application.
I want to make it multithread application.
I know how background worker works.
To all my tasks I use one WebClient with added Cookie support.
My for example needs to open one page, wait 10 min and do next instruction.
I also want to be able to stop bot at any time.
Do I have to pass my WebClient object to background worker to work with?
What is the best way to update controls on my Form?
I have one class that has all the values that I want to show on Main Form.
Should I fire some event when property changes? If yes, can you give me example?
UPDATE:
This is my Special WebClient:
using System;
using System.Net;
namespace Game_Bot
{
class WebClientEx : WebClient
{
public CookieContainer CookieContainer { get; private set; }
public WebClientEx()
{
CookieContainer = new CookieContainer();
}
public void ClearCookies()
{
CookieContainer = new CookieContainer();
}
protected override WebRequest GetWebRequest(Uri address)
{
var request = base.GetWebRequest(address);
if (request is HttpWebRequest)
{
(request as HttpWebRequest).CookieContainer = CookieContainer;
}
return request;
}
}
}
Is this good way of updating UI? Or is there any beter?
public void SetStatus(string status)
{
if (TransferLeftLabel.Dispatcher.Thread == Thread.CurrentThread)
{
TransferLeftLabel.Text = status;
}
else
{
TransferLeftLabel.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(Action)(() => { SetStatus(string status); }));
}
}
This is how I would do it:
First:
I like to manage threads manually instead of using the BackgroundWorker control when making multithreads applications like the one you want to modify.
To start a new thread, it is as simple as:
public void SomeMethod() {
var thread = new Thread(MyMethod);
thread.Start(); //Will start the method
}
public void MyMethod() {
//Do whatever you want inside the thread here
}
You can get as many Thread instances as you want, store them in a list, and manage how you prefer. However, it isn't true that the more threads the better. Search in Google.
About opening pages and keeping Cookies.
I think you could have an instance of a class in your Form, or where you have the logic (some place that threads can access), (let's name it WebUtils) with a method like: GoToUrl(<url here>) or something like that, and a CookieCollection as a field in that class to keep cookies.
Something you should take in count:
When calling GoToUrl, you might need to do lock when accessing the cookies variable, to avoid inconsistency.
About updating controls:
You can create an event inside the class WebUtils, and everytime the page is accessed you can fire this event. Before starting the threads, you must subscribe to the event in your Form, you can do something similar with lock when updating/accessing/modifying controls in your form.
Now, how to avoid the message Control ____ accessed from a thread other than the thread it was created...?
Here's an example:
If you want to modify property Text of the control textBox1, instead of just doing:
textBox1.Text = "Ey, I accessed the site
you can do:
MethodInvoker m = () => { textBox1.Text = "Ey, I accessed the site" };
if (InvokeRequired)
BeginInvoke(m);
else
m.Invoke()
Make sure all the modifications are done like that.
This is just an overview. I'm not a thread expert.
Here is good reference about threadings in general: Threading in C#
Edit:
Take a look at the IsBackground property of threads. That could be the cause of application freezes when you just want to cose it.
I suggested creating a class WebUtils, or however you want to name, because that's how I've created it in the past.
Something like:
public class WebUtils {
CookieContainer _cookies;
public WebUtils() {
_cookies = new CookieContainer();
}
public void AccessPage(string url) {
//Here I create a new instance of a HttpWebRequest class, and assign `_cookies` to its `Cookie` property.
//Don't really know if `WebClient` has something similar
}
}
Hi can I'm very new to windows forms. Here I want to maintain state (like session in web applications) in windows forms.
Actually i want to store user login details in session. But i think there is no concept of session in winforms. So what is the alternative method to handle this type of situation.
Regards,
Nagu
There is no concept of Session variables in windows forms. What you can do is:
Create a internal class that holds the User name and password and any other variables and enumerations needed across the application (Something like Common.cs). These can be accessed through public properties across the application.
Have a parameterized constructor for all the forms and send the user name and the password whenever you are showing the form.
public class MyForm : Form
{
private string userName;
private string password;
}
Since windows forms are statefull (opposed to stateless for web forms), you can just use a field in your Form class.
You need to think more in terms of scope than session; as long as an object remains in scope you will be able to pull values from its public properties/fields.
In your case it would make sense to store the user details in a static class:
public static class LoginInfo
{
public static string UserID;
}
Now you can access the UserID simply from anywhere in your code:
MessageBox.Show(LogInfo.UserID);
In winforms you can use variables that are exposed to other forms through methods or properties.
You can also use static variables.
In the following example, you would have a controller for each window or group of windows. The controllers would be passed to one another depending on how they need to collaborate (what knowledge they need to share, etc). The important thing is to keep your application state in the controllers and limit the windows to handling user input and events.
// pseudocode, because I do not know WinForms that much
class MainController
{
private Guid securityToken;
public Guid SecurityToken
{
get { return securityToken; }
set { securityToken = value; }
}
}
class LoginWindowController
{
MainController mainController;
LoginWindow loginWindow;
public LoginWindowController(MainController mainController)
{
this.loginWindow = new LoginWindow(this);
this.mainController = mainController;
}
public void Show()
{
loginWindow.IsVisible = true;
}
public void HandleLogin()
{
Guid token =
myobject.Authenticate(loginWindow.Username, loginWindow.Password);
if (token != Guid.Empty)
{
mainController.SecurityToken = token;
}
}
}
In reply to your comment to my first reply:
You are creating the new instance of the Login form. How is that supposed to have values. It is a Login form and hence I believe you will be closing it as the user enters user name and password and clicks OK or whatever.
Then, there is no way you can get the values from the Login form as it is closed. If you need to stick to this approach, this could be a way:
Do not close the Login form, just hide it.
Pass the current instance to the next form. Like this:
In Login form:
NextForm nxt = new NextForm(this);
The constructor of NextForm will look like:
public NextForm(LoginForm frm){
// Code here
}
Now in NextForm, you can access the properties through "frm".
from a program i was using with a login form to store global variables and to store the password as a secure string. Within the program I am able to "run as" a specific user when I call processes. You can use it for other things besides process.start.
//to run process as another user
//create these global variables on the first
//form or piece of code in your program
class usernameGlobalVariable
{
public static string var = "";
}
class passwordGlobalVariable
{
public static SecureString var;
}
// use these as event handlers for text fields
//for your login form
private void usernameTextBox_TextChanged(object sender, EventArgs e)
{
usernameGlobalVariable.var = usernameTextBox.Text;
}
private void passwordTextBox_TextChanged(object sender, EventArgs e)
{
SecureString passWord = new SecureString();
foreach (char c in passwordTextBox.Text.ToCharArray())
{
passWord.AppendChar(c);
}
passwordGlobalVariable.var = passWord;
}
//put this on form that launches program
//this assigns variables for process.start
//change fileName to path and name of program
// use \\ in paths
string fileName = "c:\\hdatools\\Ping2.exe";
string arguments = "";
string domain = "domain";
//start the process
//put this on the page along w the above variables that
//launches the app as another user
//the .var variables are global
{
Process.Start(
fileName,
arguments,
usernameGlobalVariable.var,
passwordGlobalVariable.var,
domain);
}
It's unclear to me whether you are talking about a web application or a stand along application based upon one of your responses. If you are talking about a web application, you can use the Session properties on the Page object.
It would set the variables like this:
Session["username"] = "Username";
Session["fullname"] = "User's full name";
You could then access like:
lblGreetings.Text = "Hi " + Session["fullname"];
Is that what you were after?