Programmatically Add New Choice to Choice Column in SharePoint List - vb.net

I'm working with a client and trying to make it so that when a button is pressed to open new Fiscal Year (FY) forms, it also adds that FY value as a choice option in a Document Library column. I also am trying to set the default to that new value.
I'm getting the error below.
Here's my current code. Is it simply a configuration issue? Or am I doing something n00by with my code?
Imports Microsoft.SharePoint
...
Partial Class _Default
Inherits System.Web.UI.Page
...
Protected Sub SaveNew_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SaveNew.Click
...
'Added 6/12/2019 - Testing new FY options in Document Upload Form
Using site As New SPSite("http://sptest/spsite/default.aspx")
Using web As SPWeb = site.OpenWeb()
Dim leadLst As SPList = web.Lists("Document Library")
Dim col As SPFieldChoice = CType(leadLst.Fields("Fiscal Year"), SPFieldChoice)
col.Choices.Add(FY.Text)
col.DefaultValue = FY.Text
col.update()
leadLst.update()
End Using
End Using
'End Added 6/12/2019
...
End Sub

When you are creting an object of SPSite, you need to pass the URL of Site Collection only not with .aspx page
Convert your SPSite object creation line from
Using site As New SPSite("http://sptest/spsite/default.aspx")
to
Using site As New SPSite("http://sptest/spsite/")
if spsite is your subsite and above code do not work then convert that line to
Using site As New SPSite("http://sptest/")
And then get SPWeb object for the spsite subsite
Let me know if this helps or not?

Related

VB store list of records in a text file and read back

I have a VB program in VS2017. It reads many excel files and store relevant data in
Dim total_sessions As New List(Of Session_CSV_file)
The list is big (~10k), and includes about 20 fields each.
Is there a way in VB to store it in a file in one command and read it later on easily., without reading and writing each field?
Often I need to debug the later part. I prefer doing it without rerunning the whole first part.
Or is there a way in Visual Studio, to stop the run at a certain point, and store this point in a dump file, like simulators often do? (so they can run the simulation from this point later on)
Perhaps if you load your csv data into a DataTable instead:
Dim dt as New DataTable
dt.Columns.Add("Name")
dt.Columns.Add("Age", GetType(Int32))
For Each filepath in tenKFilePaths
For Each line in File.ReadAllLines(file)
Dim bits = line.Split(","c)
dt.Rows.Add(bits(0), Convert.ToInt32(bit(1))
Next line
Next filepath
Then you can save the whole lot to xml:
dt.WriteXml("c:\temp\my.xml")
Have a coffee, reboot etc the read them again and carry on where you left off:
Dim dt2 as New DataTable
dt2.ReadXml("c:\temp\my.xml")
Raw datatables are a bit difficult to work with because you end up accessing their rows and column data by string indexing and having to cast it a lot - fairly lame. There is a better way to use them, as visual studio can create custom classes that inherit from the base datatables and offer lots of functionality that does away with all the awkwardness of dealing with datatables as a very generic thing. Just like the forms designer creates a UI form that inherits from Form, the dataset designer creates custom datatables that inherit from the base
If you add a new DataSet type object to your project, double click it it opens something like a database designer. Right click that surface and choose Add DataTable, Add your Columns with their respective datatypes etc - this is the equivalent of your Session_Csv_File class, with its properties and fields. Double click on it and you can add code etc - if your session_csv_file has custom methods you can transfer their code to the custom data row
Imagine I created a new datatable in a DataSet and called it SessionCsvFiles. Imagine I added a Name and Age column, and I added a custom method called BlahBlah
I'd then be able to say in code things like:
Dim dt as New MyCustomDataSet.SessionCsvFilesDataTable
'it's like making a new SessionCsvFile
Dim r = dt.NewSessionCsvFilesRow()
'It's like setting your properties
r.Name = csvBits(0)
r.Age = Convert.ToInt32(csvBits(1))
'It's like adding your file to the List
dt.Add(r)
You now have virtually the same thing as you had before - a collection of objects that represents things about your csv files. The difference is with this route it has an already-built-in way of saving all the properties and the collection to xml and reading it back again
Add the Serializable attribute to the Class. I added a parameterized constructor for ease in building my list but you must provide a constructor without parameters for this to work. Thus, the empty Sub New.
<Serializable()>
Public Class Session_CSV_file
Public Property ID As Integer
Public Property Name As String
Public Property Type As String
Public Sub New()
End Sub
Public Sub New(SessID As Integer, SessName As String, SessType As String)
ID = SessID
Name = SessName
Type = SessType
End Sub
End Class
The add Imports System.Xml.Serialization to the top of the file.
Private Sub SaveList()
Dim serializer = New XmlSerializer(GetType(List(Of Session_CSV_file)))
Using writer As New StreamWriter("C:\Users\xxx\Documents\XMLtest.xml")
serializer.Serialize(writer, lst)
End Using
End Sub
I filled my list as follows but you can fill you list any way you choose.
Private lst As New List(Of Session_CSV_file)
Private Sub FillList()
Using cn As New SqlConnection(My.Settings.CoffeeConnection),
cmd As New SqlCommand("Select Top 10 * From Coffees;", cn)
cn.Open()
Dim reader = cmd.ExecuteReader
While reader.Read
Dim sess As New Session_CSV_file(reader.GetInt32(0), reader.GetString(1), reader.GetString(3))
lst.Add(sess)
End While
End Using
End Sub
To recreate the list... (OOps! forgot this)
Private RehydratedList As New List(Of Session_CSV_file)
Private Sub CreateListFromXML()
Dim serial As New XmlSerializer(GetType(List(Of Session_CSV_file)))
Using fs As New FileStream("C:\Users\maryo\Documents\XMLtest.xml", FileMode.Open)
RehydratedList = DirectCast(serial.Deserialize(fs), List(Of Session_CSV_file))
End Using
For Each item In RehydratedList
Debug.Print(item.ToString)
Next
End Sub

How to check for strings in a panel?

I'm making a string checker which should check for a string on a website.
I'm using a panel instead of a web browser because I merged chromium with CefSharp into it.
Now I can't figure out how to check for a string in the panel.
I tried creating a different type of variable, but the variable comes from a file.
I also tried for normal text without variables.
Both didn't work.
Public Sub New()
Dim site As String = File.ReadAllText("webstore.shd")
Dim id As String = File.ReadAllText("ID.shd")
InitializeComponent()
Dim settings As New CefSettings()
CefSharp.Cef.Initialize(settings)
browser = New ChromiumWebBrowser(site) With {
.Dock = DockStyle.Fill
}
Panel1.Controls.Add(browser)
If Panel1.Contains(id) Then
' I am stuck right here.
msgbox("Succes")
Else
msgbox("Nope")
End If
End Sub
I expected that the program would search for the variable in the panel, but it wouldn't do that.

I'm trying to add the bookmark in word using VB.net..have anyone got idea?

Imports DocumentFormat.OpenXml
Imports DocumentFormat.OpenXml.Wordprocessing
Imports DocumentFormat.OpenXml.Packaging
Public Class Add_bookmark
Const fileName As String = "F:\vb\part2 here\AddRemove.docx"
Const bookmarkName As String = "Page1"
Private Sub Add_bookmark_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Using doc As WordprocessingDocument = WordprocessingDocument.Open(fileName, True)
Dim docBody As Body = doc.MainDocumentPart.Document.Body
Dim addBookmark As BookmarkStart = docBody.Descendants(Of BookmarkStart)().FirstOrDefault(Function(a) a.Name = bookmarkName)
If addBookmark Is Nothing Then
Throw New Exception(String.Format("Bookmark {0} not found", bookmarkName))
End If
'addBookmark.InsertAt(bookmarkName)
doc.MainDocumentPart.Document.Save()
End Using
End Sub
End Class
Here's my recommendation that I think will pretty much solve most Open XML SDK issue. They have both a comparison tool and a code generation tool. Use thoughts to your advantage.
Create the document you want to see in Microsoft Word. Save It.
Open the document again, add a bookmark. Save it again, but under a different name.
Open the XML SDK comparison tool and select both documents. It'll show you the differences, and also will show you the sample .NET code that can be used to create the 2 documents. In this case, you'll focus on the differences in the code.

Controlling Internet Explorer instance VB.NET

Not too long ago I had written some code in VBA that generates two separate internet explorer sessions (each with its own cookie).
I have worked with forms and the IE WebControl in visual studio 2013 before, but found that it has compatibility issues with some sites. This is why I am attempting to open external browsers so that compatibility issues won't be a problem.
I have added all needed references to my VB.NET project and the below code more or less runs without issue. However, after I create both IE instances, I am unable to make the browsers visible even though I utilize to visible property to make them so.
Most examples I have found online only give examples using WebControl, which I am trying to avoid. Any help is much appreciated. Thank you!
Imports class SHDocVw
Public Class Form1
Public shellWins As ShellWindows
Public shellWins2 As ShellWindows
Public ie1 As SHDocVw.InternetExplorer
Public ie2 As SHDocVw.InternetExplorer 'New Object
Public ieU As Object
Public ieP As Object
Public ieU2 As Object
Public ieP2 As Object
Public oWin As New Object
Public oWin2 As New Object
Public sh As New Object
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
shellWins = New ShellWindows
shellWins2 = New ShellWindows
ie1 = shellWins.Item(0)
ie1 = New SHDocVw.InternetExplorer
ie1 = shellWins.Item(0)
ie2 = ie1
ie1.Navigate("https://www.SomeSite123.com")
ie1.visible = true
ie2.Navigate("https://www.SomeSite123.com")
ie2.visible = true
ieWait1()
ieWait2()
ieU = ie1.document.getElementById("uName")
ieP = ie1.document.getElementById("uPass")
ieU.Value = "user123"
ieP.value = "Abc123"
ieU2 = ie2,document.getElementById("uPass")
ieP2 = ie2.document.getElementById("uName")
ieU2.Value = "user123"
ieP2.value = "Abc123"
ieWait1()
ieWait2()
end sub
end class
To make the WebBrowser control use the latest installed IE version on your computer refer to this answer of mine: https://stackoverflow.com/a/42367088/3740093
As for the error you're getting: MissingMemberException occurs because the resulting HtmlElement object that you get from the WebBrowser doesn't contain a property or method called Value. To get or set an element's value attribute you have to use the HtmlElement.GetAttribute() method or the HtmlElement.SetAttribute() method.
For your specific case:
ieU.SetAttribute("value", "user123")
ieP.SetAttribute("value", "Abc123")
Also, I strongly recommend you change your variable declarations from Object to HtmlElement to avoid late-binding and these kinds of errors:
Public ieU As HtmlElement
Public ieP As HtmlElement
Public ieU2 As HtmlElement
Public ieP2 As HtmlElement
Now your application won't even compile if you try to access a member that doesn't exist. Thus saving you the trouble of having to debug it.
Finally, I couldn't help but notice your ieWait methods. I just want you to know that if they're calling Application.DoEvents() (which I'm pretty sure they do) you need to get rid of them at once! Using Application.DoEvents() to keep your UI responsive is in 99% of the cases bad practice because it is almost always used incorrectly.
Instead you should use the WebBrowser.DocumentCompleted event, which is raised every time the page (or a frame in the page) is loaded.
In this answer of mine there's an example on how you can use the DocumentCompleted event more dynamically in inline code: https://stackoverflow.com/a/48110795/3740093
- Note that you can use multiple lambda expressions within each other.

Reading and writing to an xml file with VB.Net

I am rather new to the programming world ( I'm a network guy ). I have however been asked to develop a front end that configures an xml file for a console application. The console application reads from this xml file and opens multiple instances of a browser, one instance per monitor (6 monitors in total). There are 4 control centers, each of which has 6 monitors. Each control center is run off a seperate pc. These pc's are not on the network and do not have access to each other. I have been told I can also not use a database.
Each monitor will display one website at a time, there could be multiple sites listed to be displayed on the specific monitor so they will periodically change. Each control center will show different sites.
My first question: Is this XML valid?
<ControlCenter>
<Monitor>
<monitor_id>0</monitor_id>
<browser_short_na>ie</browser_short_na>
<url_list>
<url>
<url_id>0</url_id>
<url_na><![CDATA[http://www.hmv.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
<url>
<url_id>1</url_id>
<url_na><![CDATA[http://www.amazon.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
<url>
<url_id>2</url_id>
<url_na><![CDATA[http://www.google.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
</url_list>
</Monitor>
<Monitor>
<monitor_id>1</monitor_id>
<browser_short_na>ie</browser_short_na>
<url_list>
<url>
<url_id>0</url_id>
<url_na><![CDATA[http://www.amazon.com]]></url_na>
<parameter><![CDATA[]]></parameter>
</url>
</url_list>
</Monitor>
</ControlCenter>
What I do so far is open the xml file and add all the monitors to a combobox
Dim dom As New Xml.XmlDocument
dom.Load("test.xml")
ComboBox1.Items.Clear()
Dim monitorid As String = String.Empty
For Each node As Xml.XmlNode In dom.SelectNodes("//ControlCenter/Monitor/monitor_id")
monitorid = node.InnerText
ComboBox1.Items.Add(monitorid)
Next
This is now where I am stuck. Once the users selects one of the monitors from the combobox I then need to get all the information for that monitor. So I need the browser_short_na, and all the urls all based on the monitor_id selected.
I have tried creating a dataset, loading the xmlfile using readxml. I then tried creating a dataview pointing to that dataset. Tried adding a RowFilter to the dataview.
Dim val As String = ComboBox1.SelectedItem.ToString
Dim dsXmlFile As New DataSet
dsXmlFile.ReadXml("test.xml")
Dim dv As New DataView
dv.Table = dsXmlFile.Tables(0)
Dim drv As DataRowView
dv.RowFilter = "monitor_id = " & val
Dim url As String = ""
'Retrieve my values returned in the result
For Each drv In dv
url = drv("url_na")
Next
When I step through the code and look at the for each loop it fails with the message "url_na is neither a DataColumn nor a DataRelation for table Monitor."
I am thinking I am not handling the url_list section correctly.
Once all the information for the selected monitor is read I will display the values in textboxes/listboxes which the users can then edit. If they then save it should write the new values to the xml file. They could also choose to add additional urls to the list, or even create an entirely new monitor section.
Any help/suggestions would be greatly appreciated.
I would go the other way around for your issue:
Define a Class (or several classes) that will hold the data you need.
Use a simple serializer to load/save from/to that file.
If you do so, then your issue with the selection is just a classical WPF issue.
Public Class Monitor
Public Property MonitorId As integer
Public Property ListOfUrl As List(Of String)
End Class
MonitorsConfiguration will then reference a List(Of Monitor) object.
You can use a ViewModel object to handle a MonitorsConfiguration easily.
This ViewModel has a SelectedMonitorIndex property, and updates a UrlForThisMonitor List of url when the index property changes.
(Obviously it should implement INotifyPropertyChanged)
Ok so a little view on what the ViewModel might look like :
Public Class MonitorsConfigurationVM
Implement INotifyPropertyChanged
' creates a new VM. Throws exception if file name is not valid
Public Sub New(ConfigFileName As String)
_FileName = ConfigFileName
_MonitorsConfiguration = // Deserialization of the file //
_MonitorIndex = 0
End Sub
Public Property MonitorIndex As integer
Get
return _MonitorIndex
End Get
Set (Value)
if (_MonitorIndex = value) then return
' you might want to perform check here and allow only valid index
_MonitorIndex = value
_UrlIndex=0
NotifyPropertyChanged("MonitorIndex")
NotifyPropertyChanged("MonitorUrls")
NotifyPropertyChanged("HasUrl")
NotifyPropertyChanged("UrlIndex")
End Set
End Property
Public ReadOnly Property HasUrl As Boolean
Get
return Not (MonitorUrls Is Nothing OrElse MonitorUrls.count = 0 )
' ( might be used to disable the listbox bound to MonitorUrl )
End Get
End Property
Public ReadOnly Property MonitorUrls As List(Of String)
Get
return _MonitorConfiguration(_MonitorIndex).ListOfUrl '(you might want to chk)
End Get
End Property
Public Property UrlIndex As Integer
Get
return _UrlIndex
End Get
Set (value)
if value = _UrlIndex then return
' you might want to perform some check here
_UrlIndex = value
NotifyPropertyChanged("UrlIndex")
End Set
End Property
' And Also : AddMonitor / AddUrl / SaveConfiguration / ...
Private _FileName As String = Nothing
Private _MonitorsConfiguration As List(Of Monitor)=Nothing
Private _MonitorIndex As integer = 0
Protected Sub NotifyPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
You should use XPath for parsing the XML Document.
Load the xml file in XMLDocument
Create a XMLNodeList and use XPath to select nodes from loaded xml
document.
Then parse the nodes list and extract all info for selected monitor
node based on id.
Here are XPath expression helpful to you:
For selecting monitor nodes from file: "/ControlCenter/Monitor"
For selecting browser_na field based on monitor id: "/ControlCenter/Monitor[monitor_id='0']/browser_short_na"
For selection urls based on monitor id: "/ControlCenter/Monitor[monitor_id='0']/url_list/url/url_na"
Hope it works for you.