I want to store a generic list in the viewstate in an ASP.NET user control, so I have the following code:
protected List<MoodleCourse> MoodleCoursesCreated
{
get
{
if (ViewState["MoodleCoursesCreated"] == null)
{
return new List<MoodleCourse>();
}
else
{
return (List<MoodleCourse>)ViewState["MoodleCoursesCreated"];
}
}
set
{
ViewState["MoodleCoursesCreated"] = value;
}
}
So to add an item to the list, I call MoodleCoursesCreated.Add(new MoodleCourse);
However it seems then I must do MoodleCourseCreated = MoodleCourseCreated to activate the setter so the list actually gets stored in the viewstate. I suspect there is a more elegant way to achieve this, does anyone have any suggestions? Cheers
Calling .Add() you never trigger the setter. I guess, you don't need setter at all. Instead, override Add() method or create your own AddMoodleCourse(MoodleCourse moodleCourse):
protected void AddMoodleCourse(MoodleCourse moodleCourse)
{
var courses = ViewState["MoodleCoursesCreated"] as List<MoodleCourse>;
if (courses == null)
{
courses = new List<MoodleCourse>();
ViewState["MoodleCoursesCreated"] = courses;
}
courses.Add(moodleCourse);
}
and now call MoodleCoursesCreated.AddMoodleCourse(new MoodleCourse);
protected List<MoodleCourse> MoodleCoursesCreated
{
get
{
if (ViewState["MoodleCoursesCreated"] == null)
{
ViewState["MoodleCoursesCreated"] = new List<MoodleCourse>();
}
return (List<MoodleCourse>)ViewState["MoodleCoursesCreated"];
}
set
{
ViewState["MoodleCoursesCreated"] = value;
}
}
Don't use a property if you want it to have side-effects in the getter. Instead just use a method like this:
protected List<MoodleCourse> GetMoodleCourses()
{
List<MoodleCourse> list = (List<MoodleCourse>)ViewState["MoodleCoursesCreated"];
if (list == null)
{
list = new List<MoodleCourse>();
ViewState["MoodleCoursesCreated"] = list;
}
return list;
}
you should hold the same reference in the getter:
protected List<MoodleCourse> MoodleCoursesCreated
{
get
{
if (ViewState["MoodleCoursesCreated"] == null)
ViewState["MoodleCoursesCreated"] = new List<MoodleCourse>();
return ViewState["MoodleCoursesCreated"] as List<MoodleCourse>;
}
set
{
ViewState["MoodleCoursesCreated"] = value;
}
}
Related
I have create two Combobox column in gridview. Now i want to fill second combobox depending on value of first combobox (On first combobox selectedValueChanged event). Please reply.
General approach may look like this:
private MyType1 _selectedItem1;
public MyType1 SelectedItem1
{
get { return _selectedItem1; }
set
{
if (_selectedItem1 == value) return;
_selectedItem1 = value;
//replace with string implementation, if needed
OnPropertyChanged(() => SelectedItem1);
if (_selectedItem1 == ...)
{
ItemsSource2 = new List<MyClass2> { ... };
}
else if (_selectedItem1 == ...)
{
...
}
}
}
private IList<MyType2> _itemsSource2;
public IList<MyType2> ItemsSource2
{
get { return _itemsSource2; }
set
{
if (_itemsSource2 == value) return;
_itemsSource2 = value;
OnPropertyChanged(() => ItemsSource2);
}
}
I am doing a program like messenger that has all the contacts in a listbox with the relative states of the contacts.
Cyclic I get a xml with the contacts were updated over time, then updates the states within a class of binding called "Contacts".
The class Contacts has a filter to display only certain contacts by their state, "online, away, busy,.. " but not offline, for example ....
Some code:
public class Contacts : ObservableCollection<ContactData>
{
private ContactData.States _state = ContactData.States.Online | ContactData.States.Busy;
public ContactData.States Filter { get { return _state; } set { _state = value; } }
public IEnumerable<ContactData> FilteredItems
{
get { return Items.Where(o => o.State == _state || (_state & o.State) == o.State).ToArray(); }
}
public Contacts()
{
XDocument doc = XDocument.Load("http://localhost/contact/xml/contactlist.php");
foreach (ContactData data in ContactData.ParseXML(doc)) Add(data);
}
}
Update part:
void StatusUpdater(object sender, EventArgs e)
{
ContactData[] contacts = ((Contacts)contactList.Resources["Contacts"]).ToArray<ContactData>();
XDocument doc = XDocument.Load("http://localhost/contact/xml/status.php");
foreach (XElement node in doc.Descendants("update"))
{
var item = contacts.Where(i => i.UserID.ToString() == node.Element("uid").Value);
ContactData[] its = item.ToArray();
if (its.Length > 0) its[0].Data["state"] = node.Element("state").Value;
}
contactList.ListBox.ItemsSource = ((Contacts)contactList.Resources["Contacts"]).FilteredItems;
}
My problem is that when ItemsSource reassigns the value of the listbox, the program lag for a few seconds, until it has finished updating contacts UI (currently 250 simulated).
How can I avoid this annoying problem?
Edit:
I tried with Thread and after with BackgroundWorker but nothing is changed...
When i call Dispatcher.Invoke lag happen.
Class ContactData
public class ContactData : INotifyPropertyChanged
{
public enum States { Offline = 1, Online = 2, Away = 4, Busy = 8 }
public event PropertyChangedEventHandler PropertyChanged;
public int UserID
{
get { return int.Parse(Data["uid"]); }
set { Data["uid"] = value.ToString(); NotifyPropertyChanged("UserID"); }
}
public States State
{
get { return (States)Enum.Parse(typeof(States), Data["state"]); }
//set { Data["state"] = value.ToString(); NotifyPropertyChanged("State"); }
//correct way to update, i forgot to notify changes of "ColorState" and "BrushState"
set
{
Data["state"] = value.ToString();
NotifyPropertyChanged("State");
NotifyPropertyChanged("ColorState");
NotifyPropertyChanged("BrushState");
}
}
public Dictionary<string, string> Data { get; set; }
public void Set(string name, string value)
{
if (Data.Keys.Contains(name)) Data[name] = value;
else Data.Add(name, value);
NotifyPropertyChanged("Data");
}
public Color ColorState { get { return UserStateToColorState(State); } }
public Brush BrushState { get { return new SolidColorBrush(ColorState); } }
public string FullName { get { return Data["name"] + ' ' + Data["surname"]; } }
public ContactData() {}
public override string ToString()
{
try { return FullName; }
catch (Exception e) { Console.WriteLine(e.Message); return base.ToString(); }
}
Color UserStateToColorState(States state)
{
switch (state)
{
case States.Online: return Colors.LightGreen;
case States.Away: return Colors.Orange;
case States.Busy: return Colors.Red;
case States.Offline: default: return Colors.Gray;
}
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public static ContactData[] ParseXML(XDocument xmlDocument)
{
var result = from entry in xmlDocument.Descendants("contact")
select new ContactData { Data = entry.Elements().ToDictionary(e => e.Name.ToString(), e => e.Value) };
return result.ToArray<ContactData>();
}
}
I developed a similar software: a huge contact list with data (presence and other stuff) updating quite frequently.
The solution I used is different: instead of updating the whole itemssource everytime, that is quite expensive, implement a ViewModel class for each contact. The ViewModel class should implement INotifiyPropertyChanged.
At this point when you parse the XML, you update the ContactViewModel properties and this will trigger the correct NotifyPropertyChanged events that will update the correct piece of UI.
It might be expensive if you update a lot of properties for a lot of contacts at the same time, for that you can implement some kind of caching like:
contactViewModel.BeginUpdate()
contactViewModel.Presence = Presence.Available;
..... other updates
contactViewModel.EndUpdate(); // at this point trigger PropertyCHanged events.
Another point:
keep a separate ObservableCollection bound to the ListBox and never change the itemssource property: you risk losing the current selection, scrollposition, etc.
dynamically add/remove elements from the collection bound to the listbox.
Buon divertimento e in bocca al lupo :-)
Move the downloading and parsing of the contact status information to another thread.
The line where you assigning the ItemsSource I would put in another thread, but remember about invoking or you're gonna have irritating errors.
I currently have the following code:
public MyObject SessionStore
{
get
{
if (Session["MyData"] == null)
Session["MyData"] = new MyObject();
return (MyData) Session["MyData"];
}
set
{
Session["MyData"] = (MyObject) value;
}
}
I access it using SessionStore.ThePropertyIWant
I set it using SessionStore = SessionStore
This works; but is there a better way of accomplishing the same thing?
You don't need to cast in the setter, and can make the getter more concise :
public MyData SessionStore
{
get { return (MyData)(Session["MyData"]) ?? new MyData(); }
set { Session["MyData"] = value; }
}
SessionStore is fine but you could end up with alot of properties. I tend to add protected properties to base bases and access from there.
eg:
/// <summary>
/// Gets or Sets the Current Order Line
/// </summary>
protected OrderLine CurrentOrderLine
{
get
{
if (Session["CurrentOrderLine"] == null)
{
Session["CurrentOrderLine"] = new OrderLine(this.CurrentOrder);
}
return Session["CurrentOrderLine"] as OrderLine;
}
set
{
Session["CurrentOrderLine"] = value;
}
}
then it would appear as a property on your page if you inheriet from it.
I have a class which will act as variables to store data from textboxes:
public class Business
{
Int64 _businessID = new Int64();
int _businessNo = new int();
string _businessName;
string _businessDescription;
public Int64 BusinessID
{
get { return Convert.ToInt64(_businessID.ToString()); }
}
public int BusinessNo
{
get { return _businessNo; }
set { _businessNo = value; }
}
public string BusinessName
{
get { return _businessName; }
set { _businessName = value; }
}
public string BusinessDescription
{
get { return _businessDescription; }
set { _businessDescription = value; }
}
I then have the code to store the data from the textbox into a session and into a list (there can be many businesses uploaded to the database at one time) - database irrelevent for now. I then want to display the list of businesses stored into the session onto the gridview: (b = class business)
List<Business> businessCollection = new List<Business>();
protected List<Business> GetBusinesses()
{
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
if (Session["Business"] != null)
businessCollection = (List<Business>)Session["Business"];
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
businessCollection.Add(b);
GridView1.DataSource = GetBusiness();
GridView1.DataBind();
}
It doesn't seem to add the list to the gridview, can someone help?
Debug your code and ensure that if (Session["Business"] != null) actually evaluates to true.
If it is false then you are adding to a list that is never returned from GetBusinesss
Without any more information you can rewrite it like this:
List<Business> businessCollection = new List<Business>();
protected List<Business> GetBusinesses()
{
if (Session["Business"] == null)
return businessCollection;
else
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
var currentCollection = GetBusinesses();
currentCollection.Add(b);
GridView1.DataSource = currentCollection;
GridView1.DataBind();
}
I personally wouldn't do it like this, as it seems like you need an assignment to Session["Business"] but I don't want to change the logic of your code.
Update
I wanted to update this with what I think you wanted to accomplish.
protected List<Business> GetBusinesses()
{
if (Session["Business"] == null)
Session["Business"] = new List<Business>();
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
var currentCollection = GetBusinesses();
currentCollection.Add(b);
GridView1.DataSource = currentCollection;
GridView1.DataBind();
}
It seems you are not assigning anything to Session["Business"]
There's a very strong chance that you're problem is caused by the fact that you are referencing the Business List object inconsistently. You've created an accessor for this object, so use it everywhere.
This:
if (Session["Business"] != null)
businessCollection = (List<Business>)Session["Business"];
Should be:
var businessCollection = GetBusiness();
Note the use of var: I suspect defining businessCollection as a member variable is part of the problem. In any case it is bad design if your intent is to store the list in the session. So I would also remove the member declaration for businessCollection and stick with a locally scoped variable.
I am trying to do efficient paging with a gridview without using a datasource control. By efficient, I mean I only retrieve the records that I intend to show.
I am trying to use the PagerTemplate to build my pager functionality.
In short, the problem is that if I bind only the records that I intend to show on the current page, the gridview doesn't render its pager template, so I don't get the paging controls.
It's almost as if I MUST bind more records than I intend to show on a given page, which is not something I want to do.
You need to create a custom gridview control that inherits from GridView. Without the DataSourceControl, the gridview does not have knowledge of the total number of records that could potentially be bound to the control. If you bind 10 out of 100 records and you set the PageSize property to 10, the gridview only knows that there are 10 records which will be less than or equal to the PageSize and the pager control will not display. In order for your gridview to show the pager, it has to know the total number of records that could potentially be retrieved. By inheriting the gridview and overriding the InitializePager method, we can intercept the pagedDataSource and modify the AllowCustomPaging and VirtualCount methods.
This is the one I created
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace cly.Web.CustomControls
{
public class clyGridView : GridView
{
private const string _virtualCountItem = "bg_vitemCount";
private const string _sortColumn = "bg_sortColumn";
private const string _sortDirection = "bg_sortDirection";
private const string _currentPageIndex = "bg_pageIndex";
public clyGridView ()
: base()
{
}
#region Custom Properties
[Browsable(true), Category("NewDynamic")]
[Description("Set the virtual item count for this grid")]
public int VirtualItemCount
{
get
{
if (ViewState[_virtualCountItem] == null)
ViewState[_virtualCountItem] = -1;
return Convert.ToInt32(ViewState[_virtualCountItem]);
}
set
{
ViewState[_virtualCountItem] = value;
}
}
public string GridViewSortColumn
{
get
{
if (ViewState[_sortColumn] == null)
ViewState[_sortColumn] = string.Empty;
return ViewState[_sortColumn].ToString();
}
set
{
if (ViewState[_sortColumn] == null || !ViewState[_sortColumn].Equals(value))
GridViewSortDirection = SortDirection.Ascending;
ViewState[_sortColumn] = value;
}
}
public SortDirection GridViewSortDirection
{
get
{
if (ViewState[_sortDirection] == null)
ViewState[_sortDirection] = SortDirection.Ascending;
return (SortDirection)ViewState[_sortDirection];
}
set
{
ViewState[_sortDirection] = value;
}
}
private int CurrentPageIndex
{
get
{
if (ViewState[_currentPageIndex] == null)
ViewState[_currentPageIndex] = 0;
return Convert.ToInt32(ViewState[_currentPageIndex]);
}
set
{
ViewState[_currentPageIndex] = value;
}
}
private bool CustomPaging
{
get { return (VirtualItemCount != -1); }
}
#endregion
#region Overriding the parent methods
public override object DataSource
{
get
{
return base.DataSource;
}
set
{
base.DataSource = value;
// store the page index so we don't lose it in the databind event
CurrentPageIndex = PageIndex;
}
}
protected override void OnSorting(GridViewSortEventArgs e)
{
//Store the direction to find out if next sort should be asc or desc
SortDirection direction = SortDirection.Ascending;
if (ViewState[_sortColumn] != null && (SortDirection)ViewState[_sortDirection] == SortDirection.Ascending)
{
direction = SortDirection.Descending;
}
GridViewSortDirection = direction;
GridViewSortColumn = e.SortExpression;
base.OnSorting(e);
}
protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
{
// This method is called to initialise the pager on the grid. We intercepted this and override
// the values of pagedDataSource to achieve the custom paging using the default pager supplied
if (CustomPaging)
{
pagedDataSource.VirtualCount = VirtualItemCount;
pagedDataSource.CurrentPageIndex = CurrentPageIndex;
}
base.InitializePager(row, columnSpan, pagedDataSource);
}
protected override object SaveViewState()
{
//object[] state = new object[3];
//state[0] = base.SaveViewState();
//state[1] = this.dirtyRows;
//state[2] = this.newRows;
//return state;
return base.SaveViewState();
}
protected override void LoadViewState(object savedState)
{
//object[] state = null;
//if (savedState != null)
//{
// state = (object[])savedState;
// base.LoadViewState(state[0]);
// this.dirtyRows = (List<int>)state[1];
// this.newRows = (List<int>)state[2];
//}
base.LoadViewState(savedState);
}
#endregion
public override string[] DataKeyNames
{
get
{
return base.DataKeyNames;
}
set
{
base.DataKeyNames = value;
}
}
public override DataKeyArray DataKeys
{
get
{
return base.DataKeys;
}
}
public override DataKey SelectedDataKey
{
get
{
return base.SelectedDataKey;
}
}
}
}
Then when you are binding the data:
gv.DataSource = yourListOrWhatever
gv.VirtualItemCount = numOfTotalRecords;
gv.DataBind();