Thursday, July 24, 2008

Managing DataBound DropDownLists Properly

So I found a tricky problem with databound DropDownLists that causes the selectedIndexChanged event to fire more than you probably intended.

Normally when you add one - you might DataBind it, and then do something like

my_ddl.Items.Add(new ListItem("Select . . . "));

This can become tricky pretty fast if your DropDownList does anything major - like add a selection to a form. The problem is that dropdownlists seem to be loaded with a SelectedIndex of -1. This means that when they are unbound (aka have no data at all), they don't want to put 0 (because that would indicate an item) - so its a -1.

So you bind your data, and then add a top level ListItem for convenience - and now the selected Index is 0 - so SelectedIndexChanged event fires!

The simple fix is to force all of your DDL's to initialize to 0 - and always have a top item! That way, if a user selects the top item, the event does not fire, but any other databound item will work fine.

So, in Page_load, do this

protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
return;

my_ddl.SelectedIndex = 0;
}

Note that this only needs to be done for databound controls / controls created programmatically. I have not extensively tested, so I am not 100% on what controls this affects, but I am pretty sure that ones created declaratively in the ASPX page will not have this problem

Hamy

Monday, July 21, 2008

Blogger's Shortcomings

To say the least, I am pretty disappointed in blogger so far.

I can not add more tags - so if I post a 4 page tutorial (like the post just before this one) - my homepage is destroyed, rather than just showing a paragraph of the post and then a link to read the rest if you are interested.

The back end editor is quite sad. I try to add code a lot. There is almost no easy way to get code to format correctly. <pre> tags don't seem to always work, and they have no convenient <code> tag.

There is no way to add files. There is no nice upload interface or such. Come on - it's Google. Can't they even register your blog an email address and use that room as storage?


Ill be switching back to wordpress as soon as I get a chance.

~unhappy
hamy


PS - I do like the way blogger handles comments. Thats it.

ViewState - A Quick Users Guide

There are a lot of tutorials on ViewState. I have read quite a few of them, and this is meant to expand upon them by condensing information in an easy to understand format. Hope it helps.

GamePlan
I want to show you what ViewState can be used to store - with little to zero work on your part. I will start with the really simple stuff - then step it up a few times to finally end with Composite Controls (a custom control you make from other controls) with constructors.

No Dynamics - All Static Content
  • The first thing you will be told about viewstate is that declarative syntax is automatically stored. Example :

--------------------------------
ASPX
<asp:dropdownlist id="ddl" runat="server" enableviewstate="true">
<asp:listitem text="1" value="1"></asp:listitem>
<asp:listitem text="2" value="2"></asp:listitem>
<asp:listitem text="3" value="3"></asp:listitem>
<asp:listitem text="4" value="4"></asp:listitem></asp:dropdownlist>
--------------------------------


This DDL will always be visible on the page. If you choose an item, and cause a postback - your item will still be selected. This is the first thing everyone see's - which leads a lot of people to get the illusion ViewState is 'magical'. This isn't true, it's easy to understand. I am not covering how it works right now, just how to use it.

Programmatically Added Controls
  • Here it gets a tad more interesting. I am programmatically adding a DDL to the page. The key is - if you add something after the page has been sent to the client once (this is a tad misleading, but its the really simple explanation) - then you must re-add it to the page EVERY time


--------------------------------
ASPX
<asp:panel id="panel" runat="server" enableviewstate="true">
</asp:panel>

Code Behind
protected void Page_Load(object sender, EventArgs e)
{
add_ddl();
}

private void add_ddl()
{
DropDownList d = new DropDownList();
panel.Controls.Add(d);

if (d.Items.Count == 0)
{
d.Items.Add("First");
d.Items.Add("Second");
d.Items.Add("Third");
d.EnableViewState = true;
}
}
--------------------------------
  • So - the nifty part is that via some .NET naming stuff and fancy tricks - Your settings will be restored - even though it is technically a 'new' control! What this means is that you can programmatically add two controls, change their settings, and cause a postback - and re-add new controls in the same place - and they will still have the settings you chose before!

Composite Control Without Special Constructor
So - this will show you how to build a simple composite control. The nice thing is that this example is really the exact same as the last one - just with a composite control. All you have to do is (assuming you add your composite control at run-time, aka in the Code Behind and not in the aspx) re-add your control and any values you choose before will be automatically persisted. Pretty neat.

--------------------------------
Code Behind
{ . . .

protected void Page_Load(object sender, EventArgs e)
{
add_ddl();

panel.Controls.Add(new Add_Composite_Control());
}

. . . }

public class Add_Composite_Control:CompositeControl
{
private DropDownList dl;

protected override void CreateChildControls()
{
Controls.Clear();
CreateControlHierarchy();
ClearChildViewState();
}

protected virtual void CreateControlHierarchy()
{
dl = new DropDownList();

dl.Items.Add("Custom 1");
dl.Items.Add("Custom 2");
dl.Items.Add("Custom 3");

Controls.Add(dl);
}
}
--------------------------------
  • Once again, the nifty thing here is that .NET naming classes can "remember" the right names for everything, so your changes persist across postbacks. So - try it out. Add two of these controls to the page, and vary their values. Cause a Postback - and the controls will be re-created with the values they had before.


Composite Control With Special Constructor
  • This is the hardest example I am going to show you. It involves manually setting stuff in the ViewState. Up until now, we have just had to re-add controls and everything has been quite easy.
  • We need to set ViewState variables because we need to re-add the controls to the page. And without the constructor arguments, we cannot re-add anything. Note that we do not have to remember what value was selected, or anything like that. We are still using ViewState to it's full advantage. All we need to be able to do is re-add the controls
--------------------------------
ASPX
<asp:Panel ID="panel1" runat="server">
</asp:Panel>

<asp:Button Text="Add Control" ID="btn2" runat="server" OnClick="Add_Control" />


Code Behind

{ . . .

protected void Page_Load(object sender, EventArgs e)
{

if (ViewState["storage"] != null)
{
// Create the regex
string variables1 = (string)ViewState["storage"];
Regex reg = new Regex(@"\|(?.+?),(?.+?);");
Match m = reg.Match(variables1);

// extract the variables
while (m.Success)
{
string varname = m.Groups["name"].ToString();
string id = m.Groups["id"].ToString();
panel1.Controls.Add(new Log_Format_Variable(varname, id));
m = m.NextMatch();
}
}
}
protected void Add_Control(object obj, EventArgs e)
{
Random r = new Random();
int rand = r.Next(1,3);

if (rand == 1)
{
panel1.Controls.Add(new Log_Format_Variable("var_one", "1"));
ViewState["storage"] += "|var_one,1;";
}
if (rand == 2)
{
panel1.Controls.Add(new Log_Format_Variable("var_two", "2"));
ViewState["storage"] += "|var_two,2;";
}
if (rand == 3)
{
panel1.Controls.Add(new Log_Format_Variable("var_three", "3"));
ViewState["storage"] += "|var_three,3;";
}
}

. . . }

public class Log_Format_Variable : CompositeControl
{
private DropDownList d;
private string var_ID;
private string varname;

public Log_Format_Variable(string varname, string var_ID)
{
this.var_ID = var_ID;
this.varname = varname;
}

protected override void CreateChildControls()
{
Controls.Clear();
CreateControlHierarchy();
ClearChildViewState();
}
protected virtual void CreateControlHierarchy()
{
Label l = new Label();
l.Text = "Function for " + varname;

LiteralControl lc1 = new LiteralControl(" ");

d = new DropDownList();
d.Items.Add(new ListItem("Sum", "SUM"));
d.Items.Add(new ListItem("Average", "AVG"));
d.Items.Add(new ListItem("Max", "MAX"));
d.Items.Add(new ListItem("Min", "MIN"));

LiteralControl lc2 = new LiteralControl("
");

this.Controls.Add(l);
this.Controls.Add(lc1);
this.Controls.Add(d);
this.Controls.Add(lc2);
}
}

So, basically all I am doing extra here is saving the information needed to re-call the constructors. I use a small schema to save the constructor info - the "|varname,var_id;" scheme makes it simple for me to get the info back out of the ViewState, but this is really not necessary - there are many better ways to store the info. The Add_Control just lets me add a few random comtrols, and it does not matter if two controls have the same constructor information. Every CompositeControl implements a Naming interface, which means that all things belonging to that control have a unique prefix. Hence, if two controls have the same constructor info - they are still regarded as two separate entities. So - try this out. You can create as many of these as you would like, and they will all be able to remember their values across subsequent postbacks.

WrapUp
Here is the source code for the entire project - which includes all of the control types listed above.


Things to note
  • This probably did not help you understand at all why viewstate works. Please read up on that to get the full picture
  • To cause a postback, just add a button to your ASPX page. Don't assign it an OnClick value, and it will simply postback and do nothing else
  • ViewState information is saved on the CLIENT side - and can thus be victim to a few things. Notably, someone can tamper with it. It is weakly encrypted and can be easily changed.
  • Again about the client side - this means larger pages, larger page load times, etc. Long story short, don't store a lot of information in the ViewState.
  • ViewState is enabled by default. All of my enableviewstate="true" are just for clarity. You don't need them

Please let me know if I have goofed anything or could have done something better. This is not meant to be an in-depth guide to understanding ViewState, but rather a quick-flip guide to using it easily.

Hamy

References

Understanding ASP.NET ViewState by Scott Mitchell
Control Building and ViewState Lesson for the Day
Binary Fortress ViewState Helper Tool
There is at least one significant other site that I sadly cannot remember the name of. There are also a significant number of small helper sites.

When Databinding results in System.Byte[]

So - quite a few people seem to be having an issue with DataBinding to any sort of control (Dropdownlist, DataGrid, etc) resulting in the bound control printing out System.Byte[]. This may not be the one true answer, but it is definitely one of them.

I noticed a lot of people (like myself) were having trouble with MySql - specifically connector/net. The solution is simple, and probably affects other database storage engines just as well.

Basically - the just of it was this - here is my code to bind a DropDownList. Pretty readable stuff

----------------------------
string sql = "CALL Get_LFID_info('" + ddl_Log_Formats_1.SelectedValue + "');";
DataTable dts = ExecuteMySqlAdapter(sql);
relation1.DataSource = dts;
relation1.DataTextField = "extended_varname";
relation1.DataValueField = "variable_id";
relation1.DataBind();
----------------------------


The issue with this case was that my procedure calculated the result of "extended_varname" - which means that it was created by doing a SELECT CONCAT(name,".",id) sort of deal. Which meant there was no column type associated with it. The solution - pretty simple. Just cast the result. I did this - SELECT CAST(CONCAT(name,".",id) AS CHAR) AS extended_varname . While I believe that you might be able to so that simpler, it was the easy answer. If I figure out a better way to cast it, I will let you know - but the point is the general idea that the column needs to have a specific type associated with it before MySql Connector can correctly convert the rows to strings - any System.Byte[] result generally indicates a cast went wrong somewhere.

Hope that helps
-Hamy

References
mysql type conversion

Wednesday, July 2, 2008

Bypass Security so you can play Flash videos and Games

So, if you have ever been at a work/school/restricted computer and cannot play Flash games or videos, good news! There is an easy workaround even if the installers will not work.

Step 1 - install Firefox
  • get the file from http://www.mozilla.com/en-US/firefox/
  • Run the installer, and choose Custom installation
  • You want to install it inside the My Documents folder
  • Preferably, create a folder inside your My Documents called FireFox and install it there
Step 2 - download the Flash plugin
  • Right click and choose 'Save as' on the following link
  • http://fpdownload.macromedia.com/get/flashplayer/xpi/current/flashplayer-win.xpi
  • Rename the file to flashplayer-win.zip
  • If you get a warning, ignore
step 3 - install the plugin manually
  • open the firefox folder plugins folder
  • probably located somewhere like this:
  • C:\Documents and Settings\My_User_Name\My Documents\FireFox\plugins
  • Open the flashplayer-win.zip and copy two files into your plugins directory
  • You will need to copy over NPSWF32.dll (required) and NPSWF32_FlashUtil.exe (optional, just help files)
Step 4 - restart flash and start browsing!

Hope that helps someone. A little reasoning behind what we did:
We installed into the my documents folder so that no matter what user you are, you have full permissions to write, execute, and modify files (basically gets rid of that annoying "you need to be an administrator to do this" dialog)
*.xpi files are the same thing as *.zip files, just with a different extension. They can be renamed easily and that gives easy access to all the files we need

Hamy

How do you know when to take a break from Developing?

So, how do you know when to take a break from developing? Here's how:

User 'hamy' has exceeded the 'max_connections_per_hour' resource (current value: 9999)


(If you don't know - this means that in one hour I connected to mysql over 9999 times)

Obviously, I am working on a DB intensive program - Its point is to enable logging, unit testing, and metric graphing that can be setup in a matter of fifteen minutes, easily changes and thrown away.

:)

hamy