I am new to windows.forms programming. I started making an application that has the following flow of events:
click a button that opens a file - extract its contents in some list of strings - visit some websites - parse their content - etc.
So because everything in my app happens after i click a button to open a file, I have put all my code on the click event of the button. However I do know this is bad coding practice, because I realised I ended up having ALL the program flow inside that click event. I know that the event should only contain code related to the button, but where to place the code that follows, if not inside the event? Is there another event that I should use instead of just writing all in the button click?
I hope I've made my question clear. If not then I'll retry to explain my problem. I simply don't know where to write the code that follows the click event. If I put it in:
public Form1()
{
InitializeComponent();
}
..then it executes before the click event which is wrong.
Thank you in advance.
The typical way to do this is to write one or more methods that perform the action, and call those from the click event. For any long-running actions, do them in a background worker thread.
For example:
public void myButton_OnClick(EventArgs e, object sender)
{
VisitWebSites();
}
private void VisitWebSites()
{
var webSiteList = GetWebSitesFromFile();
foreach (var w in webSiteList) {
StartVisitingWebSite(w);
}
}
private IEnumerable<string> GetWebSitesFromFile()
{
// whatever
}
private void StartVisitingWebSite(string url)
{
backgroundWorker1.RunWorkerAsync(url);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string url = (string)e.Argument;
e.Result = VisitWebSite(url);
}
private string VisitWebSite(string url)
{
// This is called in background thread. Do whatever you do to return data.
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error || e.Cancelled)
return;
string result = e.Result.ToString();
// Do whatever you do with the result
}
Look at the BackgroundWorker documentation to see how to perform actions in a background thread and then handle their results.
You can encapsulate all of the work that you want to do into another function in the form's class. Its modification access would be PRIVATE of course (unless you want another class to be able to access the method). From inside of your button_click event handler, you can call this new function. That is simplest way to do this. Otherwise, you can use the example provided in the link above by #Robert S.
You should look into the Model View Presenter pattern. http://msdn.microsoft.com/en-us/magazine/cc188690.aspx
Related
I'd like to have a TextBlock changed when the button is pressed, and then return to the previous state when the button is released.
It appears that RepeatButton is not a solution here, as it only reacts to itself being held and not released - and I need to know when it is released so that I can run a proper method to return TextBlock to its original state. Being desperate I also tried to loop while(button.IsPressed) (yeah, I know, awful idea :() but to no avail - the code would hang (as if IsPressed did not change to false after the button had been released).
Is there any way to achieve it? Thanks in advance.
Maybe not the cleanest way, but I decided to create multiple handlers to my button: Click, PointerPressed, PointerCancelled, PointerCaptureLost, PointerReleased. First two are for handling the button being pressed, while the last three are for handling the release. I used all three due to recommendation here:
http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.uielement.pointerreleased
This is because PointerReleased may sometimes be substituded by other events being fired at button release.
PreviewMouseDown and PreviewMouseUp seem to work fine, if you want both left and right click to have your desired effect:
public partial class MainWindow : Window
{
private string TextBlockPreviousState = "";
public MainWindow()
{
InitializeComponent();
ButtonStatusTextBlock.Text = "foo";
}
private void StoreAndUpdate()
{
TextBlockPreviousState = ButtonStatusTextBlock.Text;
ButtonStatusTextBlock.Text = "Button Down";
}
private void Restore()
{
ButtonStatusTextBlock.Text = TextBlockPreviousState;
}
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
StoreAndUpdate();
}
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
Restore();
}
}
I'm doing this custom control, for selecting letters from a focusable Panel.
Each letter is selected on MouseDown event.
What I've been trying to do is to create an only public event, (and thus know when a letter, any, has been clicked), let's say LetterClick , which is reached with any call to Letters_MouseDown. With this code, the event is reached form client, but the application hangs.
public event MouseEventHandler LetterClick;
private void LettersPanel_Load(object sender, EventArgs e)
{
foreach (var letter in collection)
{
// get all labels and suscribe each one to a single private event
letter.MouseDown += new MouseEventHandler(Letters_MouseDown);
}
// public event
this.LetterClick += Letters_MouseDown;
}
private void Letters_MouseDown(object sender, MouseEventArgs e)
{
// ... container get focus, draw selected effect image, formatting,
// set Letter property, etc...
if (LetterClick != null)
{
LetterClick(sender, e);
}
}
protected virtual void OnLetterClick(object sender, MouseEventArgs e)
{
MouseEventHandler handler = LetterClick;
if (handler != null)
{
handler(sender, e);
}
}
From client:
lettersPanel1.LetterClick += new MouseEventHandler(lettersPanel1_LetterClick);
private void lettersPanel1_LetterClick(object sender, MouseDownEventArgs e)
{
string letterSelected = myCustomControl1.Letter;
}
I think there is some kind of infinte looping here, but I'm pretty inexperienced about events and delegates, plus the MSDN documentation is not very clear for me yet.
If anyone can give me some help I'll be very thankful.
This line is causing the error -
// public event
this.LetterClick += Letters_MouseDown;
It is subscribing to the event and then again calling itself recursively -
if (LetterClick != null)
{
LetterClick(sender, e);
}
Remove the first line and it should be ok -
private void LettersPanel_Load(object sender, EventArgs e)
{
foreach (var letter in collection)
{
// get all labels and suscribe each one to a single private event
letter.MouseDown += new MouseEventHandler(Letters_MouseDown);
}
// public event
//this.LetterClick += Letters_MouseDown; //remove this line
}
NOTE: It is not a good idea to call the handlers directly, because it will hang the calling thread. So if the calling thread is the UI thread (for example a Click Event or a Load Event), it will be hanged until the process is completed.
A more better way would be to use async and await.
EDIT:
MSDN has a very good article on async and await. Try this link - Walkthrough: Accessing the Web by Using Async and Await (C# and Visual Basic). But it is always possible to get intimidated by the feature easily, so make sure you read some good articles on async and await when you have time. Here is a good one -
Best Practices in Asynchronous Programming
Letters_MouseDown is a handler on LetterClick, then inside that method you invoke LetterClick again.
That's the infinite loop.
I have an extended method for messagebox.Show(), which uses IWin32Window of the parent winform to center the MessageBox on it.
However, I can't seem to understand how can we possibly use this same extension method to pop-up MessageBox on the main/parent winform from within BackgroundWorker.
The extension method is something like this:
MessageBoxExTn.Show(IWin32Window parentForm, string MsgText, string Caption, MessageBoxButtons);
I mean, I want to call this method from BackgroundWorker's DoWork event with threadsafe access to the parent/main winform.
You could use event in this case and fire event when you want to show the message box. Now in your parent/main form handle this event and show the message box. This way you can achieve this.
You need to leverage the BackgroundWorker event ProgressChanged. This will do the thread switching for you. So imagine something like this:
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
MessageBoxExTn.Show(parentForm,
e.UserState as string,
someCaption,
MessageBoxButtons.OK);
}
and that's called like this:
backgroundWorker1.ReportProgress(1, "The message you want displayed.");
You can add another method on your Main Form and do something like this
public void ShowMessage(string cap,string message)
{
if (this.InvokeRequired)
{
Action<string, string> action = ShowMessage;
this.Invoke(action, new object[] {cap, message});
return;
}
MessageBoxExTn.Show(IWin32Window parentForm, string MsgText, string Caption, MessageBoxButtons);
}
please take a look at Control.InvokeRequired to understand more thread-safe in windows form
call your code
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ShowMessage();
}
I am appending the string failing_client to a RichTextBox control named report_output_box in a WinForms app using C#.
Is there a to make that string clickable inside the RichTextBox control, then pass a string to the onclick method?
What are my options here?
Update: I need to be more specific. I do not want to open a URL on the OnClick action, but instead, call a function.
Sample Code:
//... code before this
//failing_client_list is list of servers
foreach (string failing_client in failing_client_list)
{
//link onclick will call a method w/ a string argument
report_output_box.LinkClicked += new
LinkClickedEventHandler(open_inet_window(failing_client));
report_output_box.AppendText(failing_client + "\n");
}
//code after...
//method it would call
private void open_inet_window(object sender, EventArgs e, string failing_client)
{
//create a window object (w/ window consturctor) and then open it
inet_clients_window inizzy_window = new inet_clients_window(failing_client);
inizzy_window.Show();
}
Thanks!
Please take a look at: http://www.codeproject.com/Articles/9196/Links-with-arbitrary-text-in-a-RichTextBox
The article describes a way to create custom links in a RichTextBox that do not start with http:// or ftp://.
Generally however, if you want to add links that do start with http://, then all you need to do is make sure that the property DetectUrls of the RichTextBox is set to true, then you must add a handler for the LinkClicked event.
Doing a little search on StackOverFlow, you will find a very good source: How can I make a hyperlink work in a RichTextBox?
Here is a quick example I made, notice that throug e.LinkText you can create a Switch() statement for example to then decide on what to do depending on the link URL value?
namespace WindowsFormsApplication11
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
richTextBox1.LinkClicked += new LinkClickedEventHandler(richTextBox1_LinkClicked);
}
void richTextBox1_LinkClicked(object sender, LinkClickedEventArgs e)
{
MessageBox.Show("You clicked the link: " + e.LinkText);
}
private void Form1_Load(object sender, EventArgs e)
{
richTextBox1.AppendText("This is link1: http://www.google.com");
richTextBox1.AppendText("\nThis is link2: http://www.stackoverflow.com");
}
}
}
In our window base c# project. We use the pageinteractorcreater i call this function using page viewer delegate I use for that following code
but it works at the second time page load event, but we want to invoke it on menustrip item click.....
public Form1()
{
InitializeComponent();
//it works
pagesViewer1.ZoomMode = ZoomMode.FitPageWidth;
pagesViewer1.PageInteractorCreated += new
EventHandler<TallComponents.Interaction.WinForms.Events.InteractorEventArgs>
(pagesViewer1_PageInteractorCreated);
}
private void editTextToolStripMenuItem_Click(object sender, EventArgs e)
{
// it does not work
pagesViewer1.ZoomMode = ZoomMode.FitPageWidth;
pagesViewer1.PageInteractorCreated += new
EventHandler<TallComponents.Interaction.WinForms.Events.InteractorEventArgs>
(pagesViewer1_PageInteractorCreated);
}
It doesn't work mean it never raises the event. Use menu strip clicked event, instead of editTextToolStripMenuItem_Click. And create a method and put your code in it.
You can see the complete list of event here.