I've got a Repeater that lists all the web.sitemap child pages on an ASP.NET page. Its DataSource is a SiteMapNodeCollection. But, I don't want my registration form page to show up there.
Dim Children As SiteMapNodeCollection = SiteMap.CurrentNode.ChildNodes
'remove registration page from collection
For Each n As SiteMapNode In SiteMap.CurrentNode.ChildNodes
If n.Url = "/Registration.aspx" Then
Children.Remove(n)
End If
Next
RepeaterSubordinatePages.DataSource = Children
The SiteMapNodeCollection.Remove() method throws a
NotSupportedException: "Collection is read-only".
How can I remove the node from the collection before DataBinding the Repeater?
Your shouldn't need CType
Dim children = _
From n In SiteMap.CurrentNode.ChildNodes.Cast(Of SiteMapNode)() _
Where n.Url <> "/Registration.aspx" _
Select n
Using Linq and .Net 3.5:
//this will now be an enumeration, rather than a read only collection
Dim children = SiteMap.CurrentNode.ChildNodes.Where( _
Function (x) x.Url <> "/Registration.aspx" )
RepeaterSubordinatePages.DataSource = children
Without Linq, but using .Net 2:
Function IsShown( n as SiteMapNode ) as Boolean
Return n.Url <> "/Registration.aspx"
End Function
...
//get a generic list
Dim children as List(Of SiteMapNode) = _
New List(Of SiteMapNode) ( SiteMap.CurrentNode.ChildNodes )
//use the generic list's FindAll method
RepeaterSubordinatePages.DataSource = children.FindAll( IsShown )
Avoid removing items from collections as that's always slow. Unless you're going to be looping through multiple times you're better off filtering.
I got it to work with code below:
Dim children = From n In SiteMap.CurrentNode.ChildNodes _
Where CType(n, SiteMapNode).Url <> "/Registration.aspx" _
Select n
RepeaterSubordinatePages.DataSource = children
Is there a better way where I don't have to use the CType()?
Also, this sets children to a System.Collections.Generic.IEnumerable(Of Object). Is there a good way to get back something more strongly typed like a System.Collections.Generic.IEnumerable(Of System.Web.SiteMapNode) or even better a System.Web.SiteMapNodeCollection?
Related
A simple, rather open ended question. I am wanting to add all items in a Listbox to an object, to later print that those items into a database. Is the best practice for adding all items in a Listbox to an array?
Thanks
Since you've commented that you are using ASP.NET, you can use a loop:
Dim allListBoxItemsText = New List(Of String)
For Each item As ListItem In listBox1.Items
allListBoxItemsText.Add(item.Text)
Next
or LINQ:
Dim items = From item In listBox1.Items.Cast(Of ListItem)()
Select item.Text
allListBoxItemsText = items.ToList() ' if you want an array use ToArray
Id use the standard declare an array and loop through it. I am sure there is a more elegant solution but it works.
Dim array(listbox1.items.count-1) As String
Dim i as integer = 0
For i = 0 to listbox1.items.count -1
array(i) = listbox1.items(i)
Next
I have a LINQ query with multiple values in a where clause. The values in where clause are from Filtering options from checkboxlist. Checkboxlist can return Null (or empty string in the e.g.) and that means I don't need where clause in the query as selecting none on checkboxlist means selecting all. I didn't know how to write one good LINQ that can handle that so I ended up using multiple IF statements with multiple queries as below. I tested it and works fine for now with 2 parameters in the where clause. But in reality, I need more parameters to pass in to the query and it will get messy having many IF statements to do that job. How can I handle that in one good LINQ query?
Function FilterCol(ByVal col As List(Of ProductDetails), Optional SelectedCategory As List(Of String) = Nothing, Optional SelectedBrand As List(Of String) = Nothing) As List(Of ProductDetails)
Dim strSelectedCategory As String = String.Join(",", SelectedCategory .ToArray())
Dim strSelectedBrand As String = String.Join(",", SelectedBrand .ToArray())
If strSelectedCategory = "" And strSelectedBrand = "" Then
Return col
ElseIf strSelectedCategory = "" Then
Dim res1 As IEnumerable(Of StatsDetails) = From x In col Where strSelectedBrand.Contains(x.Brand) Select x
Return res1.ToList
ElseIf strSelectedBrand = "" Then
Dim res2 As IEnumerable(Of StatsDetails) = From x In col Where strSelectedCategory.Contains(x.Category) Select x
Return res2.ToList
Else
Dim res As IEnumerable(Of StatsDetails) = From x In col Where strSelectedCategory.Contains(x.Category) And strSelectedBrand.Contains(x.Brand) Select x
Return res.ToList
End If
End Function
If you have multiple variable conditions for your LINQ query, consider using Dynamic LINQ Library. It will allows you to combine the conditions into a string variable in advance based on supplied parameters and then use that variable as condition for LINQ Query - this is something akin to Dynamic SQL in SQL Server.
This is easier to do using Extension Method syntax. I don't think doing it as one LINQ statement is a good idea.
What you can do is first create your base query using just the table. I'm going to do it in C#, but it won't be complex to translate to VB--I don't want to confuse you or myself!
var baseQuery = col;
Now that you've got your base table, you can start adding wheres based on your conditions.
if(condition1 == true)
{
baseQuery = baseQuery.Where(this is your where condition);
}
if(condition2 == true)
{
baseQuery = baseQuery.Where(this is another condition);
}
So, you can chain your Wheres together, allowing you to narrow down your query based on the conditions.
Finally, you return your query's results, with the ToList. This avoids the code duplication that you had in your conditions and everything's easier to understand and maintain.
Use lambda exp like this:
Dim FnValueExists =
Function(v As String, a As List(Of String)) a Is Nothing OrElse
a.Count = 0 OrElse
a.Contains(v)
Then in your linq, just test for the value like below:
Dim res As IEnumerable(Of StatsDetails) =
From x In col
Where FnValueExists(x.Category, SelectedCategory) And
FnValueExists(x.Brand, SelectedBrand)
Select x
The main code that determines if item x will be selected or not, is that a Is Nothing OrElse a.Count = 0 OrElse a.Contains(v) line in the FnValueExists lambda expression.
In the case above, if SelectedCategory and/or SelectedBrand list is Nothing or empty (Count=0), or contains the value, the item will still be selected.
So if users did not select any categories, you can set SelectedCategory to Nothing or just an empty list, therefore FnValueExists(x.Category, SelectedCategory) will always return true.
I have a dropdown list on my page (ddlProgram) which is populated via a database query like so:
Using dbContext as IRFEntities = New IRFEntities
Dim getPrograms = (From p in dbContext.IRF_Program _
Order By p.name _
Select p)
ddlProgram.DataSource = getPrograms
ddlProgram.DataTextField = "name"
ddlProgram.DataValueField = "id"
ddl.Program.DataBind()
End Using
So, for example, one might have a DataTextField of "Education" and an ID of "221".
Now, I prepopulate the form with information about the individual visiting the site (if available) - including the dropdown list like so:
If getProspect IsNot Nothing Then
If getProspect.user_id Is Nothing Then
ddlProgram.SelectedValue = getProspect.Program
End If
End If
The Program property contains a number that matches the ID of a Program. So, for example, this individual might have a Program of "221" which would match the "221" of Education mentioned above.
Currently the application successfully sets the SelectedValue to "221" for the DropDownList (ddlProgram), but the SelectedItem of the DDL remains the same (e.g., if it is initially "History" with an ID of "1" after the prepopulation it is "History" with an ID of "221").
What I'm trying to make happen is that the SelectedItem is updated to item which corresponds with the SelectedValue. So, in the end, if the individual has "221" for "Education" selected when the form is prepopulated they would see Education as the selected item and the selected value would be set correctly, whereas right now the form is showing the wrong SelectedItem but has the right SelectedValue behind the scenes.
Here is a more complete idea of the code flow from the Page_Load event:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Page.IsPostBack = False Then
' If prospect is coming from unique url
Dim prospect_url As String = Page.RouteData.Values("value")
' Save prospect_url into session variable
Session("prospect_url") = prospect_url
Using dbContext As IRFEntities = New IRFEntities
' Prepopulate the programs dropdown.
Dim getPrograms = (From p In dbContext.IRF_Program _
Order By p.name _
Select p)
ddlProgram.DataSource = getPrograms
ddlProgram.DataTextField = "name"
ddlProgram.DataValueField = "id"
ddlProgram.DataBind()
End Using
Using dbContext As IRFEntities = New IRFEntities
' Prepopulate the states dropdown.
Dim getStates = (From p In dbContext.IRF_States _
Order By p.name _
Select p)
ddlState.DataSource = getStates
ddlState.DataTextField = "name"
ddlState.DataValueField = "id"
ddlState.DataBind()
End Using
Using dbContext As IRFEntities = New IRFEntities
' Grab info. about prospect based on unique url.
Dim getProspect = (From p In dbContext.IRF_Prospects _
Where p.url = prospect_url _
Select p).FirstOrDefault
' If they have a record...
If getProspect IsNot Nothing Then
If getProspect.user_id Is Nothing Then
' Prepopulate the form with their information.
' These must have a value, so we need to make sure that no column is null in the database.
ddlProgram.SelectedValue = getProspect.program
txtFirst.Text = getProspect.first_name
txtLast.Text = getProspect.last_name
txtAddress.Text = getProspect.address
txtCity.Text = getProspect.city
ddlState.SelectedValue = getProspect.state
txtZip.Text = getProspect.zip
txtPhone.Text = getProspect.phone
txtEmail.Text = getProspect.email_address
txtYearEnrolling.Text = getProspect.enrolling_in
Else
' Redirect them to login.
Response.Redirect("login.aspx")
End If
End If
End Using
End If
End Sub
What you're doing looks like it should work. If you put a breakpoint after the setting of the value and check the SelectedItem text and value, do they appear as expected or mismatched?
Use the Immediate Window to check:
ddlProgram.SelectedItem.Text
ddlProgram.SelectedItem.Value
If they appear the same then I would presume the binding code is being refired and the list is being regenerated with the first item being selected.
To check this put a break point on the binding code and see if it is fired more than once and correct the order of the methods appropriately.
ADDED:
If it works on your local environment it should work when published, if the code is the same? Looking at your code, I'd start by seperating out some of the databinding code into seperate methods rather than have everything in Page_Load, one becuase it's good practice and two because it will make debugging easier. Further than that I'm not sure what else to suggest.
On my webpage I am loading multiple instances of a usercontrol, sometimes the usercontrol is laoded within itself. I need to save a bunch of properties for the round trip of a post back but i am confused on how to save those properties to ViewState and set them again to the repeater items within the usercontrol.
Can anyone help me in this situation, I have read the MSDN on Viewstate but I am not understanding it quite well for some reason
This is how I load the parent user controls (child controls are loaded the same way with the same user control)
Protected Sub Load_Controls(ByVal list As List(Of BSEvaluationGroup.category), ByVal gid As Integer, ByVal pid As Integer, ByVal fid As Integer)
Dim item As BSEvaluationGroup.category
For Each item In list
Dim ctl As PerformanceEvaluationSubcontractorControl = CType(Page.LoadControl("PerformanceEvaluationSubcontractorControl.ascx"), PerformanceEvaluationSubcontractorControl)
ctl.categoryid = item.catid
ctl.categoryname = item.catname
ctl.projectid = pid
ctl.folderid = fid
ctl.groupid = gid
ctl.parentid = item.parid
ctl.clist = item.categories
ctl.plist = item.points
ctl.parentpage = Me
ctl.EnableViewState = "true"
If (Not subcon Is Nothing AndAlso Not subcon.points Is Nothing) Then
ctl.epnts = subcon.points
End If
AddHandler ctl.BubbleCalculate, AddressOf Me.PostRating
Select Case gid
Case 1
Me.officephld.Controls.Add(ctl)
Dim ohrule As HtmlGenericControl = New HtmlGenericControl("hr")
ohrule.Style.Add("width", "100%")
ohrule.Style.Add("background-color", "Silver")
ohrule.Style.Add("size", "1px")
ohrule.Style.Add("border-width", "0")
ohrule.Style.Add("padding-top", "1px")
ohrule.Style.Add("float", "left")
Me.officephld.Controls.Add(ohrule)
Case 2
Me.sitephld.Controls.Add(ctl)
Dim shrule As HtmlGenericControl = New HtmlGenericControl("hr")
shrule.Style.Add("width", "100%")
shrule.Style.Add("background-color", "Silver")
shrule.Style.Add("size", "1px")
shrule.Style.Add("border-width", "0")
shrule.Style.Add("padding-top", "1px")
shrule.Style.Add("float", "left")
Me.sitephld.Controls.Add(shrule)
End Select
Next
End Sub
Accessing view-state is simple such as ViewState("PropertyName"). The View State bag is specific to a control instance so you can use same property name within multiple control types and instances.
Only important thing here is that ASp.NET run-time has to match view-state bags to control instances and for that it uses ID property (which is unique within the parent naming container). So its important that you assign unique IDs to your dynamic user control instances ( and maintain same control tree hierarchy and ids on postback - essentially it means that execute the same code on postback and don't use random ids). So your code should be something like
...
Dim n As Integer
n = 1
For Each item In list
Dim ctl As PerformanceEvaluationSubcontractorControl = CType(Page.LoadControl("PerformanceEvaluationSubcontractorControl.ascx"), PerformanceEvaluationSubcontractorControl)
ctl.ID = "MyCtl" & n.ToString()
ctl.categoryid = item.catid
....
It was a control ID issue, I removed it instead of adding an ID
I've got an ASP.NET (VB.NET) page that has an Infragistics grid on it. One of the columns is called 'status'.
When I load the grid, I'm setting permissions to the fields at the column level, granting user roles (ENUM 'UserTypes') read/only or read/write permissions.
Next I need to loop through each row and assign permissions based upon the value (ENUM StatusVals) in the field 'status' as well as the user role.
I've got all this working, but it seems clunky and I want to improve it.
Here's a snapshot of one of the methods in which I pass in a row, the record status for that row, and the user type and loop through the cells to assign the permissions and colors. The question is: is there a more elegant way to do this so that as I add to it, it doesn't become a beast?
Private Shared Sub SetDetailRowReadWrite_ByStatusVal(ByVal DetailRow As
ig.UltraGridRow, ByVal sv As StatusVals, ByVal UserType As UserRoles)
If sv = StatusVals.Pending _
OrElse sv = StatusVals.Released _
OrElse sv = StatusVals.Shipped _
OrElse sv = StatusVals.Consolidated _
OrElse sv = StatusVals.HOLD _
OrElse sv = StatusVals.Cancelled _
OrElse sv = StatusVals.PartialShipped Then
For Each column As ig.UltraGridCell In DetailRow.Cells
If column.Key = "StatusVal" Then
column.Style.BackColor = Drawing.Color.LightGreen
column.Style.ForeColor = Drawing.Color.Black
If UserType = UserRoles.Fulfillment Then
SetFulfillmentStatusValEditPermission(sv, column)
End If
ElseIf Not (sv = StatusVals.Consolidated AndAlso UserType = UserRoles.Fulfillment) Then
column.Style.BackColor = Drawing.Color.White
column.AllowEditing = ig.AllowEditing.No
End If
Next
LockSizesRow(DetailRow, UserType, sv)
ElseIf sv = StatusVals.Incomplete AndAlso UserType = UserRoles.Fulfillment Then
For Each c As ig.UltraGridCell In DetailRow.Cells
c.AllowEditing = UltraWebGrid.AllowEditing.No
Next
End If
End Sub
You may want to look into the LoginView control in ASP.Net (2.0 and above) it gives you a templated control that maps templates to ASP.Net roles, including not logged in (anonymous).
This QuickStarts, although a bit dated demonstrate a simple example but the point is made rather cleanly.