I have a webpage with filter selects at the top, a search button and a ListView with custom pager (witch is a Repeater witch contains LinkButtons with page numbers) at the bottom.
When I select filters and click the search button, I put the results in a List<> and DataBinding them at ListView. The function witch returns the results has two properties that are StartIndex and PageSize. So when I click a page I call again the function with new PageIndex. Sometimes the results need more columns from the ListView.
So I create two classes (ResultsLayoutTemplate.cs, ResultsItemTemplate.cs) to design the layout for ListView.
The problem is when I select a page (custom pager of ListView) whitch the results need more columns to ListView, the header of table does not change the columns and keeps the previous page header design. Lines have the right columns.
For example in first page ListView has 8 columns and in second page ListView must have 13 columns. But it keeps 8 columns for the header. The same happen when the third page need 8 columns it appears 13 from the previous page. The problem is only on the header.
Here's the code.
protected void ListView1_LayoutCreated(object sender, EventArgs e)
{
results = Session["results"] as List<Res>;
if (results.Count > 0)
{
Session["columns"] = results.Max(i => i.columns.Count);
}
ListView lv = sender as ListView;
ListView1.LayoutTemplate = new ResultsLayoutTemplate((int)Session["columns"]);
ListView1.ItemTemplate = new ResultsItemTemplate((int)Session["columns"]);
ListView1.AlternatingItemTemplate = new AlterResultsItemTemplate((int)Session["columns"]);
Control newlayoutContainer = new Control();
lv.LayoutTemplate = new ResultsLayoutTemplate((int)Session["columns"]);
lv.LayoutTemplate.InstantiateIn(newlayoutContainer);
var usercontrol = newlayoutContainer.Controls[0];
usercontrol.ID = "MyLayout";
lv.Controls.Add(newlayoutContainer);
}
protected void Page_PreRender(object sender, EventArgs e)
{
int num;
bool isNum = Int32.TryParse(Session["page"].ToString(), out num);
if ((Int32.Parse(Session["page"].ToString()) > 0) && (isNum))
{
results = Session["results"] as List<Res>;
Session["platos"] = results.Max(i => i.quadruplette.Count);
ListView1.Items.Clear();
ListView1.ItemTemplate = new ResultsItemTemplate((int)Session["columns"]);
ListView1.AlternatingItemTemplate = new AlterResultsItemTemplate((int)Session["columns"]);
ListView1.DataSource = results;
ListView1.DataBind();
}
}
protected void lbPage_Click(object sender, EventArgs e)
{
LinkButton btn = sender as LinkButton;
int num;
bool isNum = Int32.TryParse(btn.Text, out num);
if (isNum)
{
Session["page"] = btn.Text;
}
results = GetResults(Filters, (Int32.Parse(Session["page"].ToString()) - 1) * PageSize, PageSize);
Session["results"] = results;
Session["columns"] = results.Max(i => i.columns.Count);
}
}
not 100% sure i understand the question, but it sounds like you're trying to reinvent the wheel. Why not use the asp.net pagercontrol and just define the layout to fit what you need. Then use a sqldatasource control with a WHERE clause bound to your filter controls. Then bind that to the listview. Then have the sqldatasource rebind when you click the go button. any special operations that need to be performed before the bind actually happens can happen in the Selecting event.
Related
I have a asp:Repeater that has a DataSourceID to a custom collection.
The collection class has properties that I would like to display in the FooterTemplate. Because it calculate a value based on all the items.
In the FooterTemplate, is there a way to access the actual collection object? Maybe with Container or Eval.
I don't have direct access to the DataSource. I could change the code to have it as a parameter but would rather find an other way.
can you use something like this?
rpt.DataSource = mydatasource;
rpt.DataBind();
// label in footer
var lblDateTime = rpt.FindControl("lblDateTime") as Label;
if (lblDateTime != null)
{
lblDateTime.Text = mydatasource.First().DateChecked;
}
or like this
void R1_ItemDataBound(Object Sender, RepeaterItemEventArgs e)
{
// Execute the following logic for Footer only.
if (e.Item.ItemType == ListItemType.Footer) {
// put code here to get what you want and show it in the footer
}
}
Is there a way that I could access the e event arguments for a button that has not been clicked?
I need to delete multiple entries in a gridview by clicking a button and having it simulate clicking the delete button for each selected entry, but I can't use performClick, so I'm trying to call the actual method that deletes each one. However, that method requires an "e As System.Web.UI.WebControls.GridViewCommandEventArgs" parameter and I can't figure out how to get that.
You won't be able to access the EventArgs parameter.
I'd suggest you design your code like this:
public class MyClass
{
private ListView listView;
protected void OnClick(EventArgs e)
{
performAction();
}
private void performAction()
{
listView.deleteSelectedItems();
}
}
Don't implement functionality you are going to need somewhere else in delegates. Instead call this functionality inside the delegates' body. This way you can reuse performAction() somewhere else ..
Your problem calling delete button can be resolved if you add one check box in each row of datagrid and on click of button Delete you can perform delete operation for the checked rows in following manner
Protected void btnDelete_Click(object sender, EventArgs e)
{
for(int i = 0; i < GridView1.Rows.Count; i++)
{
CheckBox chkDelete = (CheckBox)GridView1.Rows[i].Cells[0].FindControl("chkSelect");
if(chkDelete != null)
{
if(chkDelete.Checked)
{
strID = GridView1.Rows[i].Cells[1].Text;
ids.Add(strID); //ids can colletion of any type
}
}
}
}
Now send ids to any function to perform delete.
I have a Menu control that I dynamically populate with categories. When a user clicks a category, the postback should populate a grid with products in that category. However, I seem to have tried every way possible, i.e. whatever page life cycle time, only to always get this error:
Failed to load viewstate. The control tree into which viewstate is being loaded must match the control tree that was used to save viewstate during the previous request.
Here is all my code:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
BuildCategoryMenu();
}
}
protected void categoryMenu_ItemClick(object source, DevExpress.Web.ASPxMenu.MenuItemEventArgs e)
{
var catId = new Guid(e.Item.Name);
ListProductsByCatId(catId);
}
private void BuildCategoryMenu()
{
var cats = _categoryService.ListActive();
categoryMenu.Items.Clear();
foreach (var cat in cats)
{
categoryMenu.Items.Add(new MenuItem { Text = cat.Name, Value = cat.id.ToString() });
}
}
private void ListProductsByCatId(Guid catId)
{
productGrid.DataSource = _productService.ListByCatId(new Guid("a5c2f0ef-a3cc-4af1-abac-37f1be6a5c74"));
productGrid.DataBind();
}
Here is my Menu:
<asp:Menu ID="categoryMenu" runat="server" EnableViewState="false">
</asp:Menu>
EnableViewState is only false because it didn't work with true either.
One answer to this is to avoid the postbacks and code URL's into the menu items. More RESTful, but probably making AJAXing the menu a bit harder.
I´m writing on a webpart for sharepoint, so I have to generate a Datagrid problematically.
The Situation is that I get a Dataview, generate the Gris and bind the Data.
One column should show a Image, so I have to generate a template column with item template.
So code looks like this:
//Instantiate the DataGrid, and set the DataSource
_grdResults = new DataGrid();
_grdResults.AutoGenerateColumns = false;
_grdResults.DataSource = view;
TemplateColumn colPic = new TemplateColumn();
colPic.HeaderText = "Image";
I found dozens of example for asp to create the item-template, but how construct one in code and bind it´s ImageUrl to "imgURL" of the Dataview?
thanks for any advice
Ren
You need to create a class that implements that ITemplate interface.
public class TemplateImplementation : ITemplate
{
public void InstantiateIn(Control container)
{
Image image = new Image();
image.DataBinding += Image_DataBinding;
container.Controls.Add(image);
}
void Image_DataBinding(object sender, EventArgs e)
{
Image image = (Image)sender;
object dataItem = DataBinder.GetDataItem(image.NamingContainer);
// If the url is a property of the data item, you can use this syntax
//image.ImageUrl = (string)DataBinder.Eval(dataItem, "ThePropertyName");
// If the url is the data item then you can use this syntax
image.ImageUrl = (string)dataItem;
}
}
You then set your ItemTemplate to an instance of this class.
colPic.ItemTemplate = new TemplateImplementation();
I did follow the article TRULLY Understanding ViewState (great article btw) and populating my drop down list is working great. I've even setup a OnSelectedIndexChange event which fires almost as great.
The problem I've found is the SelectedIndexChanged event won't fire when selecting the 0th index. It does all other times however.
Here's some code:
<asp:DropDownList runat="server" ID="DropDownList1" EnableViewState="false"
AutoPostBack="True" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" />
protected override void OnInit(EventArgs e)
{
this.DropDownList1.DataTextField = "Text";
this.DropDownList1.DataValueField = "Value";
this.DropDownList1.DataSource = fillQueueDropDown();
this.DropDownList1.DataBind();
base.OnInit(e);
}
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
OnSelectedQueueChanged(e);
}
public void OnSelectedQueueChanged(EventArgs e)
{
// Do stuff.
}
public event EventHandler queueNamesChangedEvent;
public void OnSelectedQueueChanged(EventArgs e)
{
if (queueNamesChangedEvent != null)
queueNamesChangedEvent(this, e);
}
I suppose I can do some type of check in the Page_Load method:
if(ViewState["selectedIndexChangedFlag"] != 1)
// raise OnSelectedChange event
Or is there something I can setup in the OnInit() method where I'm rebinding this data everytime that i can do?
See, my custom EventHander raises an event which is caught by a the parent page in which this control resides, so that the parent could take some action using the newly selected value. And this is currently working for all cases where the selected index > 0.
I create a property in this control which contains the most recently selected index, in which case my parent page can action on this property value on every Page_Load... dunno.
Open to suggestions. Or how to force this SelectedIndexChanged event to fire for that 0th index selection.
The problem is that you are loading the data each time and this is resetting the selected index. Imagine this is your dropdown:
zero [selected]
one
two
Then in the client you change the selected index:
zero
one [selected]
two
This populates the hidden input __EVENTARGUMENT with your new index (1) and the hidden input __EVENTTARGET with the id of your dropdown. Now the server-side code kicks in and reloads your data:
zero [selected]
one
two
"zero" is the selected value because that is the default when the data is loaded. Then ASP.NET looks for __EVENTTARGET and __EVENTARGUMENT in the Request and finds your dropdown's id and finds the new index (1). Now your dropdown looks like this:
zero
one [selected]
two
Since the index has changed, the dropdown raises its SelectedIndexChanged event indicating that the index has changed. Obviously this is the part that is working, now lets see why selecting the first item in the list does not raise the event.
Now lets say that we still have the dropdown in the state it was just in (with "one" being selected and the selected index of 1). What happens when we select the first item in the list on the client?
__EVENTTARGET and __EVENTARGUMENT are populated with the id of the dropdown and the new index (0). Then the server loads the data into the dropdown and the dropdown now looks like this again:
zero [selected]
one
two
Notice that since you reloaded the data before the events fired the index is already set to 0 because that is the default. Now when your event fires and the dropdown's selected index is set to 0, the dropdown does not see this as a change since the selected index (as far as it knows) has not changed.
Here is how to fix the problem:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!Page.IsPostBack)
{
this.DropDownList1.DataTextField = "Text";
this.DropDownList1.DataValueField = "Value";
this.DropDownList1.DataSource = fillQueueDropDown();
this.DropDownList1.DataBind();
}
}
What this will do is only load the data into the dropdown if the page is not a postback. This means that ViewState will maintain the data for you as well as the selected index so that when you post back the dropdown will compare the new index to the index you saw in the client.
My goal with disabling the ViewState on this drop down list is to minimize the size of the ViewState for the page.
The problem I had with only doing the if(!Page.IsPostBack){...DataBind()...}, is that when you select an item for the first time, and the page reloads, my drop down list becomes empty.
What I ended up doing was creating another Property on this control, LastIndex. When the OnSelectedIndexChanged event fires, I update the LastIndex value. In the Page_Load, I compare the Current and Last index values, if they're different, then fire a Index changed event.
public int SelectedValue{
get { return this.DropDownList1.SelectedItem.Value; }
}
public int LastIndex{
get { return this.ViewState["lastIndex"] == null ? -1 : (int)this.ViewState["lastIndex"]; }
set { this.ViewState["lastIndex"] = value; }
}
protected override void OnInit(EventArgs e){
base.OnInit(e);
this.DropDownList1.DataTextField = "Text";
this.DropDownList1.DataValueField = "Value";
this.DropDownList1.DataSource = fillQueueDropDown();
this.DropDownList1.DataBind();
}
protected void Page_Load(object sender, EventArgs e){
if (this.LastIndex != this.SelectedValue)
this.OnSelectedQueueChanged(new EventArgs());
}
private ListItemCollection fillQueueDropDown(){...}
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e){
OnSelectedQueueChanged(e);
this.LastIndex = this.SelectedValue;
}
public event EventHandler queueNamesChangedEvent;
public void OnSelectedQueueChanged(EventArgs e){
if (queueNamesChangedEvent != null)
queueNamesChangedEvent(this, e);
}
You are right though. The data is re-loaded and re-bound in the OnInit phase. Then the ViewState is restored (and when the 0th index is restored), when we finally get to the Events phase, the control doesn't detect the change.
Not sure this is the most elegant route, but it's working good so far.
Then i found this in the msdn docs for IPostBackDataHandler:
public virtual bool LoadPostData(string postDataKey,
NameValueCollection postCollection) {
String presentValue = Text;
String postedValue = postCollection[postDataKey];
if (presentValue == null || !presentValue.Equals(postedValue)) {
Text = postedValue;
return true;
}
return false;
}
Since the present value is the same as the changed-to value, the event isn't fired.