Get HTML element without 'For Each' loop - vb.net

I've started to do some programming in Visual Basic and I need some help. This might be a simple question but I can't figure this out.
So I have created a web browser and I would like to get the Profile Picture of a person on Facebook and display it on a Picture Box, and also the Name of that person and display it on a TextBox. After that the program should save the picture in a folder on my hard drive.
Private Sub WebBrowser1_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
Dim elemCollection As HtmlElementCollection = WebBrowser1.Document.GetElementsByTagName("img")
Dim fbPath As String = "C:\Users\" + Environment.UserName.ToString + "\Documents\FB Images\"
For Each curElement As HtmlElement In elemCollection
If curElement.OuterHtml.Contains("profilePic img") Then
PictureBox1.ImageLocation = curElement.GetAttribute("src") 'Showing the profile pic in picture box
TextBoxName.Text = curElement.GetAttribute("alt") 'Showing the name in a textbox
Dim NumberOfFiles As Integer = System.IO.Directory.GetFiles(fbPath).Length
Dim bmp As New Bitmap(PictureBox1.Width, PictureBox1.Height)
PictureBox1.DrawToBitmap(bmp, PictureBox1.ClientRectangle)
bmp.Save(System.IO.Path.Combine(fbPath, CStr(NumberOfFiles) + TextBoxName.Text + ".jpg"), System.Drawing.Imaging.ImageFormat.Jpeg)
End If
Next
End Sub
So the program should save the picture with a file name according to the number of files there is in the folder path(e.g '1 George Johnson.jpg'). But what happens, it is saving 5 different images because I guess there are more than one HTML elements that matches those attributes, so the For Each loop brings up more results.
Is there any way to get an HTML element without using this loop and just get a particular element that I want?

If you just want the first time the if statement for the attribute is true , add "Exit For" the line before the End If.

Related

VB.NET 2019 How to populate a Tag Property on a dynamic ToolstripmenuItem

I am struggling to populate a tag property on a dynamically created sub menu which I have created. I have a text file that contains a number of radio station names, BBC1, BBC2, BBC3 for example, as well as the associated stream addresses for said stations. I am able to pull in the names and apply it to the submenu. They appear fine. I can click on the submenus, and the sender() variable confirms the station names correctly. My problem is that I cannot get the Tag property for each sub menu/radio station, to store the stream address. The code gets the radio title cleans the code and inserts it into the RadioStreamsToolStripMenuItem.DropDownItems.Add(station_name) and all is fine. The code then gets the Stream address and inserts it here RadioStreamsToolStripMenuItem.Tag.ToString(). I can tell that it is wrong, but how do I go about ensuring the correct stream goes into the correct radio Tag property. Im very new to this so please be gentle, its just a little hobby.
'======================================================================
'Aquires Radio name and creates a dropdown sub menu within RadioStreams
'======================================================================
Do While (Not line Is Nothing)
If line.StartsWith("#") Then
station_name = Replace(line, "#", "")
Dim x = RadioStreamsToolStripMenuItem.DropDownItems.Add(station_name)
AddHandler x.Click, AddressOf ToolMenuItem_Click
'===================================================
'Aquires Radio Stream to add to the Tag property for each new station
'===================================================
ElseIf line.StartsWith("#") Then
Dim station_stream As String = Replace(line, "#", "")
'The following just checks if the data has gone into the correct place
For Each Myitem As ToolStripItem In RadioStreamsToolStripMenuItem.DropDownItems
If Myitem.Text = station_name Then
MsgBox("MyItems " & Myitem.ToString())
Me.Tag = station_stream
End If
Next
You could use a Dictionary to store the stations a stream addresses. Dictionary lookups are very fast.
I assumed from your code that your text file looks like this
#BBC1
#BBC1streamAddress
#BBC2
#BBC2streamAddress
#BBC3
#BBC3streamAddress
I looped through the lines of the file assigning the trimmed keys and values to the dictionary. Then looped through the dictionary filling the menu. When the user clicks a menu item we get the text of the item and search the dictionary for the correct stream address.
Private RadioDictionary As New Dictionary(Of String, String)
Private Sub OPCode()
Dim lines = File.ReadAllLines("Radio.txt")
For i = 0 To lines.Length - 1 Step 2
RadioDictionary.Add(lines(i).Trim("#"c), lines(i + 1).Trim("#"c))
Next
For Each item In RadioDictionary
Dim x = RadioStreamsToolStripMenuItem.DropDownItems.Add(item.Key)
AddHandler x.Click, AddressOf ToolMenuItem_Click
Next
End Sub
Private Sub ToolMenuItem_Click(sender As Object, e As EventArgs) Handles ToolMenuItem.Click
Dim station = DirectCast(sender, ToolStripDropDownItem).Text
Dim stream = RadioDictionary(station)
End Sub

Is there a way to retrieve some text from a webpage to a textbox in VB?

I'm trying to make it so I can have several text boxes in my form show pieces of information from a specific webpage. For example, would there be a way I would be able to retrieve the title of this question to a variable with the click of a button in Visual Basic?
It not hard but you have to look at the source page, and identify the elements.
In nicely formed pages, usually the div elements have a tag ID, but often they don't, so you have to grab say by a attribute name - often you can use the class name of the div in question.
So, to grab the Title, and you question text of this post?
This works:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim xDoc As New Xml.XmlDocument
Dim strURL As String = "https://stackoverflow.com/questions/55753982"
Dim xWeb As New WebBrowser
xWeb.ScriptErrorsSuppressed = True
xWeb.Navigate(strURL)
Do Until xWeb.ReadyState = WebBrowserReadyState.Complete
Application.DoEvents()
Loop
Dim HDoc As HtmlDocument = xWeb.Document
Debug.Print(HDoc.GetElementById("question-header").FirstChild.InnerText)
Debug.Print(FindClass(HDoc, "post-text"))
End Sub
Function FindClass(Hdoc As HtmlDocument, strClass As String) As String
' get all Divs, and search by class name
Dim OneElement As HtmlElement
For Each OneElement In Hdoc.GetElementsByTagName("div")
If OneElement.GetAttribute("classname") = strClass Then
Return OneElement.InnerText
End If
Next
' we get here, not found, so return a empty stirng
Return "not found"
End Function
OutPut:
(first part is title question)
Is there a way to retrieve some text from a webpage to a textbox in VB?
(second part is question text)
I'm trying to make it so I can have several text boxes in my form show pieces of
information from a specific webpage. For example, would there be a way I would be
able to retrieve the title of this question to a variable with the click of a button
in Visual Basic?

Visual Basic: reference label control from variable derived from checkbox control

I'm writing a simple Windows Form app in VB using VS Community 2017.
I have 64 checkboxes with 64 associated labels, named chk1 / lbl1 up to chk64 / lbl64. When a checkbox is checked, I want to extract a character from a string and show the answer in the label: e.g. if chk12 is checked, I want lbl12 to be enabled and the text to display the 12th character of the string.
To save writing 64 separate handlers I'm trying to do it in one. I can extract the checked number (e.g. 12) OK and write it to a string, but when I try to manipulate the label control I get an 'Object reference not set to an instance of an object' error.
The code I've come up with so far (largely from searching in here) is:
Private Sub CheckedChanged(sender As Object, e As EventArgs) _
Handles chk1.CheckedChanged, chk2.CheckedChanged 'etc. to 64
' wanted behaviour
'If chk1.Checked Then
' lbl1.Enabled = True
' lbl1.Text = GetChar(userString, 1)
'End If
'If chk2Checked Then
' lbl2.Enabled = True
' lbl2.Text = GetChar(userString, 2)
'End If
' etc. (to 64)
Dim resultsLabel As String
Dim userCheckedBox As Integer
userCheckedBox = CInt(DirectCast(sender, CheckBox).Text)
resultsLabel = "lbl" & DirectCast(sender, CheckBox).Text
Me.Controls(resultsLabel).Enabled = True
Me.Controls(resultsLabel).Text = GetChar(userString, userCheckedBox)
End Sub
I'd be very grateful if someone can nudge me over the line with this. Many thanks!
There is the old trick to use the Tag property of your checkboxes.
You can set the Tag property to the matching label name at design time using the WinForms Designer. So in the chk1.Tag property you will have the "lbl1" string assigned and so on for all the 64 checkboxes.
At this point your code in the event handler is simply
Dim chk = DirectCast(sender, CheckBox)
if chk IsNot Nothing Then
Me.Controls(chk.Tag.ToString()).Enabled = True
Me.Controls(chk.Tag.ToString()).Text = GetChar(userString, CInt(chk.Text))
End If

Web parsing issue using VB

I am very new to VB.NET and currently learning how to scrape and parse websites. My problem in a nutshell is - if I use “getElementsByClassName” more than one time in my code, it will only work the first time. Same situation with “getElementsByTagName”. And even when I just parse html code manually it will only work the first time.
Here is an example using “getElementsByClassName”. I have Form1 with Button 1 and ListBox1. I am trying to get news titles from two websites (Google and BBC) and then put them into the ListBox1. You can see I split my code into two parts. I would like to point out that both parts work very well and get the information I need, but only when used individually. When put together like in the example below, the first part (Google) will execute without problems but the second part (BBC) will give me an error on line “Dim AllItemsBBC As Object = SecondBrowser.Document.getElementsByClassName("title-link__title-text")”.
Now what’s more interesting, if I flip the code around and put the BBC part first and Google second, BBC will execute without problems and Google will give me error on line “Dim AllItemsGoogle As Object = FirstBrowser.Document.getElementsByClassName("titletext")”. Basically whichever is first executes without problems, second one fails.
The error message shows “An unhandled exception of type 'System.NotSupportedException' occurred in Microsoft.VisualBasic.dll Additional information: Exception from HRESULT: 0x800A01B6”.
Example1:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'START OF PART 1
'Creating and navigating the IE browser to Google news page
Dim FirstBrowser As Object = CreateObject("InternetExplorer.Application")
FirstBrowser.Visible = True
FirstBrowser.Navigate("https://news.google.com/news?cf=all&pz=1&ned=us")
Do
Application.DoEvents()
Loop Until FirstBrowser.readyState = 4
'Getting the titles from Google news page and adding them to ListBox1
Dim ItemGoogle As Object
Dim AllItemsGoogle As Object = FirstBrowser.Document.getElementsByClassName("titletext")
For Each ItemGoogle In AllItemsGoogle
ListBox1.Items.Add(ItemGoogle.InnerText)
Next ItemGoogle
'Closing the browser
FirstBrowser.Quit()
'END OF PART1
'START OF PART 2
'Creating and navigating the IE browser to BBC news page
Dim SecondBrowser As Object = CreateObject("InternetExplorer.Application")
SecondBrowser.Visible = True
SecondBrowser.Navigate("http://www.bbc.com/news")
Do
Application.DoEvents()
Loop Until SecondBrowser.readyState = 4
'Getting the titles from BBC news page and adding them to ListBox1
Dim ItemBBC As Object
Dim AllItemsBBC As Object = SecondBrowser.Document.getElementsByClassName("title-link__title-text")
For Each ItemBBC In AllItemsBBC
ListBox1.Items.Add(ItemBBC.InnerText)
Next ItemBBC
'Closing the browser
SecondBrowser.Quit()
'END OF PART 2
End Sub
End Class
My second example is me parsing same websites by basically just finding the phrases I need. Same situation, Google part works, BBC fails on line “Dim the_html_code_bbc As String = SecondBrowser.Document.Body.InnerHTML”.
Flip it around and BBC works and Google fails on line “Dim the_html_code_google As String = FirstBrowser.Document.Body.InnerHTML”.
The error message shows “An unhandled exception of type 'System.MissingMemberException' occurred in Microsoft.VisualBasic.dll Additional information: Public member 'InnerHTML' on type 'JScriptTypeInfo' not found.”
Example 2
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'START OF PART 1
'Creating and navigating the IE browser to Google news page
Dim FirstBrowser As Object = CreateObject("InternetExplorer.Application")
FirstBrowser.Visible = True
FirstBrowser.Navigate("https://news.google.com/news?cf=all&pz=1&ned=us")
Do
Application.DoEvents()
Loop Until FirstBrowser.readyState = 4
'Getting the titles from Google news page and adding them to ListBox1
Dim the_html_code_google As String = FirstBrowser.Document.Body.InnerHTML
Dim start_of_code_google As String
Dim code_selection_google As String
Do
Application.DoEvents()
start_of_code_google = InStr(the_html_code_google, "titletext")
If start_of_code_google > 0 Then
code_selection_google = Mid(the_html_code_google, start_of_code_google + 11, Len(the_html_code_google))
the_html_code_google = Mid(the_html_code_google, start_of_code_google + 11, Len(the_html_code_google))
code_selection_google = Mid(code_selection_google, 1, InStr(code_selection_google, Chr(60)) - 1)
ListBox1.Items.Add(code_selection_google)
End If
Loop Until start_of_code_google = 0
'Closing the browser
FirstBrowser.Quit()
'END OF PART1
'START OF PART 2
'Creating and navigating the IE browser to BBC news page
Dim SecondBrowser As Object = CreateObject("InternetExplorer.Application")
SecondBrowser.Visible = True
SecondBrowser.Navigate("http://www.bbc.com/news")
Do
Application.DoEvents()
Loop Until SecondBrowser.readyState = 4
'Getting the titles from BBC news page and adding them to ListBox1
Dim the_html_code_bbc As String = SecondBrowser.Document.Body.InnerHTML
Dim start_of_code_bbc As String
Dim code_selection_bbc As String
Do
Application.DoEvents()
start_of_code_bbc = InStr(the_html_code_bbc, "title-link__title-text")
If start_of_code_bbc > 0 Then
code_selection_bbc = Mid(the_html_code_bbc, start_of_code_bbc + 24, Len(the_html_code_bbc))
the_html_code_bbc = Mid(the_html_code_bbc, start_of_code_bbc + 24, Len(the_html_code_bbc))
code_selection_bbc = Mid(code_selection_bbc, 1, InStr(code_selection_bbc, Chr(60)) - 1)
ListBox1.Items.Add(code_selection_bbc)
End If
Loop Until start_of_code_bbc = 0
'Closing the browser
SecondBrowser.Quit()
'END OF PART 2
End Sub
End Class
Another thing worth mentioning is that if I use one method of parsing for the Google part and a different method for BBC, everything works great.
I must be missing something due to my inexperience with Visual Studio. I am using Express 2013 for Windows Desktop version. If you know what’s causing this issue, I would greatly appreciate your advice.

Highlight all lines that contain "admin" in a richtextbox

I am Making a chat for my Game launcher, when you launch it up theres a button that Opens a Chat Forum with a richtextbox on it that it refreshes/timer checks when the text has changed in the txt file on the server has changed from textbox1 then it will reprint the text but thats not the problem, i have an admin panel for it, but i want to highlight lines where there is "!ADMINISTRATOR!" in it so it shows the user is an admin and it highlighted to show what he is saying. i've tried this
Dim index As Integer = Me.RichTextBox1.Find("!ADMINISTRATOR!")
If index <> -1 Then
Dim lineindex As Integer = Me.RichTextBox1.GetLineFromCharIndex(index)
Dim first As Integer = Me.RichTextBox1.GetFirstCharIndexFromLine(lineindex)
Dim last As Integer = Me.RichTextBox1.GetFirstCharIndexFromLine(lineindex + 1)
If last = -1 Then last = Me.RichTextBox1.TextLength
Me.RichTextBox1.Select(first, last - first)
Me.RichTextBox1.SelectionBackColor = Color.Yellow
End If
But that works, in a way, it does not always highlight it, while it will highlight sometimes, it also may highlight the text a just just put, but that i don't mind, the main problem is that it only highlights the First line with the text in it, so if the admin posts a message on it it will highlight <3 but then a user will chat and then admin posts another message, it only highlights the first line that contains that text. if you need any more info oh and for testing purposes im using a local file on my pc, ive tested the ftp it works, but just to save time im using a txt file this is the code i use for the timer
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
If (System.IO.File.ReadAllText(Get_Directory_Current() & "\FilesDB\Chat\Chat.txt") = RichTextBox1.Text) Then
Else
Try
RichTextBox1.Text = System.IO.File.ReadAllText(Get_Directory_Current() & "\FilesDB\Chat\Chat.txt")
RichTextBox1.SelectionStart = RichTextBox1.Text.Length
RichTextBox1.ScrollToCaret()
ColorChat() ' this is the color chat that kinda dont work
Catch ex As Exception
End Try
End If
End Sub
if you can help thanks :) but for now i'm just trying new things. i guess it would kinda work a bit like syntax highlighting if you make it highlight a line instead of word.
Oh and if you wanted to know this is how i add the text to the chat / formatting
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
RichTextBox1.AppendText("<[" & Get_Time() & " " & strDate & "] " & "!ADMINISTRATOR!" & " | " & Environment.UserName & "> " & TextBox3.Text & vbNewLine)
End Sub
Use String.Replace() to inject the Rtf formatting where your target string appears.
Dim stringToFind = "!ADMINISTRATOR!"
Dim txt = Me.RichTextBox1.Text
Dim sb = New System.Text.StringBuilder()
sb.Append("{\rtf1\ansi\deff0 {\colortbl;\red0\green0\blue0;\red255\green0\blue0;}")
sb.Append(txt.Replace(stringToFind, String.Format("\cf2{0}\cf1", stringToFind)))
sb.Append("}")
Me.RichTextBox1.Rtf = sb.ToString()