To preface this, I've looked through several of the postings containing listviews and nothing really is comparing to what I'm trying to do.
I'm trying to take values determined through a loop that has several If Statements similar to what follows:
If Convert.ToInt32(GetData(ds.Tables("HRIS").Rows(i), "ABS_CO_RULE_DAYS", DefaultValue)) > 0 Or _
Convert.ToInt32(GetData(ds.Tables("HRIS").Rows(i), "ABS_CO_RULE_HRS", DefaultValue)) > 0 Or _
Convert.ToInt32(GetData(ds.Tables("HRIS").Rows(i), "ABS_CO_RULE_MINS", DefaultValue)) > 0 Then
msChargeDays = GetData(ds.Tables("HRIS").Rows(i), "ABS_CO_RULE_DAYS", DefaultValue)
msChargeHours = GetData(ds.Tables("HRIS").Rows(i), "ABS_CO_RULE_HRS", DefaultValue)
msChargeMins = GetData(ds.Tables("HRIS").Rows(i), "ABS_CO_RULE_MINS", DefaultValue)
msHowPaid = "CR DAYS"
AbsenceLine() 'calls sub
End If
This block of code returns valid results from the dataset that it calls from. Now in the following code block, I am trying to assimilate values that are determined by the main code block which is about 40 if statements similar in structure to the block above, all contained within a For loop.
In the following code block I am trying to insert an object of ListViewItem type into the ListView. This is where I'm getting my error denoted by comment string following it. The function that fills the ListViewItem is at the bottom, all of the variables returned are all class variables and return valid values to the ListViewItem. I have double checked this via the debugger.
Private Sub AbsenceLine()
Dim sTypeReason As String
If msType = "" Then
sTypeReason = Left(msReason, 25)
Else
sTypeReason = msType & "--" & msReason
End If
Dim item As ListViewItem
item = New ListViewItem(ListViewItemType.DataItem)
item.DataItem = FillListView(sTypeReason)
absence_lv.Items.Add(item) 'this line here is what is givine me issues
End Sub
Private Function FillListView(typeReason As String) As IEnumerable(Of String)
Return {msDate, msVoid, msCont, msDays, msHours, msMinutes, typeReason, msChargeDays, msChargeHours,
msChargeMins, msHowPaid} 'all values returned are of String type
End Function
Now with the background:
Am I completely off base with what I'm trying to do?
Is there an easier way to do this with a gridview instead?
I think you're misusing the DataItem property of the ListViewItem class. That's really used to get the underlying object that the ListViewItem represents when the ListView itself is bound.
Since your FillListView function is returning an IEnumerable(Of String), you can just set the ListView's DataSource property to the function call.
absence_lv.Datasource = FillListView(sTypeReason)
absence_lv.Databind()
As you can see, there is no need to manually add each item.
Related
I'm using ASP.NET and Visual Basic to make a custom form, used for filling in information. Once I click a button, a function gets called that takes the bits information from that form, puts them into an object, and adds that object to a list. This is being used for a sort of queued entry system, so the form will be edited and submitted multiple times.
For some reason, instead of adding the new object in the next index position of the list, it instead replaces whatever was at 0. So, there is only ever one object in the list at the time.
Here's the custom form:
Here's my custom object, which is currently placed above my _Default class:
Public Class QueueItem
Public Property _TestName As String
Public Property _ValueID As String
Public Property _MathOperator As String
Public Property _ValueInput As Integer
Public Sub New()
End Sub
Public Sub New(ByVal TestName As String, ByVal ValueID As String, ByVal MathOperator As String, ByVal ValueInput As Integer)
_TestName = TestName
_ValueID = ValueID
_MathOperator = MathOperator
_ValueInput = ValueInput
End Sub
End Class
The list is declared above my Page_Load function, inside the _Default class, and is public. Here's that list definition:
Public QueueList As List(Of QueueItem) = New List(Of QueueItem)()
And, here's what gets called when that "Add To Queue" button is pressed:
Protected Sub AddToQueueButton_Click(sender As Object, e As EventArgs) Handles AddToQueueButton.Click
'Adds a new QueueItem to the QueueList
'Values pulled from the dropdown lists in the custom form
QueueList.Add(New QueueItem() With {
._TestName = TestName.SelectedValue,
._ValueID = ValueID.SelectedValue,
._MathOperator = MathOperator.SelectedValue,
._ValueInput = ValueInput.Text
})
'Below section is for testing
Dim test1 As String = QueueList(0)._TestName
Dim test2 As String = QueueList(0)._ValueID
Dim test3 As String = QueueList(0)._MathOperator
Dim test4 As String = QueueList(0)._ValueInput
Dim testmessage As String = test1 + " | " + test2 + " | " + test3 + " | " + test4
Dim count = QueueList.Count
Dim capacity = QueueList.Capacity
Response.Write("<script language='javascript'>alert('" + testmessage + "');</script>")
End Sub
So, as you can see, I have some test variables and stuff that I'm using to make sure this is working. Any time this gets called, an object gets added to the list, I look at the count and capacity for the list, and I display all the information in an alert.
This information for the alert is always reading from index 0. So, it shouldn't matter how many times I add information to the list, 0 should stay the same, and objects should be added at 1, then 2, and so on. Right?
Well, 0 changes any time I submit new information, and neither the count or capacity never increase past the first entry. They always display as if there's only one item in the list.
Here's me running the queue entry form twice, with two different numbers on the end:
First run:
Second run:
Since I'm always reading from index 0, that number at the end shouldn't change. It should be giving me the number that's associated with the object at index 0. And, the List.Add function should make the count and capacity go up. But, none of that happens. Instead, it seems to be replacing what was at 0.
If anyone has any tips on how to fix this or can clue me in on what might be going on, that would be greatly appreciated.
Every time a request is made to your page, a new instance of your class is created to handle the request. For a post-back request, some data may be loaded - for example, the values of the fields posted to the server, and anything stored in ViewState. But data stored in fields within your page will not be persisted across requests.
The code is not replacing the item at index 0; it is adding an item to an empty list.
You need to persist your list between requests somehow. You can either store it in the Session, or put it in the ViewState.
I cannot find a complete example. Found tons on grid and combobox, but not textbox. This test is to lookup a “PhoneTypeName” from a UserPhoneType table with TypeCode = “0” and assign that first value to a asp.net textbox.
Currently, I am getting “Object reference not set to an instance of an object” when setting the text box to "phonetype.FirstOrDefault.PhoneTypeName.ToString"
Using dbContext As New EntitiesModel()
Dim phonetype As IEnumerable(Of User_PhoneType) = dbContext.User_PhoneTypes.Where(Function(c) c.PhoneTypeCode = "O")
mytextbox.Text = phonetype.FirstOrDefault.PhoneTypeName.ToString
End Using
----EDIT----
I changed as suggested. I ALSO successfully bound the entire list of PhoneTypes to a droplist control...to confirm the data is accessible. It must be the way I am going about querying the table for a single record here.
I get the same error, but at "Dim type = phonetype.First..."
The record is in the table, but it does not appear to be extracted with my code.
Dim phonetype As IEnumerable(Of User_PhoneType) = dbContext1.User_PhoneTypes.Where(Function(c) c.PhoneTypeCode = "M")
Dim type = phonetype.FirstOrDefault
If Object.ReferenceEquals(type, Nothing) = False And Object.ReferenceEquals(type.PhoneTypeName, Nothing) = False Then
mytextbox.Text = type.PhoneTypeName.ToString
End If
In general there are the following two possible reasons for getting this exception:
1) The phonetype list is empty and the FirstOrDefault method is returning a Nothing value.
2) The PhoneTypeName property of the first element of the phonetype list has a Nothing value.
In order to make sure that you will not get the Object reference not set to an instance of an object exception I suggest you add a check for Nothing before setting the TextBox value. It could be similar to the one below:
Dim type = phonetype.FirstOrDefault
If Object.ReferenceEquals(type, Nothing) = False And Object.ReferenceEquals(type.PhoneTypeName, Nothing) = False Then
mytextbox.Text = type.PhoneTypeName.ToString
End If
Fixed it.
I was able to view the SQL string being generated by using this:
mytextbox.text = phonetype.tostring
I saw that the SQL contained "NULL= 'O'"
I did it like the example?!? However, when I added .ToString to the field being queried, it worked.
So the final looks like this:
Using dbContext As New EntitiesModel()
Dim phonetype As IEnumerable(Of User_PhoneType) = dbContext.User_PhoneTypes.Where(Function(c) c.PhoneTypeCode.**ToString** = "O")
mytextbox.Text = phonetype.FirstOrDefault.PhoneTypeName.ToString
End Using
BTW, Dimitar point to check for null first is good advice (+1). The value was nothing as he said.
i can add a value to array(0), but when i then add a value to array(1) it clears the value for array(0). I've tried every way I can think of to declare and create the array. My code looks like this:
Dim aryEstimateInfo() As String = New String(7) {}
Private Sub wzrdEstimateWizard_NextButtonClick(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.WizardNavigationEventArgs) Handles wzrdEstimateWizard.NextButtonClick
Select Case wzrdEstimateWizard.ActiveStepIndex
Case 0 'first estimate wizard step
aryEstimateInfo(0) = rad_lstVehicleType.SelectedItem.ToString
Case 1 'second estimate wizard step
Dim DamageZoneSelected As Boolean = False
For Each cntrl As Control In pnlDamageZone.Controls
If TypeOf cntrl Is RadioButton Then
Dim RadButton As RadioButton = cntrl
If RadButton.Checked Then
DamageZoneSelected = True
DamageZone = RadButton.Text.ToString
Exit For
Else
DamageZoneSelected = False
End If
End If
Next
If DamageZoneSelected = True Then
lblDamageZoneError.Visible = False
aryEstimateInfo(1) = DamageZone
Else
'if no damage zone is selected a message is displayed
wzrdEstimateWizard.ActiveStepIndex = 2
wzrdEstimateWizard.ActiveStepIndex = 1
lblDamageZoneError.Visible = True
End If
Case 2 'third estimate wizard step
'assigns the number of dents to the estimate array
aryEstimateInfo(2) = ddlNumberOfDents.SelectedValue.ToString
'sets the average dent size in the estimate arrau
aryEstimateInfo(3) = ddlDentSize.SelectedValue.ToString
'sets the add-on code and number of oversized dents
If ddlOverSized.Enabled = True Then
'aryEstimateInfo.SetValue("3", 4)
aryEstimateInfo(4) = "3"
aryEstimateInfo(7) = ddlOverSized.SelectedValue.ToString
Else
End If
Case 3 'fourth estimate wizard step
Case Else
End Select
End Sub
I'm using this in an ASP.Net wizard control and in basic, visual studio 2010.
The problem is that each button click is posting back the page, which causes your aryEstimateInfo to be re-created on each postback.
In order to handle this situation elegantly, improve the maintenance of the page, and make it easier to debug this sort of situation in the future, I recommend the following changes:
1) Change the array to a class with properties:
Public Class EstimateInfo
Public VehicleType As String = ""
Public DamageZone As String = ""
Public NumberOfDents As String = ""
Public DentSize As String = ""
Public AddOnCode As String = ""
Public Oversized As String = ""
End Class
Note that all of the properties are declared as string, but the data types should probably be changed to more accurately reflect the underlying content.
This approach will help debugging because you can change the auto-implemented property to a getter/setter so that you can place a breakpoint to see where the value is getting cleared:
Private m_sVehicleType As String = ""
Public Property VehicleType As String
Get
Return m_sVehicleType
End Get
Set (Value As String
' You could set a breakpoint here to discover where the value is getting cleared.
m_sVehicleType = Value
End Set
End Property
And if you need to have the values in a string array for export to a different application or database, for example, you could add a method to the class to produce an appropriate string array.
2) Add a property to the page to store the current answer class in the page's ViewState so that you won't have to continuously re-populate the array. For example:
Private Property EstimateInfo As EstimateInfo
Get
' Add a new record to the viewstate if it doesn't already exist
If ViewState("EstimateInfo") Is Nothing Then
Me.EstimateInfo = New EstimateInfo
End If
Return ViewState("EstimateInfo")
End Get
Set (value As EstimateInfo)
ViewState("EstimateInfo") = value
End Set
End Property
Once you do this, your wizard code becomes much easier to understand and maintain:
Select Case wzrdEstimateWizard.ActiveStepIndex
Case 0 'first estimate wizard step
Me.EstimateInfo.VehicleType = rad_lstVehicleType.SelectedItem.ToString
when you declare the new array someehere in the code you cannot reuse it again after post back.
I suggest to build the array on finish wizard event
you can use the controls in whole steps where ever step you in
I guess it will be fine
otherwise you need to store the array after every update in session or view state but I don't like both
sorry I couldn't view example becoz I'm using mobile
Update: I didn't make it clear but I meant this to be a question about where/how I would use a function to return a list of strings when I'm trying to just work with classes.
I have a class called Account.
I have data access class called AccountDAO.
I have various functions that return lists of objects like GetAllAccounts, GetAccountByID etc.
I want to populate a drop down list with just the account names and nothing else. It's proving rather slow when using lists of objects and databinding them to the dropdownlist.
I feel like I should be using a simple "Select Account_Name From blah" type statement and returning a list of strings but I don't know how to work this into my class and data access class.
How should I handle this predicament?
You can use a list of string,s and bind the list of strings to a dropdownlist no problem... the DDL can support that, just leave out DataTextField and DataValueField props, and it will display the account name as is, which that name would be accessible through the ListItem's Text and Value property.
I like to use objects to be consistent with the rest of the app (which other areas might need a class), and if for some reason you want to add AccountKey later, if you use an object, all you need to do is add a property. Otherwise, if using strings, you'd have to switch up the binding later to point to the object.
HTH.
There is nothing wrong by making a function that only returns a list of strings. YOu could however wonder if it's not better to restrict the number of records you want to put in the list and use some kind of paging.
Assuming that you're using a List<>, you can try something like this:
IEnumerable<string> nameList = accountList.Select(t => t.AccountName);
Or if you need a List:
List<string> nameList = accountList.Select(t => t.AccountName).ToList();
Go with your feelings. Use a datareader to select the list and then load them into an arraylist which can then be bound to the dropdown. Alternately, use something like this method I use to provide both a DisplayMember and a ValueMember which uses a class (with both values) as members of the arraylist. This should give you the general idea. (Note: I normally include this code in a data access class (MyBase) where StartReader, _datRdr, ReadNext and_ReaderValid are a members. But the general idea is intact.)
Public Sub LoadDataSource(ByRef PlantDataSource As PlantSource, Optional ByVal Filter As String = "", Optional ByVal IncludeBlankItem As Boolean = False)
PlantDataSource = New PlantSource
If IncludeBlankItem Then
PlantDataSource.Add(0, "")
End If
If Filter = String.Empty Then
Call StartReader(" Order by PlantName")
Else
Call StartReader(String.Concat(" Where ", Filter, " Order by PlantName"))
End If
If _DatRdr.HasRows Then
While MyBase._ReaderValid
PlantDataSource.Add(PlantId, PlantName)
ReadNext()
End While
End If
Call CloseReader()
End Sub
Private Class PlantListing
Private _PlantList As New ArrayList
Public Sub Add(ByVal PlantId As Integer, ByVal PlantName As String)
_PlantList.Add(New PlantDataItem(PlantId, PlantName))
End Sub
Public ReadOnly Property List() As ArrayList
Get
Return _PlantList
End Get
End Property
End Class
Private Class PlantDataItem
Private _PlantId As Integer
Private _PlantName As String
Public Sub New(ByVal pPlantId As Integer, ByVal pPlantName As String)
Me._PlantId = pPlantId
Me._PlantName = pPlantName
End Sub
Public ReadOnly Property PlantName() As String
Get
Return _PlantName
End Get
End Property
Public ReadOnly Property PlantId() As Integer
Get
Return _PlantId
End Get
End Property
Public ReadOnly Property DisplayValue() As String
Get
Return CStr(Me._PlantId).Trim & " - " & _PlantName.Trim
End Get
End Property
Public Overrides Function ToString() As String
Return CStr(Me._PlantId).Trim & " - " & _PlantName.Trim
End Function
End Class
I'm currently trying to move through all the values added to a listbox by the user, however, I want to retrieve the actual value of each item in the listbox and not the text.
I've gotten so far with the code below, but that only gets the text and not the value.
For Each item In SelectedStoresLB.Items
Dim tCompany As Integer = CInt(Left(item.ToString, 1))
Dim tStore As String = Right(item.ToString, 3)
Dim tReason As String = ReasonTxt.Text
insertSQL = "INSERT INTO [CommsDownLog] ([DimCompanyID],[PervasiveStoreNumber],[DownReason]) VALUES (" & tCompany & ", '" & tStore & "', '" & tReason & "')"
Dim insertRow = New SqlCommand(insertSQL, objConn)
Try
objConn.Open()
insertRow.ExecuteNonQuery()
objConn.Close()
Catch ex As Exception
Response.Write(ex)
End Try
Next
How would I go about getting the value for each item in the collection?
item is a ListItem object - rather than call ToString on it, you should use the Text and Value properties to get the info you need.
Using VB 2010, note to get the actual values of the items in the listbox you need to use the "Content" property of the ListBoxItem object. Eg:
For i As Integer = 0 To lstSortUs.Items.Count - 1
sAllItems &= lstSortUs.Items(i).Content & ";"
Next
sAllItems = Left(sAllItems, Len(sAllItems) - 1)
arrAllItems = sAllItems.Split(";")
System.Array.Sort(arrAllItems)
Have you tried:
item.Value
You need to be careful when iterating over a ListBox because you may end up modifying the underlying collection. By using foreach as you are, you are utilizing the underlying enumerator. I recommend you modify your iterator to the following (C# example):
foreach (ListItem li in listbox.Items.ToArray())
{
if (li.Selected)
{
Controltest2.Remove(li.Value);
}
}
By doing this, you are modify the Array's collection and not the list's collection. This assumes LINQ to object and you may need to call Cast<t> to make it work in some cases.
The reason for this is below:
The foreach statement repeats a group
of embedded statements for each
element in an array or an object
collection. The foreach statement is
used to iterate through the collection
to get the desired information, but
should not be used to change the
contents of the collection to avoid
unpredictable side effects
Source: MSDN
To get the text you want after iterating, use .Value instead of .Text. Of course, there are other ways to iterate such as going in reverse with an indexed for loop, but that's another topic :)