Accessing a control by specifying its container? - c#

My WinForms program uses a WebBrowser control, and it creates a new TabPage with a WebBrowser control inside whenever the NewWindow event is triggered, using the following code:
private void WebBrowser1_NewWindow(object sender, System.ComponentModel.CancelEventArgs e)
{//open new window in new browser tab
string newUrl = browser.Document.ActiveElement.GetAttribute("href");
string pageName = "Page " + (tabControl1.TabCount + 1).ToString();
TabPage detailsTab = new TabPage(pageName);
Button closeTab = new Button();
closeTab.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
closeTab.Location = new Point(10, 10);
closeTab.Text = "Close Tab";
closeTab.Click += new EventHandler(btnCloseTab_Click);
tabControl1.TabPages.Add(detailsTab);
WebBrowser detailsBrowser = new WebBrowser();
detailsBrowser.Dock = DockStyle.Fill;
detailsBrowser.Navigate(newUrl);
detailsTab.Controls.Add(detailsBrowser);
detailsTab.Controls.Add(closeTab);
closeTab.BringToFront();
tabControl1.SelectedIndex = tabControl1.TabCount -1;
}
So that each time a new browser tab is opened, a WebBrowser control called detailsBrowser is added to that tab. The problem is, if I need to get an HTML element from that browser, using something like:
HtmlElement saveButton = detailsBrowser.Document.GetElementsByTagName("input").GetElementsByName("save")[0];
I have no idea which detailsBrowser will be used to pull that HtmlElement.
I tried using
tabControl1.SelectedTab.Controls["detailsBrowser"]
instead of just referencing the browser by name, but this doesn't work and returns null when I try to get an HtmlElement from the browser.
Does anyone have a recommendation for how to specify a control using its container when multiple controls have the same name? Or is there a way to give each programmatically generated WebBrowser control a unique name?
Thanks!
EDIT: Here's the method I'm trying to use with the WebBrowser to select HTML elements:
public void addNote(string noteText)
{
WebBrowser currentBrowser = (WebBrowser) tabControl1.SelectedTab.Controls["detailsBrowser"];
HtmlElement showNotesButton = null;
//var links = browser.Document.GetElementsByTagName("div");
var links = currentBrowser.Document.GetElementsByTagName("div");
foreach (HtmlElement link in links)
{
if (link.GetAttribute("className") == "user_info_link")
{
showNotesButton = link;
}
}
//HtmlElement noteBox = browser.Document.GetElementById("remark");
HtmlElement noteBox = currentBrowser.Document.GetElementById("remark");
//HtmlElement submitNote = browser.Document.GetElementById("save_note");
HtmlElement submitNote = currentBrowser.Document.GetElementById("save_note");
showNotesButton.InvokeMember("click");
noteBox.Focus();
noteBox.InnerText = noteText;
Thread.Sleep(50);
submitNote.InvokeMember("click");
}

Related

Programatically created a new tab page that contains a web browser. How to make the tab page text be same as the web browser document title?

So, the user will click a menustrip item. This will create a new tabpage in the tab control, that will contain a web browser and a textbox. How can I make the web browser's document title be the same as the tabpage text? This is my code:
private void newTabToolStripMenuItem_Click(object sender, EventArgs e)
{
TabPage tabpage = new TabPage();
tabpage.Text = "New Tab";
tabControl1.Controls.Add(tabpage);
WebBrowser webbrowser = new WebBrowser();
TextBox textbox = new TextBox();
textbox.Parent = tabpage;
textbox.Dock = DockStyle.Top;
webbrowser.Parent = tabpage;
webbrowser.Dock = DockStyle.Fill;
webbrowser.ScriptErrorsSuppressed = true;
webbrowser.Navigate("https://www.google.com");
}
Tried:
tabpage.text = webbrowser.DocumentTitle;
and:
tabpage.text = webbrowser.DocumentTitle.ToString();
but didn't work. Well, it compiled but after I clicked the toolstripmenuitem it gave me an error.
When you create your WebBrowser control, use the DocumentCompleted event to update the TabPage you created:
WebBrowser webbrowser = new WebBrowser();
webbrowser.DocumentCompleted += (s1, e1) => { tabpage.Text = webbrowser.DocumentTitle; };

read the texts of run-time added textboxes

I have an application where the user can add any number of tabpages depending on what he/she needs. These tabpages have identical controls (textboxes and labels).
The controls on each tabpages are named like this: (on tabpage1 the controls are named: txtServer1, txtPort1, txtUser1, txtDbName1. if the user clicks the 'add another connection button' the application creates a second tabpage and the controls will be named: txtServer2, txtPort2, txtUser2, txtDbName2) and so on...
for example if the user have multiple tabpages to set-up:
the user clicks the 'add another connection' and the another tabpage with identical controls has been created and the user fills it up with appropriate data:
same thing goes here:
here's the code for dynamically added tabpage with controls:
//when 'add another connection' button is clicked
private void btnAddConnection_Click(object sender, EventArgs e)
{
string tabTitle = "Connection " + (tabControl1.TabCount + 1).ToString();
TabPage tabPage = new TabPage(tabTitle);
tabControl1.TabPages.Add(tabPage);
}
//when another tabpage has been added to tabcontrol
private void tabControl1_ControlAdded(object sender, ControlEventArgs e)
{
//control instances
TextBox txtServer = new TextBox();
TextBox txtPort = new TextBox();
TextBox txtUser = new TextBox();
TextBox txtDbName = new TextBox();
Label lblServer = new Label();
Label lblPort = new Label();
Label lblUser = new Label();
Label lblDbName = new Label();
tabControl1.SelectedTab = tabControl1.TabPages[tabControl1.TabCount - 1]; //select the newly addded tabpage
tabControl1.SelectedTab.BackColor = tabPage1.BackColor; //tabpage background color
//lblServer Properties
lblServer.Location = lblServer1.Location;
lblServer.Text = lblServer1.Text;
lblServer.ForeColor = lblServer1.ForeColor;
lblServer.Name = "lblServer" + tabControl1.TabCount.ToString();
//lblPort Properties
lblPort.Location = lblPort1.Location;
lblPort.Text = lblPort1.Text;
lblPort.ForeColor = lblPort1.ForeColor;
lblPort.Name = "lblPort" + tabControl1.TabCount.ToString();
//lblUser Properties
lblUser.Location = lblUser1.Location;
lblUser.Text = lblUser1.Text;
lblUser.ForeColor = lblUser1.ForeColor;
lblUser.Name = "lblUser" + tabControl1.TabCount.ToString();
//lblDbName Properties
lblDbName.Location = lblDbName1.Location;
lblDbName.Text = lblDbName1.Text;
lblDbName.ForeColor = lblDbName1.ForeColor;
lblDbName.Name = "lblDbName" + tabControl1.TabCount.ToString();
//txtserver properties
txtServer.Location = txtServer1.Location;
txtServer.Width = txtServer1.Width;
txtServer.Height = txtServer1.Height;
txtServer.Font = txtServer1.Font;
txtServer.Name = "txtServer" + tabControl1.TabCount.ToString();
//txtport properties
txtPort.Location = txtPort1.Location;
txtPort.Width = txtPort1.Width;
txtPort.Height = txtPort1.Height;
txtPort.Font = txtPort1.Font;
txtPort.Name = "txtPort" + tabControl1.TabCount.ToString();
//txtuser properties
txtUser.Location = txtUser1.Location;
txtUser.Width = txtUser1.Width;
txtUser.Height = txtUser1.Height;
txtUser.Font = txtUser1.Font;
txtUser.Name = "txtUser" + tabControl1.TabCount.ToString();
//txtdbname properties
txtDbName.Location = txtDbName1.Location;
txtDbName.Width = txtDbName1.Width;
txtDbName.Height = txtDbName1.Height;
txtDbName.Font = txtDbName1.Font;
txtDbName.Name = "txtUser" + tabControl1.TabCount.ToString();
//add controls to tabpage
tabControl1.SelectedTab.Controls.Add(lblServer);
tabControl1.SelectedTab.Controls.Add(lblPort);
tabControl1.SelectedTab.Controls.Add(lblUser);
tabControl1.SelectedTab.Controls.Add(lblDbName);
tabControl1.SelectedTab.Controls.Add(txtServer);
tabControl1.SelectedTab.Controls.Add(txtPort);
tabControl1.SelectedTab.Controls.Add(txtUser);
tabControl1.SelectedTab.Controls.Add(txtDbName);
}
When the user clicks the save button, I want the application to read each text in the textboxes (except for that url text) so that I can save it to a configuration file.
all I can think of is this
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
string connection;
for (int i = 1; i <= tabControl1.TabCount; i++ )
{
connection = "server=" + txtServer[i].Text + ";port=" txtPort[i].Text + ";user=" + txtUser[i].Text + ";dbname=" + txtDbName[i];
//save to config file code goes here...
}
}
and I know this is not correct.
any solution for this? thanks :)
This is the simplest way you can try this just use below function to get text from control in tabpage
public string getValue(string controlName, TabPage selectedTab)
{
if (selectedTab.Controls.ContainsKey(controlName)){
TextBox selectedtb = (TextBox)selectedTab.Controls[controlName];
return selectedtb.Text;
}
else
return null;
}
and in your save code use it like below
string connection;
int i = 1;
TabControl.TabPageCollection pages = tabControl1.TabPages;
foreach (TabPage page in pages)
{
connection = "server=" + getValue("txtServer"+i,page) + ";port=" +getValue("txtPort"+i,page) + ";user=" + getValue("txtUser"+i,page) + ";dbname=" + getValue("txtDbName"+i,page);
i++;
//save to config file code goes here...
}
txtServer[i].Text it's true but You have not defined an array.
You can do it like this
//global
List<TextBox> txtServerList= new List<TextBox>();
private void tabControl1_ControlAdded(object sender, ControlEventArgs e)
{
txtServer.Location = txtServer1.Location;
txtServer.Width = txtServer1.Width;
txtServer.Height = txtServer1.Height;
txtServer.Font = txtServer.Font;
txtServer.Name = "txtServer" + tabControl1.TabCount.ToString();
txtServerList.Add(txtServer)
.
.
.
}
Finally
for (int i = 1; i <= tabControl1.TabCount; i++ )
{
connection=txtServerList.get(i).Text + ......
}
You could just loop over the controls and find the matching name, something like this:
foreach(var page in tabControl1.TabPages){
var server = ProcessControls(page.Controls, "txtServer");
//... continue with the others
}
private TextBox ProcessControls(Control ctrlContainer, string name)
{
foreach (Control ctrl in ctrlContainer.Controls)
{
if (ctrl.GetType() == typeof(TextBox))
{
if(ctrl.Name.StartsWith(name))
return (TextBox)ctrl;
}
}
}
You can use an ArrayList to store and access all dynamically added controls. Set a name for each dynamically added controls. It can be based on the size of ArrayList.
I will not show the exact systax of c#.
first
declare an ArrayList or List(Type Safe)
List<Button> buttons = new List<Button>();
We just created a storage for our buttons that will be added at runtime.
void your_event (some parameters) {
Button temp = new Button("Hello");
temp.Name="button_name";
YourMainPanel.Controls.add(temp);
//after creating your button add it to the parent container. of any control where you want this button to be added.
//you can also set the coordinates of the button if you like
//after creating the button we need to add it to our List so that we can
//access it later, since the value of the 'temp' will change after this
//event was invoked in the future.
buttons.add(temp);
}
There are several ways to get the items in List<T>. One is by using index.
void someMethod(some parameters) {
Button button = buttons.ElementAt(index);
}

How do I get WebBrowsers url from the main Window C# WPF

i have no idea how to get the Url from the WebBrowser that gets created with c# code and does not exist in the Xaml before the code is execeuted.. The AddTabItem() function is called at the start at the program and later on if user wants to add more TabItems user can click on the add button.
public List<TabItem> tabItem;
public MainWindow()
{
try {
InitializeComponent();
//nardimo array za TabItem
tabItem = new List<TabItem>();
//Dodamo zaznamek (tabItem)
TabItem novTab = this.AddTabItem();
//bajndam tab
tabControl.DataContext = tabItem;
tabControl.SelectedIndex = 0;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public TabItem AddTabItem()
{
int count = tabItem.Count;
TabItem tab = new TabItem();
tab.Header = string.Format("Zavihek {0}", count+1);
tab.Name = string.Format("zavihek{0}", count);
WebBrowser wb = new WebBrowser();
wb.Name = string.Format("Brskalnik{0}", count);
string a = "http://www.google.com";
wb.Navigate(a);
Url.Text = a;
tab.Content = wb;
tabItem.Insert(count, tab);
return tab;
}
Then user can search on the Web and wanted to make a bookmark.. so how do I get the Url from the site when he clicks on the add bookmark button? How do I access the WebBrowser since it is not located in the Xaml code? Keep in mind I have to add bookmarks from the second window and not the main one. I am really sorry for my bad English and would be really happy if someone could help <3 Thank you guys
What you want is:
wb.Source.AbsoluteUri
If you drop a WebBrowser on your window and call it "wb", and a Button then you can do this in the code behind:
using System.Diagnostics;
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
wb.Navigate("http://google.com");
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine(wb.Source.AbsoluteUri);
}
}
}
You could use the references that you add to the List<TabItem>:
WebBrowser wb = tabItem[tabItem.Count - 1].Content as WebBrowser;
string url = wb.Source.AbsoluteUri.ToString();

Passing and Storing Strings

I'm trying to pass some strings(className & classID) and store them in a different variable(classNameClicked & classIDClicked) based on what hyperlink the user clicked.
Here is my code behind(C#) for creating the hyperlinks:
TextBlock txt = new TextBlock();
txt.TextWrapping = TextWrapping.Wrap;
txt.Foreground = Brushes.Black;
txt.FontFamily = new FontFamily("Times New Roman");
txt.FontSize = 25;
txt.Margin = new Thickness(5);
TextBlock ClassID = new TextBlock();
ClassID.Visibility = Visibility.Collapsed;
ClassID.Text = classID;
Run run = new Run(className);
Hyperlink link = new Hyperlink(run);
link.Click += Link_Click;
txt.Inlines.Add(ClassID);
txt.Inlines.Add(link);
myStackPanel.Children.Add(txt);
frame.Content = myStackPanel;
I was able to pass the string from the className to classNameClicked by extracting the text from the hyperlink using the code below:
private void Link_Click(object sender, RoutedEventArgs e)
{
classNameClicked = ((sender as Hyperlink).Inlines.FirstInline as Run).Text;
classIDClicked = (sender as Textblock).Text;
Class_Page class_page = new Class_Page();
NavigationService.Navigate(class_page);
}
However, I cant seem to figure out how to extract the string from classID.
Please help.
You just have to reference the parent of your Hyperlink.
So instead of
classNameClicked = ((sender as Hyperlink).Inlines.FirstInline as Run).Text;
classIDClicked = (sender as TextBlock).Text;
you can write
Hyperlink link = sender as Hyperlink;
classNameClicked = (link.Inlines.FirstInline as Run).Text;
classIDClicked = (link.Parent as TextBlock).Inlines.OfType<Run>().First().Text;
If you don't want to use LINQ you could also write:
classIDClicked = ((link.Parent as TextBlock).Inlines.FirstInline as Run).Text;
But I have the same overall opinion as Mat in the comments. This is really ugly and MVVM would be the way to go for writing much cleaner code.
Additionally you should probably add some null checking too.
Edit as requested in the comment:
If you don't want to display the classID in your GUI then you could hide it in the Tag of the Hyperlink:
Hyperlink link = new Hyperlink(run);
link.Tag = classID;
link.Click += Link_Click;
And then just get it in your Click handler:
classIDClicked = link.Tag as string;
You don't need a hidden TextBlock for that.

Show a link to a path in a message form in c#

I have a desktop application in C# .In this I have a form in which I show different messages .
I have one message who say : "The output file was generated in : C:\Work\result.txt".
How can I show this path to the file as a link and when the form with this message is shown to see the path as a link and when the user click the link to open the specified path/file?
I tried :
The output file was generated in : C:\Work\result.txt
But doesn't work.
Thanks !
You can have an event for on-onclick, and then you can open file using the below code.
System.Diagnostics.Process.Start(#"C:\Work\result.txt"); //or like
System.Diagnostics.Process.Start(#"C:\Work\result.docx");
Here, the default program must be there for the file. Then only shell will run associated program reading it from the registry, like usual double click does in explorer.
MessageBox.Show() method takes the caption, text, icon, buttons and default button of the dialog.However there's nothing mentioned in the .NET Framework documentation that says anything about adding links to a MessageBox
However, you can do the effect you want by creating a new class inherited from System.Windows.Forms.Form and add a button (or more if you like), an icon, a label and a LinkButton. Then use the ShowDialog() Method of the Form class to display the message box in modal form. You may also create a class called MyErrorBox (static class in C# 2 or just sealed in C# 1) that contains only one static method called Show() which creates a form, adds the needed controls and displays the form in modal mode. A demonstration of the last method is shown below. Then you can use this class whenever you want and wherever you please!
using System;
using System.Windows.Forms;
using System.Drawing;
namespace MessageBoxes{
public sealed class MyErrorBox{
private MyErrorBox(){}
private static Form frm;
private static string detailsStore;
private static TextBox txt;
public static DialogResult Show(string caption, string text, string details, Icon icon){
frm = new Form(); frm.Size = new Size(510, 195);
frm.Text = caption; frm.ShowInTaskbar = false; frm.ControlBox = false;
frm.FormBorderStyle = FormBorderStyle.FixedDialog;
PictureBox icon1 = new PictureBox(); icon1.Location = new Point(8,16);
icon1.Size = new Size(icon.Width, icon.Height);
icon1.Image = icon.ToBitmap();
frm.Controls.Add(icon1);
Label lbl = new Label(); lbl.Text = text; lbl.Location = new Point(88,8);
lbl.Size = new Size(400,88); frm.Controls.Add(lbl);
LinkLabel btn1 = new LinkLabel(); btn1.Text = "View Details";
btn1.Size = new Size(72,23); btn1.Location = new Point(416,96);
btn1.Click += new EventHandler(btn1_Click); frm.Controls.Add(btn1);
//Ofcourse you can add more buttons than just the ok with more DialogResults
Button btn2 = new Button(); btn2.Text = "&Ok";
btn2.Size = new Size(72,23); btn2.Location = new Point(224,130);
btn2.Anchor = AnchorStyles.Bottom; frm.Controls.Add(btn2);
frm.AcceptButton = btn2; btn2.Click += new EventHandler(btn2_Click);
btn2.DialogResult = DialogResult.OK; detailsStore = details;
return frm.ShowDialog();
}
private static void btn1_Click(object sender, EventArgs e) {
frm.Size = new Size(510,320);
txt = new TextBox(); txt.Multiline = true;
txt.ScrollBars = ScrollBars.Both; txt.Text = detailsStore;
txt.Size = new Size(488,128); txt.Location = new Point(8,120);
txt.ReadOnly = true; frm.Controls.Add(txt);
LinkLabel lnk = (LinkLabel)(sender); lnk.Text = "Hide Details";
lnk.Click -= new EventHandler(btn1_Click);
lnk.Click += new EventHandler(btn1_ReClick);
}
private static void btn2_Click(object sender, EventArgs e) {
frm.Close();
}
private static void btn1_ReClick(object sender, EventArgs e) {
frm.Controls.Remove(txt); frm.Size = new Size(510, 195);
LinkLabel lnk = (LinkLabel)(sender); lnk.Text = "View Details";
lnk.Click -= new EventHandler(btn1_ReClick);
lnk.Click += new EventHandler(btn1_Click);
}
}
}
There's no standard MessageBox functionality that'll be able to do this via link label. What I suggest is you use the Yes/No Messagebox buttons and from the option selected you then apply an event
Something like this:
if (MessageBox.Show(
"The file is saved at the following link: link here", "Success", MessageBoxButtons.YesNo, MessageBoxIcon.Asterisk
) == DialogResult.Yes)
{
System.Diagnostics.Process.Start(#"C:\TestLocation\SavedFiles");
}

Categories