I am not sure how to search for the issue I am trying to solve here. In the program I am writing (in VB.Net) I am trying to assign values pulled from a database to different variables in a structure.
Now my issue is that sometimes, some of the values pulled from the database are NULL, for example not every phone number has an extension. This is what I have for my code at the moment:
Structure CustomerContact
Public _name As String
Public _email As String
Public _fax As String
Public _phone1 As String
Public _phone2 As String
Public _phone3 As String
Public _ext1 As String
Public _ext2 As String
Public _ext3 As String
Public _type1 As String
Public _type2 As String
Public _type3 As String
End Structure
Dim contactData As DataTable = CustomerDBFunctions.GetCustomerContacts(Customer)
For Each row As DataRow In contactData.Rows
If contacts.Count < 1 Then
contacts.Add(New CustomerContact With {
._name = row.Item("FullName").ToString() & " (" & row.Item("ContactType").ToString() & ")",
._email = row.Item("Email").ToString(),
._fax = row.Item("Fax").ToString(),
._phone1 = row.Item("Phone").ToString(),
._ext1 = row.Item("Extension").ToString(),
._type1 = row.Item("PhoneType").ToString()})
End If
Next
Right now I am getting an error when the value in the database is NULL because it can't assign a NULL value to a string. I'd like to in the instances where a NULL value is present instead set the value of the variable to "" instead. I am just unsure how to code this.
Technically, the problem isn't that the column is null. String is a reference type, so it can but null (though, if it was null, you wouldn't be able to call ToString on it anyway). What's actually going on is that ADO.NET always returns DBNull.Value for all columns where the row contains a null value.
You could check it, like this:
If row.Item("Phone") <> DBNull.Value Then
customerContact._phone1 = row.Item("Phone")?.ToString()
End If
Note, I used ?. instead of . when calling ToString just in case the column actually is null rather than DbNull.Value. The reason for this is that I don't know what kind of code you're using to fill that DataTable. If it's ADO.NET that's filling it, it'll never be null, but if it's custom code that populates it via some other means, it might get actual nulls in it.
Since you are using a DataTable to return the value, it has a convenient IsNull method that you can use, which cleans up the code a little bit:
If Not row.IsNull("Phone") Then
customerContact._phone1 = row.Item("Phone")?.ToString()
End If
Obviously, if you're doing this a lot, it would be good to wrap it up into a reusable function. Also, if you want to shorten it up into one line, you could do so like this:
._phone1 = If(row.IsNull("Phone"), row.Item("Phone")?.ToString(), Nothing)
String concatenation can be used to convert Nothing to "" (other alternatives in my answer here)
._fax = row!Fax & "",
Related
I have made 2 variables:
Public a as Integer
Public b as Integer
and I have a text field where 2 values can be inserted like this: "1 - 10"
so I split the value and saved them in those 2 variables a and b.
but I need those 2 values in different forms but all I'm getting is 0.
I also created Dim c as form1 = new form1 in the form2.
What's the problem?
Depending on a relation between Form1 and Form2, you can use this kind of communication (this is for a parent-child relation):
Form 2:
Public ParentFrm as Form1
Public a as Int16
Form 1 - in it's running instance:
Public b as Int16
Dim NewInstanceOfForm2 as New Form2
NewInstanceOfForm2.ParentFrm = Me ' this is to ensure you can talk back to correct instance of parent form
NewInstanceOfForm2.a = 12345
NewInstanceOfForm2.BackColor = colors.Pink
NewInstanceOfForm2.TextBox1.Text = "Hello World!!!"
Since we set the ParentFrm in the Form2, you can communicate the same way back and set things in Form1:
ParentFrm.b = 789
ParentFrm.TextBox3 = "Hi there!!!"
ParentFrm.UpdateForm1FromDatabasePublicFunction()
Siblings can communicate through a common parent. But in all the cases, you need to get to understand instances of the forms. Remember, that you can communicate only with objects (TextBox, Button, DataGridView,...) and Public variables of the form.
You could create a module and define the variable as puclic there, that way they'll always be accessible from anywhere on the application.
Right click the solution, Add New, Module
And inside that module add
Public a, b as Integer
To split the textbox input and store them in these vars use
Dim s as String
Dim sp() as String
s = TextBox1.Text
sp = s.Split("-")
This stores the values in the textbox separated by '-' on sp(0) and sp(1) respectively.
You can then stote them into you public variables.
a = sp(0)
b = sp(1)
And call then from any form you need.
Well, i am not sure what it exactly is you want but here are some suggestions:
if your form1 contains two variables like so:
Public a As Integer = 0
Public b As Integer = 0
and you want to retrieve the value of one of those variables from another form, lets say from Form2, then all you need to do is that:
(Put this code in either a function, or an event of your second Form (for example Form2)):
dim current_a As Integer = 0
current_a = Form1.a
dim current_b As Integer = 0
current_b = Form1.b
If you enter the text (for example: "1 - 10“), into a textbox, but want to get both values (in this case 1, and 10), you only need to grab them by for example creating a loop, to search for these values, like so:
dim textbox_str As String = ""
textbox_str = TextBox1.Text
( in this example, the TextBox1.Text would contain "1 - 10")
dim final_str1 As String = ""
dim final_str2 As String = ""
for each s_ As String In textbox_str
If Not s_ = " " Then
final_str1 = final_str1 & s_
Else
Exit For
End If
Next
(after this, final_str1 would contain "1")
(now extract the second number, by doing the following:)
final_str2 = textbox_str.Replace(final_str1 & " - ", "")
(and now, final_str2 would contain "10")
(so you would have both numbers extracted from that text)
As i said, i do not exactly know what you want, but hopefully this helped you, let me know if it is what you were looking for!
Consider a text file stored in an online location that looks like this:
;aiu;
[MyEditor45]
Name = MyEditor 4.5
URL = http://www.myeditor.com/download/myeditor.msi
Size = 3023788
Description = This is the latest version of MyEditor
Feature = Support for other file types
Feature1 = Support for different encodings
BugFix = Fix bug with file open
BugFix1 = Fix crash when opening large files
BugFix2 = Fix bug with search in file feature
FilePath = %ProgramFiles%\MyEditor\MyEditor.exe
Version = 4.5
Which details information about a possible update to an application which a user could download. I want to load this into a stream reader, parse it and then build up a list of Features, BugFixes etc to display to the end user in a wpf list box.
I have the following piece of code that essentially gets my text file (first extracting its location from a local ini file and loads it into a streamReader. This at least works although I know that there is no error checking at present, I just want to establish the most efficient way to parse this first. One of these files is unlikely to ever exceed more than about 250 - 400 lines of text.
Dim UpdateUrl As String = GetUrl()
Dim client As New WebClient()
Using myStreamReader As New StreamReader(client.OpenRead($"{UpdateUrl}"))
While Not myStreamReader.EndOfStream
Dim line As String = myStreamReader.ReadLine
If line.Contains("=") Then
Dim p As String() = line.Split(New Char() {"="c})
If p(0).Contains("BugFix") Then
MessageBox.Show($" {p(1)}")
End If
End If
End While
End Using
Specifically I'm looking To collate the information about Features, BugFixes and Enhancements. Whilst I could construct what would in effect be a rather messy if statement I feel sure that there must be a more efficient way to do this , possibly involving linq. I'd welcome any suggestions.
I have added the wpf tag on the off chance that someone reading this with more experience of displaying information in wpf listboxes than I have might just spot a way to effectively define the info I'm after in such a way that it could then be easily displayed in a wpf list box in three sections (Features, Enhancements and BugFixes).
Dom, Here is an answer in C#. I will try to convert it to VB.Net momentarily. First, since the file is small, read all of it into a list of strings. Then select the strings that contain an "=" and parse them into data items that can be used. This code will return a set of data items that you can then display as you like. If you have LinqPad, you can test thecode below, or I have the code here: dotnetfiddle
Here is the VB.Net version: VB.Net dotnetfiddle
Imports System
Imports System.Collections.Generic
Imports System.Linq
Public Class Program
Public Sub Main()
Dim fileContent As List(Of String) = GetFileContent()
Dim dataItems = fileContent.Where(Function(c) c.Contains("=")).[Select](Function(c) GetDataItem(c))
dataItems.Dump()
End Sub
Public Function GetFileContent() As List(Of String)
Dim contentList As New List(Of String)()
contentList.Add("sb.app; aiu;")
contentList.Add("")
contentList.Add("[MyEditor45]")
contentList.Add("Name = MyEditor 4.5")
contentList.Add("URL = http://www.myeditor.com/download/myeditor.msi")
contentList.Add("Size = 3023788")
contentList.Add("Description = This is the latest version of MyEditor")
contentList.Add("Feature = Support for other file types")
contentList.Add("Feature1 = Support for different encodings")
contentList.Add("BugFix = Fix bug with file open")
contentList.Add("BugFix1 = Fix crash when opening large files")
contentList.Add("BugFix2 = Fix bug with search in file feature")
contentList.Add("FilePath = % ProgramFiles %\MyEditor\MyEditor.exe")
contentList.Add("Version = 4.5")
Return contentList
End Function
Public Function GetDataItem(value As String) As DataItem
Dim parts = value.Split("=", 2, StringSplitOptions.None)
Dim dataItem = New DataItem()
dataItem.DataType = parts(0).Trim()
dataItem.Data = parts(1).Trim()
Return dataItem
End Function
End Class
Public Class DataItem
Public DataType As String
Public Data As String
End Class
Or, in C#:
void Main()
{
List<string> fileContent = GetFileContent();
var dataItems = fileContent.Where(c => c.Contains("="))
.Select(c => GetDataItem(c));
dataItems.Dump();
}
public List<string> GetFileContent()
{
List<string> contentList = new List<string>();
contentList.Add("sb.app; aiu;");
contentList.Add("");
contentList.Add("[MyEditor45]");
contentList.Add("Name = MyEditor 4.5");
contentList.Add("URL = http://www.myeditor.com/download/myeditor.msi");
contentList.Add("Size = 3023788");
contentList.Add("Description = This is the latest version of MyEditor");
contentList.Add("Feature = Support for other file types");
contentList.Add("Feature1 = Support for different encodings");
contentList.Add("BugFix = Fix bug with file open");
contentList.Add("BugFix1 = Fix crash when opening large files");
contentList.Add("BugFix2 = Fix bug with search in file feature");
contentList.Add("FilePath = % ProgramFiles %\\MyEditor\\MyEditor.exe");
contentList.Add("Version = 4.5");
return contentList;
}
public DataItem GetDataItem(string value)
{
var parts = value.Split('=');
var dataItem = new DataItem()
{
DataType = parts[0],
Data = parts[1]
};
return dataItem;
}
public class DataItem
{
public string DataType;
public string Data;
}
The given answer only focuses on the first part, converting the data to a structure that can be shaped for display. But I think you main question is how to do the actual shaping.
I used a somewhat different way to collect the file data, using Microsoft.VisualBasic.FileIO.TextFieldParser because I think that makes coding just al little bit easier:
Iterator Function GetTwoItemLines(fileName As String, delimiter As String) _
As IEnumerable(Of Tuple(Of String, String))
Using tfp = New TextFieldParser(fileName)
tfp.TextFieldType = FieldType.Delimited
tfp.Delimiters = {delimiter}
tfp.HasFieldsEnclosedInQuotes = False
tfp.TrimWhiteSpace = False
While Not tfp.EndOfData
Dim arr = tfp.ReadFields()
If arr.Length >= 2 Then
Yield Tuple.Create(arr(0).Trim(), String.Join(delimiter, arr.Skip(1)).Trim())
End If
End While
End Using
End Function
Effectively the same thing happens as in your code, but taking into account Andrew's keen caution about data loss: a line is split by = characters, but the second field of a line consists of all parts after the first part with the delimiter re-inserted: String.Join(delimiter, arr.Skip(1)).Trim().
You can use this function as follows:
Dim fileContent = GetTwoItemLines(file, "=")
For display, I think the best approach (most efficient in terms of lines of code) is to group the lines by their first items, removing the numeric part at the end:
Dim grouping = fileContent.GroupBy(Function(c) c.Item1.TrimEnd("0123456789".ToCharArray())) _
.Where(Function(k) k.Key = "Feature" OrElse k.Key = "BugFix" OrElse k.Key = "Enhancement")
Here's a Linqpad dump (in which I took the liberty to change one item a bit to demonstrate the correct dealing with multiple = characters:
You could do it with Regular Expressions:
Imports System.Text.RegularExpressions
Private Function InfoReader(ByVal sourceText As String) As List(Of Dictionary(Of String, String()))
'1) make array of fragments for each product info
Dim products = Regex.Split(sourceText, "(?=\[\s*\w+\s*])")
'2) declare variables needed ahead
Dim productProperties As Dictionary(Of String, String)
Dim propertyNames As String()
Dim productGroupedProperties As Dictionary(Of String, String())
Dim result As New List(Of Dictionary(Of String, String()))
'2) iterate along fragments
For Each product In products
'3) work only in significant fragments ([Product]...)
If Regex.IsMatch(product, "\A\[\s*\w+\s*]") Then
'4) make array of property lines and extract dictionary of property/description
productProperties = Regex.Split(product, "(?=^\w+\s*=)", RegexOptions.Multiline).Where(
Function(s) s.Contains("="c)
).ToDictionary(
Function(s) Regex.Match(s, "^\w+(?=\s*=)").Value,
Function(s) Regex.Match(s, "(?<==\s+).*(?=\s+)").Value)
'5) extract distinct property names, ignoring numbered repetitions
propertyNames = productProperties.Keys.Select(Function(s) s.TrimEnd("0123456789".ToCharArray)).Distinct.ToArray
'6) make dictionary of distinctProperty/Array(Of String){description, description1, ...}
productGroupedProperties = propertyNames.ToDictionary(
Function(s) s,
Function(s) productProperties.Where(
Function(kvp) kvp.Key.StartsWith(s)
).Select(
Function(kvp) kvp.Value).ToArray)
'7) enlist dictionary to result
result.Add(productGroupedProperties)
End If
Next
Return result
End Function
Basic arrays question:
I have declared a public array and initialized it with values. In another private sub, I want to fill that public array with values. What is the easiest way to do it?
The double variables in the array are also declared public. The following code is the array created and initialized in a public module. In the private sub, I am changing the values of the double variables (ch0LowLmt, ch1LowLmt, etc, etc) and now I just simply want to reinitialize the array with the new values. What is the simplest way to do it?
Public AlarmLowLimit() As Double = New Double(15) {Ch0LowLmt, Ch1LowLmt, Ch2LowLmt, Ch3LowLmt, Ch4LowLmt, Ch5LowLmt, Ch6LowLmt, Ch7LowLmt, Ch8LowLmt, Ch9LowLmt, _
Ch10LowLmt, Ch11LowLmt, Ch12LowLmt, Ch13LowLmt, Ch14LowLmt, Ch15LowLmt}
Because Double is a value type, you do need to change it manually in the way you are working.
Another way, is to encapsulate the double var inside a class, which can be put inside your array, so it will be a reference type.
In this case you'd just update your new reference type objects in the private function you have mentioned. They will also change in your array.
See a nice solution here.
And the code example in Vb.Net:
Public Class MyObjectWithADouble
Public Property Element() As Double
Get
Return m_Element
End Get
Set
m_Element = Value
End Set
End Property
Private m_Element As Double
' property is optional, but preferred.
End Class
Dim obj = New MyObjectWithADouble()
obj.Element = 5.0
I am trying to assign a number of values to a private array in a class module using public Property Get/Let procedures. But when I try to get the values out, the array is showing as empty. Why is this?
Here's the relevant code:
Private pdt_RentStepDate(23) As Date
Public Property Get dt_RentStepDate(ByRef d1 As Integer) As Date
dt_RentStepDate(d1) = pdt_RentStepDate(d1)
End Property
Public Property Let dt_RentStepDate(ByRef d1 As Integer, something As Date)
pdt_RentStepDate(d1) = something
End Property
In your Property Get procedure, don't specify an index when you're assigning the return value:
Public Property Get dt_RentStepDate(ByRef d1 As Integer) As Date
' dt_RentStepDate(d1) = pdt_RentStepDate(d1) ' <-- WRONG
dt_RentStepDate = pdt_RentStepDate(d1) ' <-- RIGHT
End Property
If you specify an index, then instead of assigning the return value for the Property Get, you're actually calling the Property Let with the arguments d1 and pdt_RentStepDate(d1). (This has no effect because the Property Let ends up setting pdt_RentStepDate(d1) to itself.) Since no return value is assigned, the Property Get always returns an empty Date.
I understand that List, Array, Object etc. types "copied" by reference. However my natural and ordinary intend is to just have a "copy" of it in this context where I intentionally use ReadOnly instead of Read/Write property. In below sample the ReadOnly 'Extensions' property get change through 'm_extensions' reference change. Regardless, I think this behavior is incorrect and I have to do extra work to prevent ReadOnly properties from being overwritten. Is there any built in keyword to use for 'm_extensions' value protection?
Public Classs A
' more properties and methods here...
Private m_extensions() As String = {"*.abc", "*.def"}
Public ReadOnly Property Extensions() As String()
Get
Return m_extensions
End Get
End Property
End Class
Public Classs B
' more stuff here...
Private Function BuildFilter() As String
Dim l() As String = A.Extensions
Dim s As String = String.Empty
For m As Integer = 0 To l.Length - 1
Select Case l(m).ToLower
Case "*.*" : s = "All Files"
Case "*.abc" : s = "ABC File"
Case "*.def" : s = "DEF File"
Case Else : s = "XYZ File " + m.ToString
End Select
l(m) = String.Format("{1} ({0})|{0}", l(m), s)
Next
Return String.Join("|", l)
End Function
End Class
Readonly modifier means that anything using the property cannot change the reference that you protected this way (e.g. cannot set it to Nothing). It doesn't prevent changing the values in the array returned from that property.
One way around it could be to copy the array inside the property. This will prevent modifications of the original array:
Public ReadOnly Property Extensions() As String()
Get
Return m_extensions.Clone()
End Get
End Property