Render mark up in one asp repeater item - asp.net

Ok just thinking of how to put the question right.
I have a datatable which returns some feedback details. The datatable contains feedbacks of two companies. The feedbacks of the datatable distinguished based on an indicator in the each data row.
The repeater is sorted on date order (newest first). If the companies are A and B, company A's feedback will be always listed in the top, since they are the newest. I have rendered an image and some wording to mention if the listed feedback is company B feedback.
Public Function RenderTitle(ByVal objItem As Object) As String
Select Case objItem
Case 1
Return String.Empty
Case 0
Return "Feedback below obtained whilst a member of our sister company " & "<img src=""../img/logo.png"" />"
Case Else
Return String.Empty
End Select
End Function
The title is rendered if the feedback belongs to company B.
Problem: I dont want the title to be rendered in all the items of the company B. I want it to render just in the first item (feedback) that belongs to Company B. How can I achieve this?

Formatting the contents of a DataList or Repeater control based upon the data can be accomplished by using two techniques.
The first technique is to create an event handler for the ItemDataBound event, which fires as each record in the data source is bound to a new DataListItem or RepeaterItem. In the ItemDataBound event handler, the current item's data can be examined, and then formatting can be applied to the contents of the template or, for DataListItems, to the entire item itself.
Alternatively, custom formatting can be realized through formatting functions. A formatting function is a method that can be invoked from the DataList or Repeater's templates that returns the HTML to emit in its place. Often, the HTML returned by a formatting function is determined by the values being bound to the current item. These values can be passed into the formatting function, either as scalar values or by passing-in the entire object being bound to the item (such as the ProductsRow instance).
http://msdn.microsoft.com/en-us/library/bb510136.aspx

Can you try something like this... (Ugly Code & Not Tested)
HTML
...
<asp:HiddenField ID="hdnIsTitleRenderd" runat="server"/>
...
CODE
Page_Load()
If Not IsPostBack
hdnIsTitleRenderd.Value = "False"
End If
End Sub
Public Function RenderTitle(ByVal objItem As Object) As String
Select Case objItem
Case 1
Return String.Empty
Case 0
If Convert.ToBoolean(hdnIsTitleRenderd.Value) = False
hdnIsTitleRenderd.Value = "True"
Return "Feedback below obtained whilst a member of our sister company " & "<img src=""../img/logo.png"" />"
Else
Return String.Empty
End If
Case Else
Return String.Empty
End Select
End Function

Related

Type-casting entity framework results in ItemDataBound

So I've trying to optimize performance of a page which generates the navigation menu for a website. Basically there is a table called T_product_cat which contains 4 fields I care about:
product_cat_name (the product category name)
product_cat_uid (the unique identifier of the row)
product_cat_thumbnail_path (the image that represents the category)
product_cat_parent_uid (the unique identifier of the product category
that we sit underneath - since these "product folders" can nest)
I need to create a top navigation bar which has just the names of the top level product categories and then for the dropdown navigation I need to output the sub product categories along with their images and links etc. I've got a basic ASP:Repeater control with a nested ASP:Repeater to handle the sub-product categories
Basically though I want to start using EF to start querying for my data. Initially I did a query to just pull the top level items and then in the ItemDataBound event of the repeater, I'd access the data and query the database again for the sub categories. But this was hitting the database 8x (since I've got 7 top level categories). To speed things up I've been trying different iterations of an initial query. Here's my final one for reference:
Using db As New RmpEntities
' query all the items and then all their subitems at the same time so that we don't have to repeatedly hit the database
Dim qry = (
From
productCat In db.T_product_cat
Where
productCat.company_uid = WmsSession.company_uid AndAlso
productCat.enabled = 1 AndAlso
productCat.product_cat_parent_uid = GUIDs.GlobalGuids.RootCategoryUid AndAlso
productCat.product_cat_hide <> 1
Order By
productCat.product_cat_sequence
Select
categoryName = productCat.product_cat_name,
categoryUid = productCat.product_cat_uid,
imagePath = productCat.product_cat_thumbnail_path,
subCategories = (
From
productSubCat In db.T_product_cat
Where
productSubCat.company_uid = WmsSession.company_uid AndAlso
productSubCat.enabled = 1 AndAlso
productSubCat.product_cat_parent_uid = productCat.product_cat_uid AndAlso
productSubCat.product_cat_hide <> 1
Order By
productSubCat.product_cat_sequence
Select
categoryName = productSubCat.product_cat_name,
categoryUid = productSubCat.product_cat_uid,
imagePath = productSubCat.product_cat_thumbnail_path
)
)
rptMainNav.DataSource = qry.ToList()
rptMainNav.DataBind()
End Using
When the repeater's ItemDataBound event fires I do have working code to pick apart the results and bind the data to the sub-repeater (this code works):
Protected Class CategoryInfo
Public Property categoryName As String
Public Property categoryUid As Guid
Public Property imagePath As String
End Class
Private Sub rptMainNav_ItemDataBound(sender As Object, e As RepeaterItemEventArgs) Handles rptMainNav.ItemDataBound
Dim rItem As RepeaterItem = e.Item
Select Case rItem.ItemType
Case ListItemType.Item, ListItemType.AlternatingItem
Dim dataItem = rItem.DataItem
Dim subRepeater As Repeater = rItem.FindControl("rptSubNav")
Dim parentUid As Guid = dataItem.categoryUid
Dim st = DateTime.Now
Dim x As New List(Of CategoryInfo)
For Each item In dataItem.subCategories
x.Add(New CategoryInfo With {.categoryName = item.categoryName, .categoryUid = item.categoryUid, .imagePath = item.imagePath})
Next
subRepeater.DataSource = x
dTime.Add(New MyTimes With {.g = parentUid, .ts = DateTime.Now - st})
subRepeater.DataBind()
End Select
End Sub
But how can I cast the Object type returned by accessing the repeater's DataItem to a more strongly-typed object so I can get intellisense?
Or is there some way I can give up on the worries about typing and just use the query results directly as a DataSource for the sub-repeater? That was my first goal but when I tried to take
dataItem.subCategories.ToList()
It failed with:
Public member 'ToList' on type 'CompensatingCollection(Of VB$AnonymousType_1(Of String,Guid,String))' not found.
When I tried to modify my CategoryInfo class to have another property to hold a list of CategoryInfo classes (subcategories) and have the main query return structured data (so I'd have a type to cast to in the ItemDataBound) things failed even worse. The original query's .ToList failed so I couldn't even do the initial bind.
What is the correct way to handle this? Continue working on properties of an Object which represents an anonymous type? Surely there must be something better.
EDIT:
Robert - Thanks for your suggestions. It turned out that I did NOT have a navigation property to SubCategories. The reason being that the people who came before me designed the database such that all top level categories end up with an artificial product_cat_parent_uid (just some random GUID). It doesn't point to a real product category in the database - it just acts as a placeholder. I question why they didn't just use NULL but it's too late to change it. At any rate since the item doesn't exist in the database, the database itself doesn't have the foreign key since it's faking the link. The databse won't create the link with product categories pointing to a product categorie that is not in the database. I did use your suggestion to find more information about what could be done and I used this article (Entity Framework - Add Navigation Property Manually) to add those links I need.
The question still remains though - can I get strong typing within the ItemDataBind event?

add dataitem(from string array) to listview

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.

Accessing server-side control by its ID property in ASP.net

On my default.aspx page I have a bunch of divs with an ID and runat="server":
<div id="serverOne" runat="server"></div>
<div id="serverTwo" runat="server"></div>
<!--etc...-->
In my code behind I've declared a multidimensional array (or grid) with 2 values -- the first being an IP address and the second the server name.
Dim servers = {{"10.0.0.0", "serverOne"}, {"10.0.0.1", "serverTwo"}}
My question is, is there a way where I can target my divs from my code behind using a value from the array?
For i As Integer = 0 To 1
'This is what I want it to do:
servers(i, 1).InnerHtml = "<span>Testing " & servers(i, 1) & "</span>"
Next
You can do this using the FindControl method on the page. However, out of the box FindControl looks only at the first level of children, and does not go into the childrens' children. In order to handle this you need to use a helper method that allows FindControl to recursively search through the control hierarchy to find the one you want. Add this method to your code behind, or some shared class that multiple pages can access:
Protected Function FindControlRecursive(control As Control, id As String)
If (control.ID = id) Then
Return control
End If
For Each ctl In control.Controls
Dim foundControl = FindControlRecursive(ctl, id)
If (foundControl IsNot Nothing) Then
Return foundControl
End If
Next
Return Nothing
End Function
Once you have that, it's pretty easy to find your <div> just by using the string ID property.
For i As Integer = 0 To 1
Dim div = CType(FindControlRecursive(Me, servers(i, 1)), HtmlGenericControl)
div.InnerHtml = "<span>Testing " & servers(i, 1) & "</span>"
Next
Reference: http://forums.asp.net/t/1107107.aspx/1

Add New Item to Already Bound ListView in ASP Net (Unable to set DataKeys/FieldName)

My overall goal is to add fake/unbound items to a listview control (for final HTML Table output reasons). This is a code behind solution. Users will not be adding items as it will be outputted in a rigid table.
I have looked at several examples and while this is easy for a dropdown it is not for listview.
The code below works without error, but my item is not shown on runtime. I think the class is not setting the item fieldname correctly, but I can't figure out the right syntax to fix it.
ColumnNameAList.DataSource = PeriodDataView
ColumnNameAList.DataBind()
Dim test As New Example1("ColumnNameA")
Dim newItem As New ListViewDataItem(ColumnNameAList.Items.Count, ColumnNameAList.Items.Count)
newItem.DataItem = test
ColumnNameAList.Items.Insert(ColumnNameAList.Items.Count, newItem)
ColumnNameAList.Items.Add(newItem)
Here is the Example1 class that is supposed to set the DataValueField:
Public Class Example1
Public Sub New(ColumnNameA__1 As String)
ColumnNameA = ColumnNameA__1
End Sub
Private m_ColumnNameA As String
Public Property ColumnNameA() As String
Get
Return m_ColumnNameA
End Get
Set(value As String)
m_ColumnNameA = value
End Set
End Property
End Class
This outputs my original datasource list, but not the added item.
<ItemTemplate>
<td>
<%# Eval("ColumnNameA")%>
</td>
</ItemTemplate>
In the end I could only reliably solve this with a codebehind solution.
I made a copy of the original datasource, modified my copy and then databound to it.
Dim MyOriginalTableSource As Data.DataView = DataManager.example()
Dim ModifiedTable As DataTable = MyOriginalTableSource.ToTable
'do stuff here
Mylistbox.DataSource = ModifiedTable
Mylistbox.DataBind()
Won't work for everyone, but in this case it works fine for me.
There could be a couple of issues with the way you are approaching this, including that the ListView is already databound and that you are both adding and inserting the newItem.
When we have a scenario like this, we take one of two approaches:
1) Add the new item to the data source before the source is data bound.
2) Remove databinding and manually create each of the list view items, then add your new item at the beginning or end of the loop.
Another way to do it would be to inject it into the sql.
select col1, col2, col3 from table1 union select '1','2','3'
this would ensure that the item is always added, and asp.net doesn't need to know or care.
You can add this into the sql query or add it from the behind code before binding query. if you are not binding with sql, you can also do this to any list item with LINQ

dynamically set control ID

i have few texboxt and dropdownlist, which their id would be something like "txtName1, txtName2, txtName3..." and "ddlAction1, ddlAction2, ddlAction3...."! I would to to dynamically set the textboxt and dropdownlist id into something like this:
for i as integer = 0 to 6
a = txtName+i.text
b = ddlAction+i.SelectedValue
next i
Need help from you guys to do this! thanks....
The key is FindControl, which looks up a control by its expected ID:
For i As Integer = 0 To 5
Dim txt As TextBox = TryCast(Me.Page.FindControl("txtName" & i.ToString()), TextBox)
Dim ddl As DropDownList = TryCast(Me.Page.FindControl("ddlAction" & i.ToString()), DropDownList)
If txt IsNot Nothing AndAlso ddl IsNot Nothing Then
Dim a As String = txt.Text
Dim b As String = ddl.SelectedValue
End If
Next
It will return null/nothing if a control with that ID isn't found.
Note that FindControl will only search the given control's (or Page's) immediate children, not the entire control tree. To search recursively, you need to use your own FindControl method.
Private Function FindControlRecursive(ByVal control As Control, ByVal id As String) As Control
Dim returnControl As Control = control.FindControl(id)
If returnControl Is Nothing Then
For Each child As Control In control.Controls
returnControl = child.FindControlRecursive(id)
If returnControl IsNot Nothing AndAlso returnControl.ID = id Then
Return returnControl
End If
Next
End If
Return returnControl
End Function
.Net requires you to resolve your variable names at compile time, rather than runtime like your code is trying to do. This is a good thing, as it prevents you from making certain kinds of errors. But it does mean you'll need to look at an alternative approach for this particular problem.
One option is a FindControl -based method. But odds are the controls you care about are grouped together on the page. If they aren't already, put them in a common container control, like a panel. Then you can do something like this (requires System.Linq):
For Each t As TextBox In MyContainer.Controls.OfType(Of TextBox)()
a = t.Text
Next t
For Each d As DropDownList In MyContainer.Controls.OfType(Of DropDownList)()
b = d.SelectedValue
Next d
Also, I hope you're really doing something other than assignment inside your loop. Otherwise, most of the work is for nothing as you will exit the loop having simply assigned the value from the last iteration to your variable.
Finally, it seems like these controls might work in pairs. To me, that's a situation that calls out for you to implement a user control.
I'm not a webforms expert, but I believe the page stores a reference to each control present in the page.
You can think of webforms like an n-ary tree... each control has a parent and can have 0 to many children. So, if these are static controls you should just be able to grab a reference to their parent and iterate over that... with no need for the children's ids.
Also, you can query for children based on their id... something like myControl["myID1"] so you can concat the number with the string and get the control that way.
Lastly, if these are purely dynamic controls, i.e. you don't know how many there are, just store references to them in an ordered collection and iterate over them that way.
EDIT:
Here we go:
WebControl myControl;
myControl.Controls.Add(someControlReference);
Then, to grab a control by ID:
WebControl someControl = myControl.FindControl("someControlID1");
From there you can do like:
string a = someControl.Text

Resources