How to perform same operations on both WebControls and HtmlControls - asp.net

I find myself needing to preform the same actions on both HtmlControls and WebControls. I am a firm believer in DRY and find the fact that there is only the Control class to use if I want to consolidate the functions on both types. The problem that I have with using Control is that there certain properties that both HtmlControl and WebControl expose that Control does not. In the current case, the Attributes property is the problem. Does anyone have any suggestions on how to avoid the duplication of code in this type of instance?

Both HtmlControl and WebControl implement the interface IAttributeAccessor (explicitly). Use the IAttributeAccessor.SetAttribute instead. I'm not a vb.net coder, so I leave the task of writing the code to the reader. ;)

In the past I've duplicated code to set the attributes for HtmlControls and WebControls. However, here's another idea:
Private Sub SetAttribute(ByRef ctrl As Control, ByVal key As String, ByVal value As String)
If TypeOf ctrl Is HtmlControl Then
DirectCast(ctrl, HtmlControl).Attributes(key) = value
ElseIf TypeOf ctrl Is WebControl Then
DirectCast(ctrl, WebControl).Attributes(key) = value
End If
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
For Each ctrl In Me.Controls
SetAttribute(ctrl, "class", "classname")
Next
End Sub

I know what you mean. Theoretically, you could do one of the following:
Use reflection to assign some of those common settings.
Create a wrapper class that can take either a webcontrol or html control reference, and assign the values. (if control is webcontrol) assign value else if (html is htmlcontrol) assign value, something like that.
Create another logical class to store the common settings, then another component to copy those settings and apply them to the class.
Ultimately, there isn't any common bridge (no common base class or interface). What kind of assignments are we talking about?

Simon's answer works:
Private Sub SetAttribute(ByRef ctrl As IAttributeAccessor, ByVal key As String, ByVal value As String)
ctrl.SetAttribute(key, value)
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
For Each ctrl In Me.Controls.OfType(Of IAttributeAccessor)()
SetAttribute(ctrl, "class", "classname")
Next
End Sub

Related

EnableViewState causing loss of value

Using vb.net 4.5 and Telerik 2017.2.711.45 (Q2)
I am trying to get radgrid filter expressions and a public string variable to persist across postbacks.
With EnableViewState=FALSE, radgrid filter expressions do not persist through postback, however a public string variable (stringVar) does persist.
When I set EnableViewState=TRUE the filter expressions in radgrid do persist, however it causes stringVar to not persist.
From my understanding of ViewState, it makes no sense that setting EnableViewState=TRUE would cause stringVar to not persist across postbacks. I would love to know why this is occurring and what I could do to resolve this.
EDIT:
The highlighted Line is where an error would be thrown because ReportTitle no longer has a value.
Partial Class displayxslgrid
Public ReportTitle As String
Public ReportsDB As reportDataBase
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Page.EnableViewState = True
Reports = New reportDataBase.Global_Functions(System.Web.HttpContext.Current)
End Sub
Protected Sub RadGrid1_NeedDataSource(sender As Object, e As Telerik.Web.UI.GridNeedDataSourceEventArgs) Handles RadGrid1.NeedDataSource
Call BindRadGrid1()
End Sub
Protected Sub RadGrid1_ItemCommand(ByVal source As Object, ByVal e As Telerik.Web.UI.GridCommandEventArgs) Handles RadGrid1.ItemCommand
Dim strReportTitle As String
Select Case e.CommandName
Case RadGrid.ExportToExcelCommandName, RadGrid.ExportToWordCommandName, RadGrid.ExportToCsvCommandName
strReportTitle = ReportTitle.Trim
End Select
End Sub
Public Sub BindRadGrid1()
Dim strReportTitle As String
Dim dt As DataTable = Nothing
ReportTitle = dt.Rows(0).Item("ReportTitle")
strReportTitle = dt.Rows(0).Item("ReportTitle").ToString
'RadGrid1 Data source gets set here along with other stuff
End Sub
End Class
Using view state is normal, and Telerik controls need it to preserve their values across post-backs. A public string property on your page class should not persist, and should be set/calculated every time. If you absolutely need something like that to persist, save the value in a hidden server control, or have it in the QueryString of the URL.
So it turns out, that that variable was not truly persisting. It was getting its value from the bindradgrid1. When EnableViewState=True the need data source event is not fired, therefore the bindradgrid1 is not called and the variable does not get a value. Simple fix was adding a bindradgrid1() in the item command sub so that even with EnableViewState=True, bindradgrid1() will still get called. Thanks for all who helped.

How to share a variable value with a webmethod?

I have this code:
Dim main_id As int
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
main_id =1
End Sub
<WebMethod()> _
Public Shared Function BindPersonnel(ByVal product_id As String) As String
Dim project_id AS int16
project_id=main_id 'this doesn't work
End Function
On page load, I set the value of variable main_id and I need a way to somehow share the main_id with webmethod function, how can this be done? I have not tried session variable and I don't want to use session variable. I found this link Accessing a common variable in WebMethod and jQuery but I can't figure out how this is going to solve my problem. I also read some posts about using hidden field, which requires me to go two trips. I would love to avoid that.
WebMethods are independent of the other variables on the page. If you want to access main_id, you could declare it as Private Shared main_id As Integer, but that would then cause all of your users to have access to the same ID value, which you probably don't want.
Perhaps the easiest way to do this is to store the value in SessionState, and enable sessionstate access in the WebMethod. On the other hand, this removes the asynchronous-like functionality that you are looking for (it may not be an issue).
SessionState will give you the ability to have a per-session value (avoiding the use of the Shared solution mentioned above).
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Session("main_id") = 1
End Sub
<WebMethod(EnableSession:=True)>
Public Shared Function BindPersonnel(ByVal product_id As String) As String
Dim project_id As Integer = CInt(HttpContext.Current.Session("main_id"))
End Function

How To Pass Control As A Variable

This is one of those problems which seems like it should have a simple solution but I can't work out what it is!
How can I pass a control from one sub to another if the first sub doesn't actually call the second? For example, where btnChangeText is in a panel that has a ModalPopupExtender called mpExample, and therefore isn't usually visible:
Protected Sub btnChangeText_Click(sender as object, e as EventArgs) Handles btnChangeText.Click
<SpecifiedTextBox>.Text = "Hello"
End Sub
And then on the main page, visible at all times, is a button associated with each textbox. In this example, it's textbox15:
Protected Sub btnChangeTextBox15_Click(sender as object, e as EventArgs) Handles btnChangeTextBox15.Click
<Set TextBox15 as variable>
mpExample.Show()
End Sub
I know it's a silly example - believe me when I say that the real application I want to make of this actually makes sense! But the point is that I want to somehow store the name of the control to be updated by the first sub when the second sub is run.
If I was calling the first sub from the second it'd be easy, I'd just pass it as an argument, but I'm not. The first sub is called from a button click and is an independent action from the running of the second sub.
I don't seem to be able to use a session variable (my first thought) because I can't find any way to store the control name as a string and then convert it back to an actual control when the first sub runs. That'd be the easiest answer if somebody could tell me how to do it.
One approach would be to store the control's ID as a string in a Session variable, and then use the FindControl method to grab the control in your 2nd Click event.
Protected Sub btnChangeTextBox15_Click(sender as object, e as EventArgs) Handles btnChangeTextBox15.Click
Session("currentTextBox") = TextBox15.ID
mpExample.Show()
End Sub
Protected Sub btnChangeText_Click(sender as object, e as EventArgs) Handles btnChangeText.Click
Dim currentTextBox As TextBox
currentTextBox = CType(Page.FindControl(Session("currentTextBox")),TextBox)
currentTextBox.Text = "Hello"
End Sub
Note that if your TextBox15 control is inside some kind of container (a Panel or something), you'll need to use that container's FindControl method, rather than Page.FindControl.
Another approach is to store the TextBox itself in a Session variable, and then pull that out to set the text in your other method. Note that this only works if the methods are both called in the same request (which doesn't sound like it would work for your use-case). Here's what that would look like:
Protected Sub btnChangeTextBox15_Click(sender as object, e as EventArgs) Handles btnChangeTextBox15.Click
Session("currentTextBox") = TextBox15
mpExample.Show()
End Sub
Protected Sub btnChangeText_Click(sender as object, e as EventArgs) Handles btnChangeText.Click
Dim currentTextBox As TextBox
currentTextBox = CType(Session("currentTextBox"), TextBox)
currentTextBox.Text = "Hello"
End Sub

populate multiple dropdownlists with same data

I need to populate 12 asp:dropdownlist 's with the same 0-9 options. (ASP.NET VB)
Now I could just populate them manually, but I wandered if there is an easier method... Can i populate an Array and use that to populate them all?
I thought I could write a small function that you pass the DDL's name into, but how then could I use that function input string as the dropdownlist name?
I know this is simple stuff but its something Ive not needed to do before, and cant see a simple method to do it.
Try this
Protected Sub Page_Load(ByVal sender as Object, ByVal e As EventArgs)
If (Not Page.IsPostBack) Then
BindDropdown(list1)
BindDropdown(list2)
BindDropdown(list3)
End If
End Sub
Private Sub BindDropdown(ByVal list As DrodownList)
Dim items As String() = {"0","1","2","3","4","5","6","7","8","9" }
list.DataSource = items
list.DataBind()
End Sub
Add this to cache and make a common helper function to bind the dropdown

Preserve Data-structure on AJAX postback

Partial Class ClientCenter_UpdateSub
Inherits System.Web.UI.Page
Structure PInfo
Dim Name As String
Dim Surname As String
End Structure
Dim OldPInfo As New PInfo
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
'blah blah
OldPInfo.Name = Dt.Rows(0).Item("Name").ToString
OldPInfo.Surname = Dt.Rows(0).Item("Surname").ToString
end if
end sub
End Class
The first time the page loads my structrure is filled correctly.
After an AJAX postback all the structure fields are setting to nothing. (It seems that the Dim OldPInfo As New PInfo is called again), but i should better ask the SO Experts.
So anyway, what am i doing wrong here?
First off, You should never assign a variable outside of a property or a method.
Second, web applications are stateless (which means NOTHING is automatically saved from call to call - unless you store it somewhere like Viewstate, Session, etc.).
Remember to accept this answer if it helps solve your problem.

Resources