Virtual Listview for ASP.net? - asp.net

Is there a virtual listview for ASP.net?
Most tables (and grids, listview, data tables, data grids, grid views, list grids) i find for asp.net expect the user to page through data.
i want a listview that contains, for example, 10,000 items; and i don't want 10 pages.
The problem of a long list was solved in 1994 using a listview in "virtual" mode. The control need only be given the number of items to show. The control information about those items as required (i.e. as the user scrolls them into view, or tries to sort on a column).
Has anyone created a virtual listview (presumably using Asynchronous Javascript and XML, or Synchronous Javascript and XML)?
If the answer's "no": don't be afraid to answer the question. There's nothing wrong with an answer of:
No.

I just make one virtual ListView sample.
I start with a repeater that I render divs, with an attribute that contain the Data Base id that need to be loaded.
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<div data-id="<%# GetID(Container.DataItem) %>" class="DataLine">
loading...
</div>
</ItemTemplate>
</asp:Repeater>
Next the javascript that check if this div is visible, and get the data using ajax.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script>
function isScrolledIntoView(elem)
{
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = elem.offset().top;
var elemBottom = elemTop + elem.height();
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
var cTimerID = null;
function RunWithSomeDelay()
{
if(cTimerID)
clearTimeout(cTimerID);
cTimerID = setTimeout(CheckWhatToShow, 1000);
}
function CheckWhatToShow()
{
$(".DataLine").each(function(i, selected) {
var ThisOne = $(selected);
if(ThisOne.attr("Done") != "1")
{
if(isScrolledIntoView(ThisOne))
{
ThisOne.attr("Done", "1");
// here you can use a simple ajax load, to load your data.
ThisOne.text(ThisOne.attr("data-id"));
}
}
});
}
$(document).ready(function() {
// first time run
CheckWhatToShow();
// run on scrool
$(window).scroll(RunWithSomeDelay);
});
</script>
and here is my code behind as test that one
public partial class Dokimes_StackOverFlow_VirtualList : System.Web.UI.Page
{
List<int> oMainIds = new List<int>();
protected void Page_Load(object sender, EventArgs e)
{
for (int i = 0; i < 3000; i++)
oMainIds.Add(i);
Repeater1.DataSource = oMainIds;
Repeater1.DataBind();
}
public int GetID(object oItem){
return (int)oItem;
}
}
I tested and its working just find.
and here is the source code: http://www.planethost.gr/VirtualList.rar
Improvements that can be done:
To optimize what div to search for visibility base on the scroll point.
To load a group of data and place them on divs
Update
I make some speed optimizing, and add ajax call test. For this optimizations work correct the height of the div that contains the data must be the same across the page. Left to load a group of data, get them as json and place them on the correct place.
http://www.planethost.gr/VirtualList2.rar

Try to look at infinite scroll jQuery plugin. I think that is it like what are you looking for.

Related

Jquery chosen multiple select, How to get selected values server side in asp.net?

I am revitalizing a very old application and trying not to introduce Devexpress or Telerik into this application.
I have a need for some dropdownlists with multiple selection availability. I have poked around on the web and the chosen jquery plugin looks to be the route to go.
I have it implemented in one of my test pages, but I am trying to get this implemented rather quickly without much tooling around with it. I am having some difficulties grabbing the multiple selected values on the server side in my code behind. I don't really want to have a bunch of client side functionality holding and maintaining data on change etc.
Any one ever attempt to get at this data server side versus client side and have luck?
Code example. :
<select id="slcExample" multiple class="chosen-select" style="width:350px;" runat="server"></select>
<script type="text/javascript">
$(document).ready(function () {
var config = {
'.chosen-select': {},
'.chosen-select-deselect': { allow_single_deselect: true },
'.chosen-select-no-single': { disable_search_threshold: 10 },
'.chosen-select-no-results': { no_results_text: 'Oops, nothing found!' },
'.chosen-select-width': { width: "95%" }
}
for (var selector in config) {
$(selector).chosen(config[selector]);
}
});
</script>
I have found that if I could get at this property .SelectedIndices that I would have access to the selected values but it will not let me use this on the server side as it is a protected property of the select in asp.net.
Here is what I ended up doing. :
Dim index As Integer
Dim valuesChosen As String = ""
For index = 0 To (slcExample.Items.Count - 1)
If (slcExample.Items(index).Selected) Then
valuesChosen += slcExample.Items(index).Value.Trim + ";"
End If
Next
I needed something on the server side. Hopefully this helps someone else. If you have a better option I am open to seeing it and will mark as answer if better.
You can create a hidden field with asp:net class. with a javascript method, add all value in a comma separated list.
You can have the list on the server side after submiting your form.
try putting clientIDMode="Static"
<asp:HiddenField runat="server" ID="hidTest" ClientIDMode="Static" />
but if you can't, you will have to see the generated name from asp to update it in your javascript method
<script type="text/javascript">
$(document).ready(function () {
var config = {
'.chosen-select': {},
'.chosen-select-deselect': { allow_single_deselect: true },
'.chosen-select-no-single': { disable_search_threshold: 10 },
'.chosen-select-no-results': { no_results_text: 'Oops, nothing found!' },
'.chosen-select-width': { width: "95%" }
}
var hiddenSeparatedList = "";
for (var selector in config) {
hiddenSeparatedList += $(selector).chosen(config[selector]) + ','
$('#hidTest').val(hiddenSeparatedList);
}
});
</script>
I had the same problem and switched to this JQuery plugin instead: http://www.erichynds.com/blog/jquery-ui-multiselect-widget
Since the asp.net dropdown control does not allow multiple items, I used a regular tag with runat server and an ID. The plugin actually selects the items and then you can read them from code behind.
The plugin will works client-side on the asp.net dropdown, but you can't get the selected items in the code behind. So depending on your needs .. .
Hope this helps!
Bonnie
I used a List instead:
int i;
IList<string> chosenItems = new List<string>();
for (i = 0; i <= selectExample.Items.Count - 1; i++)
{
if (selectExample.Items[index].Selected)
{
chosenItems.Add(selectExample.Items[index].Value.Trim());
}
}

How to dynamically and incrementally add controls to a container

I thought this was straight forward, but i have a link button, and I do this in the click event:
myContainer.Controls.Add( new FileUpload());
I expect 1 new file control upload to be spawned in the container for each click, however, I always get 1. What do I need to do to have multiple file upload controls?
Since the control was added dynamically, it does not survive the postback. This means that you have to add the file upload (or any other dynamically added control) again in the preinit event to be able to have its values populated and to use it later on in the page life cycle.
Sounds like you are trying to be able to upload multiple files. You may want to add the file uploads with jQuery and use the Request.Files property to get them on the back-end.
I agree with Becuzz's answer. Try this, there are better ways to do it, but this might help as well:
Add this to the "Load" event of your page
if (!IsPostBack) {
Session["UploadControls"] = null;
}
if (Session["UploadControls"] != null) {
if (((List<Control>)Session["UploadControls"]).Count > 0) {
foreach ( ctrl in (List<Control>)Session["UploadControls"]) {
files.Controls.Add(ctrl);
}
}
}
And also add this to the PreInit portion of your page:
string ButtonID = Request.Form("__EVENTTARGET");
if (ButtonID == "Button1") {
FileUpload NewlyAdded = new FileUpload();
List<Control> allControls = new List<Control>();
if (Session["UploadControls"] != null) {
if (((List<Control>)Session["UploadControls"]).Count > 0) {
foreach ( ctrl in (List<Control>)Session["UploadControls"]) {
allControls.Add(ctrl);
//Add existing controls
}
}
}
if (!allControls.Contains(NewlyAdded)) {
allControls.Add(NewlyAdded);
}
Session["UploadControls"] = allControls;
}
And add this to your HTML. This can be anything of course:
<div id="files" runat="server">
</div>
I use the "__EVENTTARGET" value to know what caused the postback, so that you don't get unwanted Upload controls.
Good luck, and hopefully this helps.
Hanlet

ASP.NET - How can I add a new javascript from a user control loaded by callback?

I'm doing a menu that loads levels dynamicly, when you click on a item the next level is loaded asynchronously. For each menu item I have a user control. Every user control is declared in its parent, for example, the "secondlevelcontrol" has the reference to "thirdlevelcontrol".
With this level of nesting, I want to manage the asynchronous calls on every user control so, when the first level is loaded the javascript to load the second is loaded too. When the second level is loaded the javascript to load the third is loaded too.
To do asynchronous calls I'm implementing ICallbackEventHandler interface. As you can see in the examples, controls are added to the page as plain html. The method "ProcessOnLoadEvent" executes all lines of the "OnLoad" event of the user control.
An example of the implementation is this for the user control of fourth level:
public string GetCallbackResult()
{
return _callbackRendering;
}
public void RaiseCallbackEvent(string itemId)
{
var id = Int32.Parse(itemId);
var menu = new LateralMenu();
var currentChildren = menu.GetNodesById(id, 1);
var ctrl = this.Page.LoadControl(USER_CONTROL_FIVE_LEVEL_RELATIVE_PATH) as LeftSideFifthLevel;
ctrl.Items = currentChildren.Children;
ctrl.ProcessOnLoadEvent();
_callbackRendering = ctrl.GetHtml();
}
And this is the code for the fifth level user control:
public void ProcessOnLoadEvent()
{
EnsureChildControls();
if (null != RepeaterMenu)
{
SettingCallbackReference();
Visible = null != Items && 0 < Items.Count;
if (null != Items && 0 < Items.Count)
{
RepeaterMenu.DataSource = Items;
RepeaterMenu.DataBind();
}
}
}
public void RaiseCallbackEvent(string itemId)
{
var id = Int32.Parse(itemId);
var menu = new LateralMenu();
var currentChildren = menu.GetNodesById(id, 1);
var ctrl = this.Page.LoadControl(USER_CONTROL_SIX_LEVEL_RELATIVE_PATH) as LeftSideSixthLevel;
ctrl.Items = currentChildren.Children;
ctrl.ProcessOnLoadEvent();
_callbackRendering = ctrl.GetHtml();
}
public void SettingCallbackReference()
{
var cm = this.Page.ClientScript;
var cbRef = cm.GetCallbackEventReference(this, "itemId", "AnchorLevel5_OnClick_Callback", "ctx");
var cbScript = "function AnchorLevel5_OnClick(itemId, ctx){ new Menu().empty(ctx); " + cbRef + "; }";
cbScript += "function AnchorLevel5_OnClick_Callback(htmlText, ctx){ new Menu().render(htmlText, ctx); }";
cm.RegisterClientScriptBlock(this.GetType(), "CallServer", cbScript, true);
}
My problem is that levels beyond second level never work because the javascript associated with the user control ("SettingCallbackReference" method) has no html to put on the page.
Is there any way to create some user controls created dynamicly that implements ICallbackEventHandler interface that add new user controls to the page? Or, Am I doing something wrong and this is not the right way to implement this behaviour?
Thanks!!!
I suspect that you are probably going about this the wrong way. Take a look at the answer to this question: How to lazy load Infragistics UltraWebTree control?
This question was specifically about lazy-loading a tree view, but the same principles apply for lazy loading menu items. Follow these steps:
On first page load, render the top level menu
Include a function on the page that makes an ajax call to the server with the
parent id and retuns the next level items for that parent (getNodes in my example)
Bind this function to the click event of the top level menu items (that have sub items)
In the success handler of the ajax call, inject the returned menu items below
the parent and bind the same function to the click event of these items only.
On callback, be careful not to bind the function to the click event of ALL menu items, because then you will end up getting the function bound multiple times to the top level items and called multiple times. Just bind to the returned items.
Also, you need some way of determining that an item's sub items have already been loaded. That is what the following line in my example was for, but you might need something slightly different:
if (jQuery(nodesDiv).text() == 'Loading...') {
I used jQuery because it is the most concise, but you culd do this in pure js - I wouldn't recommend it.

Display jquery dialog on postback in ASP.NET after saving a new record

What I would like to do is have the user add a new record to the database and popup a JQuery dialog confirming that the new record was saved. I thought this would be a simple exercise. I have a gridview bound to a LINQDataSource to allow the user to view and edit existing records and a textbox and a button to add new codes.
In the head of the document, I have the following:
$('#dialog').dialog({
autoOpen: false,
width: 400,
buttons: {
"Ok": function () {
$(this).dialog("close");
}
}
});
and futher down in the markup I have:
<div id="dialog" title="New Code Added">
<p>"<asp:Literal runat="server" ID="LiteralNewCode"></asp:Literal>" was successfully added.</p>
</div>
So when the user enters a new description and it passes all the validation, it's added to the database and the gridview is rebound to display the new record.
protected void ButtonSave_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
CCRCode.Add( <long list of paramters> );
GridCode.DataBind();
IsNewCode = true;
NewDescription = <new description saved to database>;
}
}
Now, here's where (I thought) I'd set a boolean property to indicate that a new description had been added as well as the text of the new description. See below:
protected bool IsNewCode
{
get { return ViewState["IsNewCode"] != null ? (bool)ViewState["IsNewCode"] : false; }
set { ViewState["IsNewCode"] = value; }
}
private string NewDescription
{
get { return ViewState["NewDescription"] != null ? ViewState["NewDescription"].ToString() : string.Empty; }
set { ViewState["NewDescription"] = value; }
}
Here's where I loose my way. My guess is I want to add functionality to include code similar to:
$('#dialog').dialog('open');
I've added a registerscriptblock method in the page_load event but that didn't work. Any ideas? Or am I just going about this entirely wrong?
Thanks.
Not really get what you want to do. But, i use jquery alot with .NET in my projects. here is how i do, probably could give you a hint.
foo.aspx.cs
public String ScriptToRun = "$('#dialog').dialog('open');";
change the value of ScriptToRun in your C# code
foo.aspx
$(document).ready(function() {<%=ScriptToRun %>});
Remember that whatever you done in backend is going to generate HTML, Css& javascript to browser.
Two ways: one, write the javascript in your server-side code. Or, define a JS method to show the dialog (say named showDialog), and call it via:
Page.ClientScript.RegisterStartupScript(... "showDialog();" ..);
RegisterStartupScript puts the method call at the end, ensure your script is above it to work. You can also wrap it with document.ready call too, to ensure JQuery is properly loaded.
I think that the only think that you have miss is the creation of the dialog when the Dom is ready.
$(document).ready(function() {$('#dialog').dialog('open');});
I posted code in a different question for a custom "MessageBox" class I wrote:
ASP.NET Jquery C# MessageBox.Show dialog uh...issue
the code by default uses the javascript alert() function, but you can define your callback so that it calls your custom javascript method to display the messages.

Best practice for modal window in Web Forms application

On a list page, clicking on one of the items brings up the details in a modal popup window which will have its own functionality (like validation, updating etc). What's the best practice to implement this (not looking for a hack). I see two options here:
Hide the details markup until a list item is clicked at which time, do a ajax request to get the details and populate and show the details section.
Have the details section as a separate page by itself. On a list item click, show this page in a modal window (is this even possible?) This is similar to the IFrame approach and sounds like an old school approach.
What are the pros of cons of these approaches or are there other ways of doing this? There should not be a postback on list item click.
Edit: Any more opinions are appreciated.
I'm doing option 1 currently, it's very lightweight and all you need is an ajax post (jQuery or UpdatePanel) and some modal (I'm using jQery UI). It's easier than a full page post, plus you have the added benefit of being able to manipulate the page you're in as part of the result.
For example I have grids in the page, the editor is modal, usually with more detail, when you hit save, the grid is updated. I've put this in a generic template solution and it's very easy to work with, and is as light as webforms can be in that situation, so I'm all in favor of option 1.
Here's an example approach, having your modal control inherit from UpdatePanel (code condensed for brevity):
public class Modal : UpdatePanel
{
private bool? _show;
public string Title
{
get { return ViewState.Get("Title", string.Empty); }
set { ViewState.Set("Title", value); }
}
public string SaveButtonText
{
get { return ViewState.Get("SaveButtonText", "Save"); }
set { ViewState.Set("SaveButtonText", value); }
}
protected override void OnPreRender(EventArgs e)
{
if (_show.HasValue) RegScript(_show.Value);
base.OnPreRender(e);
}
public new Modal Update() { base.Update();return this;}
public Modal Show() { _show = true; return this; }
public Modal Hide() { _show = false; return this; }
private void RegScript(bool show)
{
const string scriptShow = "$(function() {{ modal('{0}','{1}','{2}'); }});";
ScriptManager.RegisterStartupScript(this, typeof (Modal),
ClientID + (show ? "s" : "h"),
string.Format(scriptShow, ClientID, Title, SaveButtonText), true);
}
}
In javascript:
function modal(id, mTitle, saveText) {
var btns = {};
btns[saveText || "Save"] = function() {
$(this).find("input[id$=MySaveButton]").click();
};
btns.Close = function() {
$(this).dialog("close");
};
return $("#" + id).dialog('destroy').dialog({
title: mTitle,
modal: true,
width: (width || '650') + 'px',
resizable: false,
buttons: btns
}).parent().appendTo($("form:first"));
}
Then in your markup (Can't think of a better name than MyControls right now, sorry!):
<MyControls:Modal ID="MyPanel" runat="server" UpdateMode="Conditional" Title="Details">
//Any Controls here, ListView, whatever
<asp:Button ID="MySaveButton" runat="server" OnClick="DoSomething" />
</MyControls:Modal>
In you pages codebehind you can do:
MyPanel.Update().Show();
Fr your approach, I'd have a jQuery action that sets an input field and clicks a button in the modal, triggering the update panel to postback, and in that code that loads the details into whatever control is in the modal, just call MyPanel.Update.Show(); and it'll be on the screen when the update panel ajax request comes back.
The example above, using jQuery UI will have 2 buttons on the modal, one to close and do nothing, one to save, clicking that MySaveButton inside the modal, and you can do whatever you want on then server, calling MyPanel.Hide() if successful, or put an error message in the panel if validation fails, just don't call MyModal.Hide() and it'll stay up for the user after the postback.

Resources