Unable to get a specific Cell value from a Table - vba

I need to port some VBA code to a VB.Net addon, but I can't get a particular cell value from my Table.
I am able to change Range(2,2) to "Test" but table.Range(2, 2).ToString gives System.__ComObject as Value.
Private Sub Button1_Click(sender As Object, e As RibbonControlEventArgs) _
Handles Button1.Click
Dim table As Excel.ListObject
table = Globals.wsTables.ListObjects("vehicleDefinations")
table.Range(2, 2) = "Test"
Dim file As New System.IO.StreamWriter _
("C:\Users\Ryzen2600x\Downloads\Debug.txt", False)
file.WriteLine(table.Range(2, 2).ToString)
file.Close()
End Sub

Don't use .Range to retrieve the value. Instead try to use
table.DataBodyRange.Cells(2, 2)
or
[MyTable].Cells(2,2)

You're missing the .Value
file.WriteLine(table.Range(2, 2).Value.ToString)

Please reference this code:
private void ListObject_Range()
{
Microsoft.Office.Tools.Excel.ListObject list1 = this.Controls.AddListObject(this.Range["A1", "C4"], "list1");
MessageBox.Show("The list object contains " + list1.Range.Cells.Count.ToString() + " cells.");
}
More informaiton, please see
ListObject.Range Property
Help it helps you.
Thanks,
Yuki

Related

Cycle through xml data in VB.Net

I posted a question about how to read the content of external xml files in VB.Net (find it here) and so far everything is going great, but I have no idea how to cycle through the data (they are all elements called savedPassword with a specific id number). Now, I know I am supposed to give a minimum of code, but I am just starting off in XML and VB.Net and I have no idea how much code I need to give for someone to help me out with a script, so here I am, giving a paragraph of code blocks...
I have the following code so far and it works amazingly well (so if no one could modify it, that would be amazing).
My module (Overview.vb):
' Dim values for directories and paths '
Public ReadOnly DirectoryHome As String = "C:\VelocityDK Codes"
Public ReadOnly DirectoryApp As String = "C:\VelocityDK Codes\Password Manager"
Public ReadOnly DataFile As String = "C:\VelocityDK Codes\Password Manager\appData.xml"
' Dim values for .xml file '
Public ReadOnly xmlRoot As String = "savedData"
My [general] form reading the data from my xml file (frmManager.vb):
Option Strict On
Imports System.IO
Imports System.Xml.Serialization
' Some unrelated code '
' This current line is not in the code, but I am disabling the error message with an "unused member" - which is refering to the xmlRoot value right below. '
#Disable Warning IDE0051 ' Remove unused private members
Private ReadOnly xmlRoot As String = "savedData"
#Enable Warning IDE0051 ' Remove unused private members
' Class to represent the xml file '
Public Class SavedData
<XmlElement("savedPassword")>
Public Property SavedPasswords As List(Of SavedPassword)
End Class
' Class to represent data from external xml file '
Public Class SavedPassword
<XmlAttribute("id")>
Public Property ID As Byte
<XmlElement("name")>
Public Property Name As String
<XmlElement("email")>
Public Property Email As String
<XmlElement("password")>
Public Property Password As String
End Class
' Read xml content at first load '
Private Sub FrmManager_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim filename = DataFile
Dim data As SavedData
Dim serializer As New XmlSerializer(GetType(SavedData))
Using sr = New StreamReader(filename)
data = CType(serializer.Deserialize(sr), SavedData)
End Using
For Each sp In data.SavedPasswords
txtID.Text = {sp.ID}.ToString
txtName.Text = {sp.Name}.ToString
txtEmail.Text = {sp.Email}.ToString
txtPassword.Text = {sp.Password}.ToString
Next
End Sub
Finally, my .xml file (appData.xml located in the directory C:\VelocityDK Codes\Password Manager) looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<savedData>
<savedPassword id="01">
<name>Name 01</name>
<email>email01#mail.com<email>
<password>password01</password>
</savedPassword>
<savedPassword id="02">
<name>Name 02</name>
<email>email02#mail.com<email>
<password>password02</password>
</savedPassword>
<!-- Other sections like the aboves going from id's 03 to 06 -->
<savedPassword id="07">
<name>Name 07</name>
<email>email07#mail.com<email>
<password>password07</password>
</savedPassword>
</savedData>
In brief, I have two buttons (btnPrevious & btnNext) and I want to make it so that when I click on the btnPrevious button, it goes to the previous savedPassword (located in my xml file) and vice versa for the btnNext button. How can I do so?
First of all, make your form's load event look like this.
'Make this global
Dim data As SavedData
Private Sub FrmManager_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim filename = DataFile
Dim serializer As New XmlSerializer(GetType(SavedData))
Using sr = New StreamReader(filename)
data = CType(serializer.Deserialize(sr), SavedData)
End Using
'Remove for loop to display just the first record. .
txtID.Text = {data.SavedPasswords(0).ID}.ToString
txtName.Text = {data.SavedPasswords(0).Name}.ToString
txtEmail.Text = {data.SavedPasswords(0).Email}.ToString
txtPassword.Text = {data.SavedPasswords(0).Password}.ToString
End Sub
Next, keep an index somewhere in your program for cycling back and forth the list.
Dim data As SavedData
Dim currentIndex As Integer = 0
Next, Under the button click events, add the following code
'Next button
Public Sub BtnNext_Click(sender As Object, e As EventArgs)
If currentIndex < data.SavedPasswords.Count() Then
currentIndex += 1
Else
MessageBox.Show("End of data reached")
End If
txtID.Text = {data.SavedPasswords(currentIndex).ID}.ToString
txtName.Text = {data.SavedPasswords(currentIndex).Name}.ToString
txtEmail.Text = {data.SavedPasswords(currentIndex).Email}.ToString
txtPassword.Text = {data.SavedPasswords(currentIndex) .Password}
End Sub
'Previous button
Public Sub BtnPrevious_Click(sender As Object, e As EventArgs)
If currentIndex > 0 Then
currentIndex -= 1
Else
MessageBox.Show("This is the first record!")
End If
txtID.Text = {data.SavedPasswords(currentIndex).ID}.ToString
txtName.Text = {data.SavedPasswords(currentIndex).Name}.ToString
txtEmail.Text = {data.SavedPasswords(currentIndex).Email}.ToString
txtPassword.Text = {data.SavedPasswords(currentIndex).Password}
End Sub
I would do the following.
First, create some global variable which would keep the current saved password ID. Then the following procedure will search for the next ID. Note that the actual getting XML must be realized by you.
Private curr_id$ = "01" '//Global variable
'// The direction we're searching
Enum Direction
Forward
Backward
End Enum
'// Get the <savedPassword> element. The function returns Nothing,
'// if it doesn't find ID.
Function GetSavedPassword(direction As Direction) As XElement
Dim obj_xml =
<?xml version="1.0" encoding="UTF-8"?>
<savedData>
<savedPassword id="01">
<name>Name 01</name>
<email>email01#mail.com</email>
<password>password01</password>
</savedPassword>
<savedPassword id="02">
<name>Name 02</name>
<email>email02#mail.com</email>
<password>password02</password>
</savedPassword>
<!-- Other sections like the aboves going from id's 03 to 06 -->
<savedPassword id="07">
<name>Name 07</name>
<email>email07#mail.com</email>
<password>password07</password>
</savedPassword>
</savedData>
Dim next_id = -1 '//ID we're searching (initial state)
Dim curr_id_num = CInt(curr_id) '//Convert string to int
'// Get all IDs from XML
Dim ids = obj_xml.<savedData>.<savedPassword>.Select(Function(x) CInt(x.#id))
'// Next we compare the current ID with available IDs
If direction = Direction.Forward Then
'// If we need to go FORWARD,
'// we must get all IDs which are greater than current id
Dim next_ids = ids.Where(Function(id) id > curr_id_num)
'// Make sure we have found something -
'// in this case it's safe to call Min()
If next_ids.Any() Then next_id = next_ids.Min()
ElseIf direction = Direction.Backward
'// If we need to go BACKWARD,
'// we must get all IDs which are less than current id
Dim next_ids = ids.Where(Function(id) id < curr_id_num)
'// Make sure we have found something -
'//in this case it's safe to call Max()
If next_ids.Any() Then next_id = next_ids.Max()
End If
'// If we found id, it will be greater than 0
If next_id > 0 Then
Dim id_string = If(next_id <= 9, "0" & next_id, next_id)
Return obj_xml.<savedData>.<savedPassword>.
Where(Function(p) p.#id = id_string).
FirstOrDefault()
End If
End Function
'// Usage
Sub Main()
Dim saved_password As XElement = GetSavedPassword(Direction.Forward)
If saved_password IsNot Nothing Then
'// Update current id
curr_id = saved_password.#id
Dim name = saved_password.<name>(0)
Dim email = saved_password.<email>(0)
Dim password = saved_password.<password>(0)
'// Update state of the program
'// ....
End If
End Sub

Pasting new records to a Bound DataGridView

Apologies if this has already been asked. If so, I am unable to find a simple solution. I am trying to allow a user to copy/paste multiple records in a DataGridView (the in memory copy of the data, to be saved later when the user clicks the save button) and cannot find anything that works. It probably is because there is something I do not understand about all of this.
I set up a standard edit form with Visual Studio's drag/table into a form, so it's using a BindingSource control and all the other controls that come with doing that. It works just fine when manually entering something in the new row one by one, so it seems to be set up correctly, but when it comes to adding a record (or multiples) using code, nothing seems to work.
I tried a few things as outline in the code below. Could someone please at least steer me in the right direction? It cannot be that difficult to paste multiple records.
I run this when the user presses Control-V (the clipboard correctly holds the delimited strings):
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item(1) = Items(0)
drv.Item(2) = Items(1)
drv.Item(3) = Items(2)
drv.Item(4) = Items(3)
'Error on next line : Cannot add external objects to this list.
AdjustmentsBindingSource.Add(drv)
Next
End If
End Sub
EDIT
(the bindingsource is bound to a dataadapter, which is bound to a table in an mdb file, if that helps understand)
I adjusted the inner part of the code to this:
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
It kinda works, but it also adds a blank row before the 2 new rows. Not sure where that is coming from, as I have even included your RowHasData() routine...
I would think that “attemp3” SHOULD work, however, it is unclear “what” the AdjustmentsBindingSource’s DataSource is. Is it a List<T> or DataTable?
If I set the BinngSource.DataSource to a DataTable, then attempt 3 appears to work. Below is an example that worked.
Private Sub PasteClipboard2()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
Next
End If
End Sub
This appears to work in my tests. I added a small function (RowHasData) to avoid malformed strings causing problems. It simply checks the size (at least 5 items) and also checks to make sure a row actually has “some” data. If a row is just empty strings, then it is ignored.
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
I am guessing it would be just as easy to add the new rows “directly” to the BindingSource’s DataSource. In the example below, the code is adding the row “directly” to the DataTable that is used as a DataSource to the BindingSource. I am confident you could do the same thing with a List<T> by simply adding a new object to the list. Below is a complete example using a BindingSource and a DataTable. This simply adds the rows to the bottom of the table.
Dim gridTable1 As DataTable
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PasteClipboard()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
gridTable1 = GetTable()
FillTable(gridTable1)
AdjustmentsBindingSource.DataSource = gridTable1
AdjustmentsDataGridView.DataSource = AdjustmentsBindingSource
End Sub
Private Function GetTable() As DataTable
Dim dt = New DataTable()
dt.Columns.Add("FontName", GetType(String))
dt.Columns.Add("FontSize", GetType(String))
dt.Columns.Add("LetterCombo", GetType(String))
dt.Columns.Add("Adjustment", GetType(String))
dt.Columns.Add("HorV", GetType(String))
Return dt
End Function
Private Sub FillTable(dt As DataTable)
For index = 1 To 10
dt.Rows.Add("Name_" + index.ToString(), "Size_" + index.ToString(), "Combo_" + index.ToString(), "Adjust_" + index.ToString(), "HorV_" + index.ToString())
Next
End Sub
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
Try
Dim dataRow As DataRow
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
dataRow = gridTable1.NewRow()
dataRow("FontName") = Items(0)
dataRow("FontSize") = Items(1)
dataRow("LetterCombo") = Items(2)
dataRow("Adjustment") = Items(3)
dataRow("HorV") = Items(4)
gridTable1.Rows.Add(dataRow)
End If
Next
Catch ex As Exception
MessageBox.Show("Error: " + ex.Message)
End Try
End If
End Sub
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
Hope the code helps…
Last but important, I am only guessing that you may have not “TESTED” the different ways users can “SELECT” data and “how” other applications “copy” that selected data. My previous tests using the WIN-OS “Clipboard” can sometimes give unexpected results. Example, if the user selects multiple items using the ”Ctrl” key to “ADD” to the selection, extra rows appeared in the Clipboard if the selection was not contiguous. My important point is that using the OS clipboard is quirky IMHO. I recommend LOTS of testing on the “different” ways the user can select the data. If this is not an issue then the code above should work.

How to create my commands except select case method vb.net

Making a textbased game. I have a command prompt its made from richtextbox as outputbox and
textbox for inputtextbox. i need to make some commands like "cls" "dir" "config". lots of more commands in my list. i just stuck how to that and how to approach to solution.
Here is my code i tyred some of 'em with select case method but its too primitive.
Private Sub Output(s As String)
If s <> "" Then
nCounter = nCounter + 1
sCounter = Convert.ToString(nCounter)
consoleoutputbox.AppendText(vbCrLf & sCounter & " " & s)
End If
End Sub
Private Sub consoleinputbox_KeyDown(sender As Object, e As KeyEventArgs) Handles consoleinputbox.KeyDown
Dim Command As String = consoleinputbox.Text
If e.KeyCode = Keys.Enter Then
If Command <> "" Then
Select Case Command
Case "cls"
consoleoutputbox.Clear()
consoleinputbox.Clear()
consoleinputbox.Focus()
nCounter = 0
Case "help"
Output("Welcome to help section. Avaliable commands:")
Output("help, cls")
Case Else
Output(Command)
consoleinputbox.Clear()
consoleinputbox.Focus()
End Select
End If
End If
End Sub
Perhaps a Dictionary(Of String, Action) would help. Put each thing(s) you want done into sub routines and add them to the dictionary with the command as the key:
Dim Commands As New Dictionary(Of String, Action)
Commands.Add("test1", New Action(AddressOf test))
Then just pass the string to the dictionary as the index and invoke the sub routine
Commands("test1").Invoke()
One option might be to define a module and add methods named after your commands. You could then use Reflection to find and invoke the method with the name entered by the user as a command.

Visual Basic rename a group of files

I'm starting with Visual Basic and use Visual Studio 2012 and trying to make a tool for renaming a group of files.
How it shoul be:
1- With the "Select Files" button, I can choose the files and they are listed in the ListBox
2- "Old value" is a textbox and is the value to be changed in the filename. For example: fff
3- "New value" is a textbox and is the new value that should replace the old. For example: zzz
4- Rename is a button to start the process.
To rename only one file it's not a problem.
But how to rename all the files from the ListBox which are containing the Oldvalue ?
Can you please help me!
Thanks
I suggest looping over the selected files in the listbox, checking to see which ones contain the OldValue string.
You could use string.contains http://msdn.microsoft.com/en-us/library/dy85x1sa(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
Sounds like you have the replace function sorted since you say it's no problem to do just one file.
Thank you 70Mike.
But I have some problem with the loop.
If it works only for 1 file and not for all.
Here is my code:
Public Class frmRename
Private Sub cmdSelectFile_Click(sender As Object, e As EventArgs) Handles cmdSelectFile.Click
If (OpenFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK) Then
For Each S As String In OpenFileDialog1.FileNames
lstSelectFiles.Items.Add(S)
Next
Else : Exit Sub
End If
End Sub
Private Sub cmdRename_Click(sender As Object, e As EventArgs) Handles cmdRename.Click
Try
For LC As Integer = 0 To lstSelectFiles.Items.Count - 1
Dim s1 As String = lstSelectFiles.Items(LC)
Dim s2 As String = txtbOld.Text
Dim b As String
b = s1.Contains(s2)
Console.WriteLine("Is the string, s2, in the string, s1?: {0}", b)
Do While b = True
Dim oldFile As String = lstSelectFiles.Items(LC)
Dim newFile As String = Replace(lstSelectFiles.Items(LC), txtbOld.Text, txtbNew.Text)
If File.Exists(oldFile) And Not File.Exists(newFile) Then
File.Move(oldFile, newFile)
Kill(oldFile)
End If
Loop
Next
Catch ex As Exception
MsgBox("No file renamed")
End Try
End Sub
End Class

Gridview invalid column name

I have a gridview0 in a multiview and when I click select on a row I have the GridView0_SelectedIndexChanged sub change to a different view in the multiview, which has a different gridview1 but the same datasource as gridview0. This is when it errors out and it displays the invalid column name error, with the column name being the datakeyname of the first gridview0 row that was selected.
The first image is the view of gridview0, and the second is the error that occurs when I click select. Thanks!
image one http://img291.imageshack.us/img291/9221/gridview0.jpg
image two http://img188.imageshack.us/img188/6586/gridview1.jpg
Protected Sub GridView0_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles GridView0.SelectedIndexChanged
Dim ISTag As String = GridView0.SelectedDataKey.Value.ToString
Dim type As String = getTypeMethod(ISTag)
filterText.Text = type
If (type.Equals("Computer")) Then
InventoryComputer.SelectCommand = "SELECT * FROM T_Computer WHERE ISTag = " & ISTag
MultiView1.ActiveViewIndex = 8
End If
End Sub
Adding a new datasource and setting the where to the original gridview worked.