A few years ago, I decided to create my own DataGrid as I didn’t like the standard one provided by Microsoft. It’s a very simple function which takes a DataTable as an input parameter and which returns a string (the html code to display a table on a webpage).
It’s very flexible (there are some optional parameters to do the paging, sorting and to format each column the way I want) and fast (only the records which are used are retrieved from the database).
The function itself is very short (about 20ish lines of code). I’ve been using it for at least 4 years now.
Assuming you have a PlaceHolder on your webpage, this is how you would call the custom function:
MyPlaceHolder.Controls.Add(new LiteralControl(CreateCustomGrid(MyDataTable)))
CreateCustomGrid(MyDataTable))) would return something like this (if MyDataTable has 2 columns and 2 rows):
<table class="MyClass" rules="all">
<tr>
<th>Column1</th>
<th>Column2</th>
</tr>
<tr>
<td align="center">Value1</td>
<td align="center">Value2</td>
</tr>
<tr>
<td align="center">Value3</td>
<td align="center"><a href=’MyLink’>Value4</a></td>
</tr>
</table>
Internally the function knows how to format each column (this function is only being used on one website) but it’s also possible to change it for each individual column by using optional parameters. Same for the paging and sorting. All in all it’s very flexible and very easy to use.
Now things have changed and the DataGrid has been replaced by the GridView and/or ListView. I’ve looked at them but I don’t see anything that they do that my function doesn’t so I would be tempted to carry on using my function but I might be overlooking something. At the same time, it looks a bit odd to carry on using a custom function to generate an html table. What’s your views on this?
If your code works and is seasoned, I wouldn't change any existing code. For additional functionality, you might want to consider wrapping it in a custom data-bound WebControl. That way you can use datasources, etc.
I'd say that you should investigate the GridView/ListView to see what they can do but, ultimately, if you and your customers are happy with your own code and it works for you, does everything you need it to then there's no need to change just because there is something else out there.
Related
Using the IMPORTXML function, is it possible to construct an XPATH query that pulls the Industry value for a given Wikipedia page?
For example, the value I want to pull from this page - https://en.wikipedia.org/wiki/Target_Corporation - is "Retail" whereas on this page - https://en.wikipedia.org/wiki/Boohoo.com - it would be "Fashion".
You want to create the xpath for retrieving the Industry value for a given Wikipedia page.
If my understanding is correct, as other pattern, how about the formula with this xpath? Please think of this as just one of several answers.
Sample formula:
=IMPORTXML(A1,"//th[text()='Industry']/following-sibling::td")
The xpath is //th[text()='Industry']/following-sibling::td.
In this case, the URL of https://en.wikipedia.org/wiki/Target_Corporation or https://en.wikipedia.org/wiki/Boohoo.com is put in the cell "A1".
Result:
Reference:
XPath Axes
Added:
From your replying, I knew that you want to add 2 more URLs. So all URLs are as follows.
https://en.wikipedia.org/wiki/Target_Corporation
`https://en.wikipedia.org/wiki/Boohoo.com
`https://en.wikipedia.org/wiki/Woot
`https://en.wikipedia.org/wiki/TripAdvisor
Issue and workaround:
For above URLs, when the formula of =IMPORTXML(A1,"//th[text()='Industry']/following-sibling::td") is used, Retail, Fashion, Retail and Travel, services are returned.
When the xpath is modified to //th[text()='Industry']/following-sibling::td/a, Retail, #N/A, #N/A and Travel are returned.
The reason of this is due to the following difference.
<tr>
<th scope="row">Industry</th>
<td class="category">Travel services</td>
</tr>
and
<tr>
<th scope="row" style="padding-right:0.5em;">Industry</th>
<td class="category" style="line-height:1.35em;">Retail</td>
</tr>
and
<tr>
<th scope="row" style="padding-right:0.5em;">Industry</th>
<td class="category" style="line-height:1.35em;">Fashion</td>
</tr>
By this, I think that unfortunately, in order to retrieve Travel, Retail and Fashion from above, those cannot be directly retrieved with only one xpath. So I used a built-in function for this situation.
Workaround:
In this workaround, I used INDEX. Please think of this as just one of several answers.
=INDEX(IMPORTXML(A1,"//th[text()='Industry']/following-sibling::td"),1,1)
The xpath is //th[text()='Industry']/following-sibling::td. This is not modified.
In this case, the URL is put in the cell "A1".
When 2 values are retrieved, the 1st one is retrieved. By this, I used INDEX.
Result:
try:
=INDEX(IMPORTXML("https://en.wikipedia.org/wiki/Boohoo.com",
"//td[#class='category']"), 2, 1)
=INDEX(IMPORTXML("https://en.wikipedia.org/wiki/Target_Corporation",
"//td[#class='category']"),2,1)
I am trying to access a property of cells in a table.
<table id="m-103" class="m-row" cellspacing="0">
<a name="2"></a>
<table id="m-108" class="m-row " cellspacing="0">
<a name="3"></a>
<table id="m-191" class="m-row " cellspacing="0">
<tbody>
<tr>
<td class="m-st">
<td class="m-jk m-N">
</td>
</td>
</tr>
</tbody>
</table>
This is the xpath I have so far
.//*[#class='m-row']/tbody/tr/td[#class='m-jk']
but it will only access the cells in the first table.
I am interested in the m-N class value. Not every table has the m-N value. I'm only interested in the ones that do. Is there a way to only check tables that contain "m-N" or do I have to go through each one and check and if so how do I do that? I know now only how to go to specific paths so I have no clue how to iterate through each table.
How do I access the second class value "m-N"? Every css or xpath Iv'e used does not work, and again they are only for a predetermined table.
I saw an answer but the person was using jquery? Is this something I should learn and use as well? Can I if I'm using Ruby and Selenium?
How to get the second class name from element?
There are many more tables this is only 3 of them I'm showing for the example. Also the number of tables and cells changes frequently.
To get the td elements which have a class attribute which contains m-N you can use the xpath function contains().
Try this:
"//td[contains(#class, 'm-N')]"
This could get a little bit more complex if there also other classes which contains 'm-N' like 'm-Nx'. Than you have to do something like this:
"//td[contains(concat( ' ', #class, ' '), ' m-N ' )]"
Goal
What I need is to display a SQL DataTable to the user on a webpage through ASP.NET and allow them to select a number of rows very quickly and easily, and then hit any of a number of different buttons to signify different operations. The selected rows are to be sent off to be processed (generally sent to a webservice or similar through codebehind). This is a somewhat generic solution, as I will be using this same setup in many places - thus the data, and the available operations will be different.
The goal is to be able to know basically nothing about the data - that is for the user to understand. If this concept is already impossible (though I believe it is not), please let me know.
Pulling from T-SQL (SQL Server 2008 R2 or later).
Using ASP.NET to make aspx webpage. Codebehind is done in VB.NET, in both cases we're using 4.0
I'm fluent in C# and VC++, so if you can't easily translate your code, don't worry about that.
The problem
My problem has been creating a row selection process for the user which persists while sorting or paging the table, is quick for the user and relatively quick in response time, and which is reflected in the GridView's bound data (as this allows me to use a filter on a DataView to produce a DataTable for passing). If there's another way to know the selected rows then I'm all game so long as it is persistant and quick for the user.
I'm not debugging and I'm not having syntax issues (I do not believe) - I don't know how to proceed.
I don't necessarily need code as a solution - I need to understand if my metaphor is either bad practice, or simply uncommon and thus not well supported. In either case, what is an appropriate way to proceed? If nothing else, where do I look for a solution besides endless API and Tutorials?
My original plan
I wanted to use asp:GridView for binding the DataTable pulled from TSQL since I can auto-generate columns. This allows me to display data without needing to know what it is. I planned to add a specific boolean Column (left most) for storing the User's row selection. Then I can simply run a filter on the DataTable to produce a DataView, get the resulting DataTable from that DataView, and pass it down the chain.
All the columns except our specific selection one would be Read-Only. The user isn't editing data - their selecting records and view their selection side-by-side with the viewable relevant data.
Of note, I planned to check for name collision so our added Column doesn't collide with any existing Column in the DataTable - I'd rename it as needed using 'Select' followed by an integer produced by looping through, comparing my name to other columns, incrementing as needed.
I presumed that once I set this up, it would just handle itself: the user could click the resulting checkbox column cell's and it would change the data on the fly. The point of saving the selection was their selection persisted through sorting and paging for convenience (I save the GridView's DataSource and re-bind when necessary). It didn't really matter if the user refreshed the page and completely and lost their checkboxes - they would need to review any changes/new data, and they wouldn't need more then a brief moment to check off whatever they wanted.
This did not happen.
I found that AutoGenerateColumns does not produce checkboxes for boolean DataColumns. It produces text, so I end up with the word 'True' or 'False'.
I began looking for a way to get check boxes in the cells for a boolean DataColumn. I found that for the added boolean column, I could bind an asp:CheckBoxField to it using the attribute DataField. Of course, I have to figure out how to point it at a variable DataField...
<Columns>
<asp:CheckBoxField DataField="Select" HeaderText="Select"
ReadOnly="False" SortExpression="Select">
<ItemStyle HorizontalAlign="Center" />
</asp:CheckBoxField>
</Columns>
However, because a GridView is designed for Row-By-Row editing, all the resulting CheckBoxes are disabled and cannot be checked. This is because their containing row is not in Edit Mdoe. I do not want to enable Row-By-Row editing using a button or similar metaphor as the user often needs to check off several rows. More clicks is bad, and annoying. They should be focused soley on their data and making their selection, not worrying about remembering to enter and end edit mode. Also, the metaphor seems poorly placed here since the checkboxes are the -ONLY- editable data - and are not part of the represented data.
I could, of course, make a new class which inherits from GridView, overload the method which generates columns to produce checkboxes instead of text fields - but I felt there had to be a more straightforward path. Maybe this is the way to go? But maybe it would have the same editing issue as above - I'm not sure.
So, next I tried looking at using an asp:TemplateField as a column in my GridView. This TemplateField contains an asp:CheckBox who's Checked state is based on the underlying bound data value. The issue here is trying to make changing the check-state update that bound value. I would need some way of looking up a GridView value I could then use to find the same row in the DataTable. I've seen great examples using a Primary Key. While I could assume everyone keeps a Primary Key for any possible table, this might not be the case! I could further add a Primary Key myself, but now it looks like I would be adding and removing two columns before I pass a DataTable off to a WebService instead of just one. Again, I also would need to be able to assign a Column name which is dynamic to avoid collision.
<asp:TemplateField HeaderText="Select">
<ItemTemplate>
<asp:CheckBox runat="server" ID="CheckBox"
Checked="<%# DataBinder.Eval(Container.DataItem, "Select") %>"
AutoPostBack="true" OnCheckedChanged="SelectCheckBox_CheckChanged" />
</ItemTemplate>
</asp:TemplateField>
I stopped myself - This path is also fairly indirect. Surely there is a much cleaner, simpler way to do this. Is it uncommon for someone to display Read-Only data where records are selected to be submitted, in one fashion or another, for processing/updating/alteration?
Is my choice of GridView poor? I haven't yet found another good way to represent Table data.
I have two fields which are taken in two variables that are StrcategoryName and StrcategoryUrl means category name is stored in StrcategoryName and url is stored in StrcategoryUrl which are come from text file and then bind to input type text
now i want two edit these data in textfile,
which are stored in text file. Now I want to edit these two fields
<tr>
<th align="left" valign="middle" class="text" scope="row"><b>Enter Category Name:</b></th>
<td align="left" valign="middle"><input type="text" name="txtcategoryname" value="<%=StrcategoryName%>" /></td>
</tr>
<tr>
<th align="left" valign="middle" class="text" scope="row"><b>Enter Url Of Category Image :</b></th>
<td align="left" valign="middle"><input type="text" name="txtName" value="<%=StrcategoryUrl%>"/></td>
</tr>
I have tried following code but I am getting an error.
You need to use the "Scripting.FileSystemObject" to read/write textfiles using ASP. Most of the examples you will find on the web show you the contents of these text files by using Response.Write to display the values.
One of the better sites for learning how to read/write textfiles files is located here.
It sounds like you need to do for 4 things:
1. Read the text file using the Scripting.FileSystemObject and store the data into ASP variables
2. Place these variables into an HTML form that posts to an update page
3. On the update page obtain the updated values using the Request object
4. Save these new values to the text file using the Scripting.FileSystemObject
Text manipulation is not easy, because it's always treated as a collection of lines that we need to parse line by line or know exactly what line holds what ... and someone come along and change the order ... it's not pretty!
That is why we always use XML to store data into files as the Framework gives us much more control on what is stored and it's easy to remove, edit, insert or delete something.
in Classic ASP, you have several tutorials that help you read and write XML files all over the web and several questions here at SO.
Asp XML Parsing
ASP Classic - XML Dom
and one from my favorite site back the days
Accessing XML Data using ASP
Currently, I have a page that has a table that shows a varying level of information depending upon the logged in status of the requester. It is set up as a table with repeaters pulling from a data source.
Some examples of things that are different:
The Date of Birth column gives the exact DOB for those with a certain role, and the month of their birth for other users.
Last names are a hyperlink for those in a certain role, and just a regular literal for everyone else.
There is also an extra column in a table for those with a certain role.
Currently, I essentially have the entire page duplicated with minor tweaks inside the loginview control. This seems like a poor way to do business, but I'm not really sure of a cleaner way to do it with webforms. With MVC and Razor (which I am less experienced with), it seems like it would be fairly trivial to tweak the output with some conditionals in the view but it seems less intuitive in webforms.
Is there a better way to do this in webforms?
I've done similar implementations using a repeater, and have had success with the following approach:
Use the decorator pattern to add "display directives" to the object you'll bind to the repeater. In your example, I might add bool flags for ShowFullDob, ShowLastNameAsLink, and ShowColumnX (for your optional column).
When you retrieve your data to be displayed, convert the model objects to your new decorated model objects, and set the bool flags as appropriate for the current request.
This is the part that's a little counter-intuitive: in your repeater's ItemTemplate, render ALL the data, but use your bool flags to set the Visible attribute (see code sample below).
<ItemTemplate>
<td runat="server" Visible='<%# (bool)DataBinder.Eval(Container.DataItem, "ShowFullDob") %>'>construct full DOB here</td>
<td runat="server" Visible='<%# !((bool)DataBinder.Eval(Container.DataItem, "ShowFullDob")) %>'>construct just the birth month/year here</td>
</ItemTemplate>
Perhaps other SO'ers can suggest more elegant ways of implementing conditional elements within a repeater - if so, I'd love to hear them! However, even though it's a little ugly and heavy-handed, it gets the job done.