Anyone know of a smooth way to get all of the selected items in a listbox control by using extension methods?
And, please, spare me the argument of it's irrelevant as to how one gets such a list because in the end everything uses a loop to iterate over the items and find the selected ones anyway.
var selected = yourListBox.Items.GetSelectedItems();
//var selected = yourDropDownList.Items.GetSelectedItems();
//var selected = yourCheckBoxList.Items.GetSelectedItems();
//var selected = yourRadioButtonList.Items.GetSelectedItems();
public static class Extensions
{
public static IEnumerable<ListItem> GetSelectedItems(
this ListItemCollection items)
{
return items.OfType<ListItem>().Where(item => item.Selected);
}
}
Extension method:
public static List<ListItem> GetSelectedItems(this ListBox lst)
{
return lst.Items.OfType<ListItem>().Where(i => i.Selected).ToList();
}
You can call it on your listbox like:
List<ListItem> selectedItems = myListBox.GetSelectedItems();
You could also do the conversion using a 'Cast' on the list box items like:
return lst.Items.Cast<ListItem>().Where(i => i.Selected).ToList();
Not sure which will perform better OfType or Cast (my hunch is Cast).
Edit based on Ruben's feedback for a generic ListControl method which would indeed make it much more useful extension method:
public static List<ListItem> GetSelectedItems(this ListControl lst)
{
return lst.Items.OfType<ListItem>().Where(i => i.Selected).ToList();
}
Hello i've created one solution for this problem in this post using VB.NET:
Getting all selected values from an ASP ListBox
This code below is the same as the link above:
Public Shared Function getSelectedValuesFromListBox(ByVal objListBox As ListBox) As String
Dim listOfIndices As List(Of Integer) = objListBox.GetSelectedIndices().ToList()
Dim values As String = String.Empty
For Each indice As Integer In listOfIndices
values &= "," & objListBox.Items(indice).Value
Next indice
If Not String.IsNullOrEmpty(values) Then
values = values.Substring(1)
End If
Return values
End Function
I hope it helps.
Related
I am new to Lucene. I am trying to create an index of records. So far, I have just been adding one-to-one data to my index, and this seems fine. But, I have scenarios where I need to add one to many relationship data, and I am not sure what is the best way to handle this. I have tried adding each of the individual relationship, concacting feilds into CSV values, adding the field multiple times, but nothing seems to work. Here is my code for when the data is indexed:
Private Shared Sub _addToLuceneIndex(ByVal sampleData As LuceneSearchData, ByVal writer As IndexWriter)
Dim searchQuery = New TermQuery(New Term("Id", sampleData.Id.ToString()))
writer.DeleteDocuments(searchQuery)
Dim doc = New Document()
doc.Add(New Field("Id", sampleData.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED))
doc.Add(New Field("Name", sampleData.Name, Field.Store.YES, Field.Index.ANALYZED))
doc.Add(New Field("Description", sampleData.Description, Field.Store.YES, Field.Index.ANALYZED))
For Each item As Integer In sampleData.HomeStates
doc.Add(New Field("Home_State", item, Field.Store.YES, Field.Index.ANALYZED))
Next
'i have also tried
'doc.Add(New Field("HomeStates ", String.Join(",", sampleData.HomeStates ), Field.Store.YES, Field.Index.ANALYZED))
writer.AddDocument(doc)
End Sub
While the above code seems to index the one-to-one data, it does not for the HomeStates list of integers. Do I have to add the same document for every item in the list of integers? If so, how do you best manage this? I have several "one-to-many" relationships I need to include. I can see this getting unwieldy quickly. Or, is there a better way?
EDIT
I updated to add the one to may value as a field like this:
doc.Add(New Field("Geo_Locations", String.Join(" ", sampleData.Geo_Location), Field.Store.YES, Field.Index.ANALYZED))
This is how I search for that field:
Private Shared Function _search(ByVal searchQuery As String, ByVal Optional searchField As String = "") As IEnumerable(Of LuceneSearchData)
If String.IsNullOrEmpty(searchQuery.Replace("*", "").Replace("?", "")) Then Return New List(Of LuceneSearchData)()
Using searcher = New IndexSearcher(_directory, False)
Dim hits_limit = 1000
Dim analyzer = New StandardAnalyzer(Version.LUCENE_30)
If Not String.IsNullOrEmpty(searchField) Then
Dim parser = New QueryParser(Version.LUCENE_30, searchField, analyzer)
Dim query = parseQuery(searchQuery, parser)
Dim hits = searcher.Search(query, hits_limit).ScoreDocs
Dim results = _mapLuceneToDataList(hits, searcher)
analyzer.Close()
searcher.Dispose()
Return results
End If
End Using
End Function
Private Shared Function _mapLuceneToDataList(ByVal hits As IEnumerable(Of ScoreDoc), ByVal searcher As IndexSearcher) As IEnumerable(Of LuceneSearchData)
Dim listOfResults As List(Of LuceneSearchData)
Try
listOfResults = hits.[Select](Function(hit) _mapLuceneDocumentToData(searcher.Doc(hit.Doc))).ToList()
Catch ex As Exception
Return Nothing
End Try
Return listOfResults
End Function
Private Shared Function _mapLuceneDocumentToData(ByVal doc As Document) As LuceneSearchData
Return New LuceneSearchData With {
.Id = Convert.ToInt32(doc.[Get]("Id")),
.Mechanism_Name = doc.[Get]("Name"),
.Mechanism_Purpose = doc.[Get]("Description"),
.Geo_Location = doc.[Get]("Home_State")
}
End Function
I then call the search bu:
LuceneData = LuceneSearch.Search("5451", "HomeStates")
Creating a new document for each item will result in duplicates when searching for other fields.
You should design your documents & fields according to your needs.
If you don't need to search for these fields, store them as you wish.
If you need to search these multiple valued fields then create a field that can be searched for each item.
For integer lists you can merge them with spaces.
For string lists you should replace white spaces with some custom character while indexing and searching to avoid matching substrings (eg "stack overflow" => stack_overflow)
One to many using a term
As you have figured out, integers can be concatenated using a variety of separators including spaces, resulting in something like:
"234 12342 345 5476456 234"
The StandardAnalyzer will tokenise the above string into separate tokens just as it would any other string of words. So you can search them and it will give you the expected results.
One to many using a phrase
If you need to tokenise phrases, such as:
"Control Support Engineer|Technical Support Engineer|Maintenance Technician"
into individual phrases, not down to words (terms), then you'll need to inherit from a few classes. A tokenizer that inherits from CharTokenizer and an analyzer that inherits from Analyzer.
Tokenizer
public sealed class PipeTokenizer: CharTokenizer
{
public PipeTokenizer(LuceneVersion matchVersion, TextReader input) :
base(matchVersion, input)
{
}
public PipeTokenizer(LuceneVersion matchVersion, AttributeFactory factory, TextReader input) :
base(matchVersion, factory, input)
{
}
protected override bool IsTokenChar(int c)
{
return !((char)c).Equals('|'); //<-- the only line that matters
}
}
Pretty simple stuff so far.
Analyzer
public class PipeAnalyzer: Analyzer
{
protected override TokenStreamComponents CreateComponents(string fieldName, TextReader reader)
{
var tokenizer = new PipeTokenizer(LuceneVersion.LUCENE_48, reader);
var lowerCaseFilter = new LowerCaseFilter(LuceneVersion.LUCENE_48, tokenizer);
return new TokenStreamComponents(tokenizer, lowerCaseFilter);
}
}
This is based on what the KeywordAnalyzer does but with a twist. The KeywordAnalyzer is very simple. It takes the entire string, or phrase, and indexes it as is. No further breaking down into smaller tokens. The analyzer above uses the PipeTokenizer to break phrases down by the | separator, and converts to lower case.
Querying with any of the following phrases will return the doc:
"control support engineer"
"technical support engineer"
"maintenance technician"
I am new to ASP.NET and can't seem to find an answer for my problem.
I want to retrieve a list of countries form a table in my database.
I have found a way to do it in C# but i need to do it in Viusal Basic.
private List<Country> PopulateCountry()
{
using (MyDatabaseEntities dc = new MyDatabaseEntities())
{
return dc.Countries.OrderBy(a => a.CountryName).ToList();
}
}
This is the code sample from C# which I tried to convert to VB.net, but I keep getting this error message and can't figure out why.
Private Function GetListOfCountries() As List(Of Country)
Using dc As New MyDatabaseEntities
Try
Dim countryList = (From p In dc.Counties Order By p.CountryName
Ascending
Select p)
Return countryList.ToList()
Catch ex As Exception
Return Nothing
End Try
End Using
End Function
On the Return countryList.ToList() line i get this error
Value of type 'List(Of Country)' cannot be converted to 'List(Of Country)'.
Any help would be much appreciated. Thanks!
I believe this function will suit your needs. (your conversion failed due to County instead of Country, where County happens to be a valid entity type as well.)
Private Function GetListOfCountries() As List(Of Country)
Using dc As New MyDatabaseEntities
Try
Return dc.Countries.OrderBy(Function(a) a.CountryName).ToList()
Catch ex As Exception
Return Nothing
End Try
End Using
End Function
Private Function GetListOfCountries() As List(Of Country)
Using dc As New MyDatabaseEntities
Try
Dim countryList = (From p In dc.Countries Order By p.CountryName
Ascending
Select p)
Return countryList.ToList()
Catch ex As Exception
Return Nothing
End Try
End Using
End Function
You are selected Counties instead of Countries in your from statement.
What I'm trying to do is send a string from ASPX to the code behind of a UserControl.
The first step works, and calls a WS and brings back a string.
ASPX
function (data) {
//UpdateUserControl!
};
The issue is that I have no idea how to pass this string on to a UserControl, I tried a __doPostBack but that only refreshes the whole page. I just want to update a listbox with the items. With the intention of later retrieving a string from the User Control. I am using a UpdatePanel in my UserControl.
ASCX.VB
'Get shopping list
Public Function UpdateList(list As String) As ListBox
Dim result As String = ""
Dim strItems As String = list
Dim strArray As String() = strItems.Split(","c)
For Each item As String In strArray
myListBox.Items.Add(item)
Next
Return email_add
End Function
Add a property to the code behind of the usercontrol. You can then set the value from the containing page.
Use this code,
In aspx
public string yourvalue { get; set; }
then ascx.cs
_Default d = new _Default();
string s = d.yourvalue;
sure its working.
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
Im making a site in Visual Studio using vb and I have a variable in page_load but need its value in the event handler of a button for passing on session.
Any suggestions on how I can do this? Any help is appreciated
You can store a value in the CommandArgument property of a Button:
btn.CommandArgument = "Your value"
And then when handling the event you can pull it out:
CType(sender, Button).CommandArgument
You could also make a new class that extends Button and create new properties like below if you need multiple arguments:
Class SessionButton
Inherits Button
Public Property SessionGUID() As Guid
Get
Dim s As Guid = Nothing
If Not String.IsNullOrEmpty(ViewState("SessionGUID")) Then
s = New Guid(ViewState("SessionGUID").ToString())
End If
Return s
End Get
Set(ByVal value As Guid)
ViewState("SessionGUID") = value.ToString()
End Set
End Property
End Class
couldn't you just make the variable a class scoped variable, instead of local?
You can store it in a viewstate backed property:
Public Property MyStringVar() As String
Get
If ViewState("MyStringVar") = Nothing Then
Return String.Empty
End If
Return ViewState("MyStringVar").ToString()
End Get
Set
ViewState("MyStringVar") = value
End Set
End Property
Now using this property you can save your variable on page load and access it in the button click event handler.
EDIT: updated to VB
you declare the variable outside the page_load and then you can use it where you want :)
You could also create a class that encapsulates the action. Then you can capture any data you want and easily add to it later.
void Page_Load()
{
myBtn.Click += new MyButtonClass(variable).Run;
}
class MyButtonClass
{
int Param;
MyButtonClass(int param)
{
Param = param;
}
public void Run(object sender, EventArgs args)
{
// Do something usefull
}
}
If the data is created/retrieved on every Page_Load, this will work. If it is wrapped around an (! IsPostBack), the data must be stored in a session. Fortunatly, the class can be easily modified to store/load the variable from a session parameter.
I'm sorry for the c# code, maybe someone else can translate it then remove this message?