How to improve method for assigning grid permissions - asp.net

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.

Related

Addhandler for dynamically created controls

I've written a piece of code that retrieves the number of photo albums a person has and then dynamically creates the same amount of ImageButtons within the 'AlbumsPanel' panel.
I have given Each ImageButton a unique Id (Grabs AlbumId from Albums table within sql server) and would like to be able to identify which ImageButton the user clicks (Store the myAlbum.ID in a variable some how so it can be used in a later stored procedure to retrieve pictures that belongs to that album).
spRetrieveAlbums example code:
SELECT AlbumId FROM dbo.Albums WHERE Id = #CurrentUserId;
SET #AlbumCount = ##ROWCOUNT;
Main.aspx.vb code:
Connection.Open()
sqlReader = spRetrieveAlbums.ExecuteReader()
If sqlReader.HasRows() Then
Do While sqlReader.Read()
Dim myAlbum = New ImageButton
myAlbum.ID = sqlReader.GetInt32(0)
myAlbum.Visible = True
myAlbum.Width = 160
myAlbum.Height = 160
myAlbum.BorderStyle = BorderStyle.Solid
myAlbum.BorderColor = Drawing.Color.WhiteSmoke
myAlbum.BorderWidth = 1
AlbumsPanel.Controls.Add(myAlbum)
Loop
End If
sqlReader.Close()
AlbumCount = spRetrieveAlbums.Parameters("#AlbumCount").Value
Connection.Close()
AlbumCountSpan.InnerHtml = "Albums: " & AlbumCount
Someone mentioned using an addhandler to the code but I'm not 100% sure how they work! Can someone point me in the right direction and give an example?
Add in loop block:
AddHandler myAlbum.Click, AddressOf NameOfMethode
or separate metode each iteration, using lambda:
AddHandler myAlbum.Click, Sub() someAction
In the first way you should identify the sender. like:
Sub NameOfMethode(sender As Object, e As ImageClickEventArgs)
Label1.Text = Ctype(sender, ImageButton).ID
End Sub
but in the second way you can write Appropriate function for each iteration. like:
AddHandler myAlbum.Click, Sub() Label1.Text = sqlReader(0)

Changing DropDown Selected Item Based on Selected Value (ASP.NET)

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.

Confused about using ViewState with dynamically added usercontrol

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

asp.net multithreading with synclock

i have some test code which i run at every load of a page in my asp.net website
this is the code
Sub TryThreads()
Dim t1 = New Thread(AddressOf TryLock)
t1.Priority = ThreadPriority.Lowest
t1.Start()
Dim t2 = New Thread(AddressOf TryLock)
t2.Priority = ThreadPriority.Lowest
t2.Start()
End Sub
Sub TryLock()
Dim LockObject = New Object
SyncLock LockObject
DoTrace("entered locker")
For x = 0 To 10000
Next
DoTrace("exiting locker")
End SyncLock
DoTrace("exited locker")
End Sub
the "dotrace" simply add a record to a log table in the db
now the right result would be that i should have the entries in the db in order "entered","exiting","exited"
but actually when i look in the db i see first 2 "entered" then 2 "exiting" etc.
meaning that the multithreading is working ok, but not the synclock
is that correct?
and how can this be fixed?
the real code will be adding records to the db and might be called from several pages of different sessions, but the same code must not run twice concurrently
i do appreciate anybodys help
thank you very much!!!
EDIT:
in response to Sashas wonderful post i changed my code to a class (it was in a module) and now it looks like this:
Public Class CheckClass
Property LockObject As Object
Get
If HttpRuntime.Cache("CheckSessionsLock") Is Nothing Then HttpRuntime.Cache("CheckSessionsLock") = New Object
Return HttpRuntime.Cache("CheckSessionsLock")
End Get
Set(ByVal value As Object)
If value Is Nothing Then
HttpRuntime.Cache.Remove("CheckSessionsLock")
Else
HttpRuntime.Cache("CheckSessionsLock") = value
End If
End Set
End Property
Sub TryThreads()
Dim t1 = New Thread(AddressOf TryLock)
t1.Priority = ThreadPriority.Lowest
t1.Start()
Dim t2 = New Thread(AddressOf TryLock)
t2.Priority = ThreadPriority.Lowest
t2.Start()
End Sub
Sub TryLock()
SyncLock LockObject
DoTrace("entered locker")
For x = 0 To 10000
Next
DoTrace("exiting locker")
End SyncLock
DoTrace("exited locker")
End Sub
End Class
now it works 80-90% of the time.
on page load i have:
Dim cc = New CheckClass
cc.TryThreads()
if i open multiple pages at once, they still clash some times. but if i'm correct, the issue is now not with the synclock as much as with the httpruntime.cache, because when using a standard property, on one page, the code works 100%.
so how can i make sure that 2 threads, even from totally different sessions never run the trylock simultaneously?
thank you all for helping out
You are creating a new object instance when the TryLock method is called, and use that for locking. If you want mutual exclusion between the two threads, you need to use a common object instance for locking, e.g. a static member of your class or a parameter that you pass to both threads.

How can I remove nodes from a SiteMapNodeCollection?

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?

Resources