I am working on a quiz control in asp.net with dynamically created questions and options.
The main control is basically a container to hold all of the questions.
In design view users can add questions through a custom Collection Editor.
Everytime i add a question to the collection editor list it generates a question tag for me.
Inside each question object is a label and a n amount of Option objects that inherit the Radiobutton Control. Each of these Option objects in turn represent a option the user can select for each question.
This all works except i am now at the part where i want to be able to read the Checked value of each radiobutton. When i want to implement this quiz inside a page and check the questions i want to put a button in this page and call the following function that is inside the control:
$
public String checkQuestions()
{
if (questions != null)
{
foreach (Question question in questions)
{
options = question.readOptions();
int i = 0;
foreach (Option option in options)
{
testLabel.Text = option.Checked.ToString(); // test purposes only
}
}
}
return errors;
}
However once i select a radiobutton and click on the submit button the Checked value will always turn out false for all of the options.
Basically it is losing its checked value after a Postback and i am just stuck in trying to solve it.
Would appreciate it if anyone could point me in the right direction.
At a first glance, there are two things I'd check. Firstly, make sure you're implementing IPostBackDataHandler. this requires you to implement two methods, LoadPostData and RaisePostDataChangedEvent. At my first guess, the first one is probably the source of your problem.
Handling postback manually
LoadPostData takes a string postDataKey and a NameValueCollection postCollection and returns a bool indicating whether or not the value has changed as a result of the postback. You don't need to implement this the way .Net originally intends, for example I created a control that held several radio buttons (that for reasons that aren't important here couldn't simply be a RadioButtonList control) and so made sure they were all named by a property string GroupName and inspected the postCollection for that GroupName:
public bool LoadPostData(string postDataKey,
System.Collections.Specialized.NameValueCollection postCollection)
{
bool oldValue = _isChecked;
postCollection = HttpContext.Current.Request.Form; // See note below
_isChecked = (postCollection[this.GroupName] == this.Text);
return oldValue == _isChecked;
}
You'll notice that I'm redefining the postCollection here; this is because postCollection only contains a subset of the HttpRequest.Form corresponding to what ASP.Net thinks your control should care about. As you're also building a composite control here, you probably want to do the same.
Don't worry if this doesn't work first time round; it's worth stepping through what gets passed into this method in debug mode (or outputting things to the HttpContext.Trace, which I often find easier) to see why your code isn't quite what you need.
A quick caveat
One last thing: LoadPostData is only called if the posted form contains a field with a name which matches the UniqueID of your control. As your control is a composite control, you might want to cowboy this slightly, like so:
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
writer.WriteBeginTag("input");
writer.WriteAttribute("type", "hidden");
writer.WriteAttribute("name", this.UniqueID);
writer.WriteAttribute("value", "post");
writer.Write(" />");
}
It's a dirty hack, but it'll work ;o)
Handling viewstate manually
If handling the postback manually doesn't solve your problem, it might be that you need to mess with the viewstate of your control. Don't worry, this is nowhere near as scary as it seems, provided you follow a few simple rules.
To handle your viewstate manually, you just need to override two methods called, obviously enough, LoadViewState and SaveViewState. The first takes an object of viewstate to inflate and the other returns that same object structure. If you make your SaveViewState override return something containing the structure you need to save all the important properties that need persisting, then you just inflate it again in your LoadViewState method.
Here's where the first of the cunning tricks comes up. There are certain datatypes that you should use for saving viewstate and you should never use any other type (because other types are stored really inefficiently). The types that will probably be most useful to you are System.Web.UI.Pair, System.Web.UI.Triplet and our old friends System.Collections.ArrayList and System.Collections.Hashtable. Pairs and Triplets simply store two or three values of type object; ArrayLists are effectively a List<object>.
I'd guess that, in your circumstance, you probably want to store either (1) an ArrayList of boolean flags, storing the "checkedness" of your radiobuttons or (2) an ArrayList of strings or ints, storing the IDs or index of the checked radiobuttons.
In the control I mentioned earlier, I just needed to store the checkedness and the Text property, so my LoadViewState and SaveViewState methods looked like this:
protected override void LoadViewState(object savedState)
{
Pair state = savedState as Pair;
if (state != null)
{
_isChecked = state.First as Nullable<bool> ?? false;
this.Text = state.Second as string;
}
}
protected override object SaveViewState()
{
return new Pair(_isChecked, this.Text);
}
Again, if this doesn't work first time, you almost certainly want to step through the code or throw things into the Trace. Importantly, you probably want to avoid throwing Exceptions from these methods, in case your viewstate is corrupt or non-existent or something.
Further reading on viewstate
There are a couple of very useful articles I keep bookmarked for when I'm messing with viewstate. The first one explains about why you should only store certain types in the viewstate (like using ArrayList and Hashtable, rather than List<T> and Dictionary<TKey, TValue>) and the second is a good in-depth explanation of how all this viewstate stuff actually works.
Don't let the BinaryFormatter get at it!
Truly understanding ViewState
I hope all this helps resolve your problem.
Related
I have a large WPF application that uses the MVVM design pattern and asynchronous data access methods. It uses the old style asynchronous code with callback handlers and the IAsyncResult interface... here is a typical example:
function.BeginInvoke(callBackMethod, asyncState);
Then , in the view model, I have the following callback handler:
private void GotData(GetDataOperationResult<Genres> result)
{
UiThreadManager.RunOnUiThread((Action)delegate
{
if (result.IsSuccess) DoSomething(result.ReturnValue);
else FeedbackManager.Add(result);
});
}
The RunOnUiThread method is basically the following:
public object RunOnUiThread(Delegate method)
{
return Dispatcher.Invoke(DispatcherPriority.Normal, method);
}
This problem only affects one view model, the one that enables the users to edit the Release objects. On the related view, certain collections that populate ComboBoxes are requested from the database when it is first loaded. Let's simplify this saying that there is just one collection called Genres. After the data arrives in the view model, it is handled like so:
private void GotGenres(GetDataOperationResult<Genres> result)
{
UiThreadManager.RunOnUiThread((Action)delegate
{
if (result.IsSuccess) Genres.AddEmptyItemBefore(result.ReturnValue);
else FeedbackManager.Add(result);
});
}
When the collection is present and a Release object has been selected in the UI, I have the following code selects the current Release.Genre value from the collection:
if (!Release.Genre.IsEmpty && Genres.ContainsItemWithId(Release.Genre.Id))
Release.Genre = Genres.GetItemWithId(Release.Genre);
At this point, I should note that this all works fine and that this is the only line that references the Release.Genre property from the view model.
My particular problem is that sometimes the Release.Genre property is set to null and I can't work out how or from where. >> Edit >> When I put a break point on the property setter, << Edit << the Call Stack provides no real clues as to what is setting the null value, as there is only a [Native to Managed Transition] line. On selecting the Show External Code option from the Call Stack window, I can see basic asynchronous code calls:
Now I can confirm the following facts that I have discovered while attempting to fix this problem:
The one line that references the Release.Genre property is not setting it to null.
The call to Genres.AddEmptyItemBefore(result.ReturnValue) is not setting it to null... this just adds the result collection into the Genres collection after adding an 'empty' Genre.
The Release.Genre property is sometimes set to null in or after the call to Genres.AddEmptyItemBefore(result.ReturnValue), but not because of it... when stepping through it on a few occasions, execution has jumped (in an unrelated manner) to the break point I set on the Release.Genre property setter where the value input parameter is null, but this does not happen each time.
It generally happens when coming from a related view model to the Release view model, but not every time.
The related view model has no references to the Release.Genre property.
To be clear, I am not asking anyone to debug my problem from the sparse information that I have provided. Neither am I asking for advice on making asynchronous data calls. Instead, I am really trying to find out new ways of proceeding that I have not yet thought of. I understand that some code (almost certainly my code) somewhere is setting the property to null... my question is how can I detect where this code is? It does not appear to be in the Release view model. How can I continue to debug this problem with no more clues?
I usually use Flat File, XML or Database logging for debugging purpose. I created those Log classes for logging purpose, so that I can call it from every applications I develop.
For database logging, you can do it as simple as:
void WriteLog(string log){
// Your database insert here
}
Maybe you need datetime and other supporting information, but it's up to the developer. For simple flat file logging is:
void WriteLog(string log){
using(TextWriter tx = new StreamWriter("./Log/" + DateTime.Now.ToString() + ".txt", false)){
tx.WriteLine(log);
}
}
You can use the logging in your application in both ways:
1: Method call
WriteLog((Release.Genre == null).ToString());
if (!Release.Genre.IsEmpty && Genres.ContainsItemWithId(Release.Genre.Id))
Release.Genre = Genres.GetItemWithId(Release.Genre);
2: Add it in your Release.Genre set (or get) property
public class Release{
private Genre _genre=null;
public Genre Genre{
get{
WriteLog((_genre == null).ToString());
return _genre;
}
set{
WriteLog((_genre == null).ToString());
_genre = value;
}
}
}
With this, you can try to get the call sequence, whether the Release.Genre is being set in other places before, during call, etc.
Please note I just giving the general image of building logging. Please expect errors. However, it is developer's responsibility to develop the Logging acitivities to meet requirement.
Context:
I've often been in situations where our ASP.NET pages would have to show data to the user on a GridView, let him change it as he pleases (Textbox on cells) and only save it to the database when he actually hits the "Save Button". This data is usually a virtual state of the information on the page, meaning that the user can change everything without really saving it until he hits the "Save Button".
In those cases, there's always list of data that needs to be persisted across ASP.NET Postbacks. This data could be an instance of a DataTable or just some List<Someclass>.
I often see people implementing this and persisting the data on Session. On that cases i also usually see problems when it comes to some user navigating with multiple tabs open, some times on the same page. Where the data of two different tabs would get merged and cause problems of information being scrambled.
Example of how Session is often used:
private List<SomeClass> DataList
{
get
{
return Session["SomeKey"] as List<SomeClass>;
}
set
{
Session["SomeKey"] = value;
}
}
People often tries to solve it by doing something like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DataList = null
}
else
{
FillGridView(DataList);
}
}
But what about when two tabs are already loaded and the user is changing the GridView values and for some weird reason he tries to save the data by hitting the Save button on the other page? I personally dislike this option.
Other ways to do this would be to put the data on ViewState. However, when it comes to persisting substantially big lists, it could impact the page heavily when it's stored on the page (HiddenField).
But, what's the best way to make that work? Once, i thought in using Session together with ViewState where the ViewState would hold an unique identifier which would index the Session saved data. That would prevent sharing the data between tabs on the browser:
private List<SomeClass> DataList
{
get
{
if (ViewState["SomeKey"] == null)
{
ViewState["SomeKey"] = Guid.NewGuid().ToString();
}
return Session[ViewState["SomeKey"].ToString()] as List<SomeClass>;
}
set {
if (ViewState["SomeKey"] == null)
{
ViewState["SomeKey"] = Guid.NewGuid().ToString();
}
Session[ViewState["SomeKey"].ToString()] = value;
}
}
On the other hand it would store a new list of data to the Session every time the user enters the page. Which would impact the server memory. Maybe they could be erased in some way.
Question:
What would be the best way of persisting that kind of data across Postbacks, considering the contexts of multiple tabs on the browser, with the less cost to the server and to the maintenance coding team?
Update:
As #nunespascal nicely posted, one option would be to store the ViewState in the Session using the SessionPageStatePersister. But unfortunately that's not an option on my case. And yet it is not very different from my last example, saving the data on the Session indexed by an UniqueId stored on the ViewState.
Would there be any other options?
There is a simple solution to that problem. Store the ViewState in the Session.
For that you need to use the SessionPageStatePersister
Refer: Page State Persister
All you need to do is override the PageStatePersister and make it use SessionPageStatePersister instead of the default HiddenFieldPageStatePersister
protected override PageStatePersister PageStatePersister
{
get
{
return new SessionPageStatePersister(this);
}
}
This even saves you the headache of maintaining a unique key. A hidden field will be used automatically to keep a unique key per instance of the page.
I've come across a similar situation. The idea is if you allow long sessions for each user to change the grid view, this means you'll also have a concurrency problem because eventually you will accept only one last set of modifications to your data.
So, my solution was, to allow changes on the database but make sure all the users see the same state via SignalR.
Now, the concurrency problem has disappeared but you still need to make the changes on the fly. You might not want to save the changes after all. I've solved this problem by applying the command design pattern. Now any set of changes can either be approved or discarded. Whenever you check the index you will see the last approved gridview. Go to update page and you see the live-updated gridview. Also, go to revisions to see old approved gridview -another advantages of command design pattern-.
I have a PivotControl with bindings. Everything worked well until I added a pivot two pivots with the same title, let's call them C and C', and A an B two different pivots, ordered like this: AC'BC. Now, when I try to go from B to C everything is OK. But when I go from A to C, it does strange things: doesn't move, moves the titles but not the content... weird.
I debugged the SelectionChanged event in the PivotControl, and it's even weirdest. It's called two times: the first, a transition from C to another pivot and then another call which contains the right transition, from A to C.
I've tried everything. The databinding is a list of structs, like this:
struct Resource
{
public someenum Type;
public string Data;
public string Identifier { get {...} set {...} }
public UserToken User;
}
The title of the pivot is set by a IValueConverter, which takes the string Identifier (which is a function of Type, Data and User) as the parameter. I changed it to make the titles always different, but it still does not work. For example, when Type and Data are the same there are error, although Identifier and User are different.
I have written both operators == and != for Resource, and also for UserToken. They behave as they should, but still does this weird transition.
Stating the obvious: give them different names.
If they are different you should give them different names. Just post-fix the name with a counter.
EDIT
From the MSDN:
Name is one of the very few dependency properties that cannot be
animated (IsAnimationProhibited is true in metadata), because the name
itself is vital for targeting an animation. Data binding a Name is
technically possible, but is an extremely uncommon scenario because a
data-bound Name cannot serve the main intended purpose of the
property: to provide an identifier connection point for code-behind.
Just found the answer. After a lot of googling, I noticed that maybe GetHashCode had something to do in this mess. I modified the definition of getHashCode in the struct and, voilá! It works smoothly :)
Just a tip: to get a good GetHashCode function return a XOR of the volatile parameters of the function. In my case, the code was this:
Type.GetHashCode() ^ Data.GetHashCode() ^ User.GetHashCode();
I'm facing a problem that I don't know how to solve and am hoping the community can help.
I'm writing an app that manages "Lead" objects. (These are sales leads.) One part of my program will import leads from a text file. Now, the text file contains lots of potential leads, some of which I will want to import and some of which I won't.
For ease of programming (and use), I'm parsing the text file into a List<Lead> object, and using a DataGridView to display the leads by setting the DataSource property of the DataGridView.
What I want to do is add a column to the grid, called "Import," with a checkbox that the user can check to indicate whether or not each lead should be imported.
My first thought is to derive a class from Lead:
public Class LeadWithImportCheckbox : Lead
{
bool bImport = false;
public bool Import
{
get { return bImport;}
set { bImport = value;}
}
}
However, the parsing engine returns a list of Lead objects. I can't downcast a Lead to a LeadWithImportCheckbox. This fails:
LeadWithImportCheckbox newLead = (LeadWithImportCheckbox)LeadFromParsingEngine;
This is an invalid cast.
The other option I see is to create a constructor for LeadWithImportCheckbox:
public LeadWithImportCheckbox(Lead newlead)
{
base.Property1 = newlead.Property1;
base.Property2 = newlead.Property2;
....
base.Property_n = newlead.Property_n;
}
This is problematic for two reasons. One, the Lead object has several dozen properties and writing this constructor is a PITA.
But worse, if I ever change the underlying structure of Lead, I need to remember to go back and change this constructor for LeadWithImportCheckbox. This is a danger to my code maintenance.
Is there a better way of accomplishing my goal?
or, to avoid the PITA aspect, use reflection... (try this...)
EDIT: use property, not Field as I had originally written...
public class NewLead : Lead
{
public bool Insert;
public NewLead(Lead lead, bool insert)
{
Insert = insert;
foreach (PropertyInfo pi in typeof(Lead).GetProperties())
GetType().GetProperty(pi.Name).SetValue
(this, pi.GetValue(lead,null), null);
}
}
public class LeadListItem
{
public Lead Lead { get; set; }
public bool ShouldImport { get; set; }
}
i.e. don't copy the Lead object's contents, just store a reference to it in a new LeadListItem object, which adds extra info "outside" the original object.
If you want the properties of Lead to appear in the grid, there is almost certainly a way of doing that. Why not ask that question, instead of downvoting me for telling you the right answer to this question!
A couple options you might have missed:
You could update the Lead object itself to have an Import property (that defaults to false).
You could have your "ImportLead" object treat the Lead as payload (even make it generic, if you want), so you don't need the big constructor.
Build a new Lead object list or enumerable that only contains the objects you want to import in the first place.
You can only downcast, if the object to be downcast is really an object of that type.
An easier way to solve your problem would be to have a DisplayLead class, such as:
public class DisplayLead {
Lead lead;
bool bImport;
}
which would also help you separating stored data from their representation in a GUI.
What you want to do is display the checkbox column on your grid and not have it related at all to your Lead objects. You use the marked columns (and possible the original List) to build a new set of List which will be your import list.
Then handle whatever you wish to do with the newly created List.
Edit: One thing to be careful of when working with lists is the fact every class object is actually only a pointer to the class so if you work with the original list and do something like:
List<Lead> Importable = new List<Lead>();
for(int i=0, i++, i<viewGrid.Count)
if(viewGrid[i].CheckedColumn.Checked)
Importable.Add(OriginalList[i]);
That objects will exist in both lists and if you edit data of a Lead on either list both will be changed.
I cannot downcast to something it is not. If the object was instantiated as a Lead, then it can't be downcast to any derived class. If it were instantiated as a LeadWithImportCheckbox and then returned to your code as Lead, then you can downcast it.
Protip: Check type at runtime with is operator.
There are many ways to do this, but the "right" way pops out because of what you said, here:
For ease of programming (and use), I'm
parsing the text file into a
List object, and using a
DataGridView to display the leads by
setting the DataSource property of the
DataGridView.
What I want to do is add a column to
the grid, called "Import," with a
checkbox that the user can check to
indicate whether or not each lead
should be imported.
Your Lead object stands well on its own, and you want to attach some metadata to it -- you don't want to create another Lead classification (i.e. the LeadWithImportCheckbox class).
So, the best approach in your case is to have a class like so:
public class LeadInfo
{
private Lead lead;
private bool shouldImport;
public LeadInfo(Lead lead)
{
this.lead = lead;
this.ShouldImport = false;
}
public bool ShouldImport
{
get { return shouldImport; }
set { shouldImport = value; }
}
}
This will scale well when you want to add more metadata to your list, like if you want to send yourself email reminders about them every week.
I've seen the correct solution listed so many times I feel like a heel posting it again, but the best way to approach this is to write a wrapper for the Lead object that includes the import flag.
If the properties of the Lead object don't appear in the GridView because you're databinding to the object, then write passthrough properties that mirror the Lead properties on the wrapper object.
The issue is that you want something displayed to the user that isn't an inherent part of the data model. The answer is to wrap the data before presenting it to the user so you can control what they see without changing the underlying model.
If you're concerned that the Lead object will change so many times in the future that changes to the wrapper will be cumbersome, you could look into dynamic code generation based on the Lead object that will automatically generate a wrapper object with the same fields as the Lead object plus the import flag. Though frankly, that's a lot more work than you'll probably need for something as straightforward as this.
As a quick and dirty solution, you can create your 'checkbox' object as a different object that contains an instance of Lead.
public GridLead {
public bool Import { get; set; }
public Lead Lead { get; set; }
}
This way you can easily add more 'grid' properties to this object, while still always retaining a reference to the Lead details without hardcoding property cloning into it.
Recommend you try modifying (upgrading) your imported lead objects.
Try starting with the examples here...
If your Lead class had a copy constructor (e.g. "Lead(Lead otherLead)"), LeadWithImportCheckbox would inherit that and you could just call the base Lead constructor in the LeadWithImportCheckbox constructor - hence no need for LeadWithImportCheckbox to be aware of the details of Lead.
I've got a situation where I have a main form that pops up an advanced configuration form that just has half a dozen matched check boxes and combo boxes to select some advanced options (the check boxes to enable/disable, the combo to select a media if enabled).
If I just pass the individual settings for the check and combo boxes in to the constructor for the dialog that's obviously a dozen arguments, which seems a bit excessive.
My other obvious option would be since in the main form these settings are stored in a large IDictionary with all the other main form settings I could just pass this dictionary in and fetch it back afterward with the updated values, but my understanding is that this wouldn't really be very good coding practice.
Am I missing a good way to do this that is both efficient and good coding practice?
(this particular code is in C#, although I have a feeling a general solution would apply to other languages as well)
I personally would create a carrier object to store the values. You then get the nice intellisense for it, and changes to it would be quite straightforward. It would also be faster than dictionary lookups for parameter values. And of course, you get type safety. :)
You could go with Rob's solution; that's the prettiest for development. Your "carrier object" could contain the entire IDictionary and have typed properties to help intellisense. The properties could update the IDictionary. When you're done, you can pass the carrier object back and fetch the IDictionary directly from it.
For example, if your dictionary had key/value pair "FirstEnabled"/boolean, you could do this:
class ContainerObject
{
public IDictionary<object, object> _dict;
public ContainerObject(IDictionary<object, object> dict)
{
_dict = dict;
}
public bool FirstEnabled
{
get { return (bool) _dict["FirstEnabled"]; }
set { _dict["FirstEnabled"] = value; }
}
}
You can change the member "_dict" to private or protected and have a accessor function if you want.
Something like this should be good:
MyConfigurationDialog dialog = new MyConfigurationDialog();
//Copy the dictionary so that the dialog can't mess with our settings
dialog.Settings = new Dictionary(existingSettings);
if(DialogResult.OK == dialog.Show()) {
//grab the settings that the dialog may have changed
existingSettings["setting1"] = dialog.Settings["setting1"];
existingSettings["setting2"] = dialog.Settings["setting2"];
}
I agree with Rob Cooper. Create a class to represent your configuration, and pass that into the constructor of your form. This will also allow you to define methods on your new "config" class like "saveSettings", "LoadSettings", etc. That in turn should keep the code more maintainable in general.
As an quick-and-dirty alternative, if you are saving these to a file somewhere, just pass the name of the file, and have your form read that at run-time.
The first option really is the way to go though, IMO.