Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Wednesday, November 18, 2009

Encoding & Decoding Bit-Sums For a CheckBoxList

Have you ever had occasion where you need to allow users to select one or more items in a list? With ASP.Net a good UI control to do that with is the CheckBoxList. Here's a pair of examples:



Then the question arises, how do you store their selections?  If the potential list is quite long then you're probably going to want to use a CDF string of the values you've assigned to each list item - example: "2,3,11,17,34"

But for smaller, more finite lists a good way to go is with bit-encoded sums.  You assign each list item a base-2 value, such as:
  • Item A    2^0 = 1
  • Item B    2^1 = 2
  • Item C    2^2 = 4
  • Item D    2^3 = 8
In this way, when the user has finished their selection, you can add up the associated values, store that sum, and then decode it later when you return to the page again.  Thus for the aforementioned example, if the user selected A, C, & D, the sum would be: 1 + 4 + 8 = 13.

Encoding the values is very straightforward.  Here's a little algorithm that does the job:
int newSum = 0;
    foreach (ListItem item in checkBoxList.Items)
    {
      if (item.Selected)
        newSum += Convert.ToInt32(item.Value);
    }

Decoding though is somewhat trickier.  I imagine there are many possible approaches.  Here's a method I created to do the job:
private void SelectCblItems(CheckBoxList cbl, int sum)
  {
    // Determine the largest Base-2 Power that comprises sum
    int maxPower = (int)Math.Log(sum, 2);
    for (int pow = maxPower; pow >= 0; pow--)
    {
      int partSum = (int)Math.Pow(2, pow);
      if (partSum <= sum)
      {
        ListItem listItem = cbl.Items.FindByValue(partSum.ToString());
        if (listItem != null)
          listItem.Selected = true;

        sum -= partSum;
      }
    }
  }

Monday, August 17, 2009

A "Subtle" MessageBox

For years now, whenever I wanted to display a message box to the user I'd use some variation of the following:

// Displays an alert message box to the user.
public static void ShowMessage(string msg)
{
msg = Tools.FixJavaScriptString(msg);
Page page = HttpContext.Current.CurrentHandler as Page;
ScriptManager.RegisterStartupScript(page, page.GetType(),
Guid.NewGuid().ToString(), "alert('" + msg + "');", true);
}

// A single apostrophe is not allowed in a SQL string
// on its own so we need to prefix it with a backslash
.
public static string FixJavaScriptString(string text)
{
string text2 = text;
if (text.IndexOf("'") != -1)
text2 = text.Replace("'", "\\'");

return text2;
}

This works fine but the drawback is that the user must press the OK button on every Alert box.

Inspired by a feature I first noticed in FaceBook, I decided to create a MessageBox that would appear but then fade away after a developer-defined period of time. You can see it in action in this video:


The solution works with all of the following, which I use in most of my web development work:
  • The ASP.Net AJAX UpdatePanel
  • C#
  • jQuery
To get it working, first you must add this markup code to every page you wish to display a Subtle MessageBox on:




I always place this code right near the bottom of every Content page, just above "".

Then add the following server-side code to your web app (I have it in a shared code file called "Common.cs") :

// Displays a message box that disappears on its own.
public static void ShowSubtleMessage(string msg, int duration, string[,] cssParams)
{
msg = Tools.FixJavaScriptString(msg);
string script = "$('div#msg').empty().html('" + msg + "'); $('div.subtleMsg')";

if (cssParams != null)
{
string property = "";
foreach (string cssItem in cssParams)
{
if (property == "")
property = cssItem;
else
{
string propVal = cssItem;
script += ".css('" + property + "', '" + propVal + "')";
property = "";
}
}
}

Page page = HttpContext.Current.CurrentHandler as Page;
script += ".fadeIn(1000).animate({ opacity: 1.0 }, " + duration.ToString() + ").fadeOut(2000);";
ScriptManager.RegisterStartupScript(page, page.GetType(), Guid.NewGuid().ToString(), script, true);
}

Here is the CSS code that I use:

div.subtleMsg
{
display:none;
float: left;
position:absolute;
left:360px;
top:220px;
width:300px;
height:100px;
background:#0e90e6 url(../Images/Gradients/blueRectGradient4.jpg);
text-align:center;
padding:10px;
font-size:16px;
font-weight:bold;
color:White;
border:ridge 5px darkgray;
}


And here's a typical call to the method:

string msg = "Blank descriptions are not allowed!";
Common.ShowSubtleMessage(msg, 2000, new string[,] { { "left", "470px" }, { "top", "310px" }, { "width", "330px" }, { "height", "60px" } });



There are several improvements that could be made:
  • The HTML markup code could potentially be created with jQuery, via a call from the server-side code. I tried to do this but after several hours gave up. Perhaps someone reading this will come up with a solution!
  • Potentially the minimum required dimensions of the message box could be automatically calculated using some method in the .Net library.
But I think it's a good start and it works well for me! You can download a copy of everything here.

Tuesday, June 9, 2009

Cacading Events in a Master-Content Page Project

I came across a situation where I needed to add a toolbar to a Master Page and monitor the toolbar button events from the Content Pages. I did a bunch of research and came across this article. It explains what one must do to accomplish this.

One key necessity is to add a directive similar to this on every content page:
<%@ MasterType VirtualPath="~/main.Master" %>

Then in the Master Page you have to define an event handler similar to this:
public event EventHandler CommandButton_ClickHandler;

When a button in the Master Page is pressed, you explicitly fire the event like this:
protected void CommandButton_Click(object sender, EventArgs e)
{
CommandButton_ClickHandler(sender, e);
}

Finally, in every content page you have to wire up the event handler mechanism:
protected void Page_Init(object sender, EventArgs e)
{
Master.Toolbar_ClickHandler += new EventHandler(CommandButton_Click);
}

Whenever a button in the Master Page is pressed, it causes the event handler you setup to fire, which in turn is monitored by the listener in the content page. It's all quite simple but you have to get the syntax just right.



I created a small proof of concept project, which includes buttons directly located in a Master Page, as well as another example of buttons in a toolbar, which in turn sits in a Master Page. You can download this project here. For the first approach, set "default.aspx" as the start page. For the second approach, use "default2.aspx" instead. Hopefully this will help you implement this powerful concept in your own work!

Sunday, April 26, 2009

Smart Redirection

I have a situation where I have a test server running on my home network. I was using it to host just one application but when I wanted to do so with 2 or more, I ran into a problem because external URLs could only be redirected to the root folder.

After some experimenting I found a simple solution that seems to work very well:

  1. In the root of Inetpub/wwwroot either remove "default.htm" or change the priority order so that "default.aspx" appears first.
  2. Install into this root folder the two files shown below, Default.aspx and Default.aspx.cs
  3. Then with your IP redirection, use this format: http://your_local_IP_address?app=folder_name - Example: http://24.81.19.172?app=MyTestApp

It's not perfect in that it requires one to add the "app" parameter & value but other than that, it works well. If someone has a simpler solution, I'd love to see it!

Here's the contents of the two required files:

Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>

Default.aspx.cs

using System;
using System.Web;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string targetFolder = "/MyMajorTestApp"; // Default app to run, in case not "app" parameter is provided
if (Request.Params["app"] != null)
targetFolder = "/" + Request.Params["app"];

HttpContext.Current.Response.Redirect(targetFolder, true);
}
}

Friday, January 16, 2009

Retrieving a Person's Last Name from a FullName field

I encountered a unique situation where a client's database table had usernames stored in a single "FullName" field, rather than the more common approach of having one field for the first name and another field for the last name. Restrictions within the company prevent this from ever being altered. Yet I needed a way to retrieve a person's last name, no matter how a name might be formatted.

Based on the possible data in their database, I wrote this code to accomplish this:

// A string containing a person's full name comes in 3 forms:
// 1. John Smith
// 2. John O. Smith
// 3. Smith, John [O.]
// This method returns just the last name.
public static string GetLastName(string fullName)
{
string lastName = "";
fullName = fullName.Trim();

if (fullName.Contains(",")) // Case #3
{
int idx = fullName.IndexOf(",");
lastName = fullName.Substring(0, idx);
}
else
{
int idx = fullName.IndexOf(" "); // Find the first space character
int idx2 = -1;
if (idx != -1)
idx2 = fullName.IndexOf(" ", idx + 1);

if (idx2 != -1) // Case #2
lastName = fullName.Substring(idx2 + 1);
else // Case #1
lastName = fullName.Substring(idx + 1);
}

return lastName;
}

Friday, November 21, 2008

Passing a Method as a Parameter

I have a library that has a number of methods similar to this:

public static DataTable GetContracts()
{
string tableName = "Contracts";
DataTable dataTable = (DataTable)Tools.GetCacheObject(tableName);
if (dataTable == null)
{
dataTable = DataObjects.Common.GetContractsFromDB();
Tools.AddToCache(tableName, dataTable);
}

return dataTable;
}


The only things that change in each one are the items shown in purple. I got to thinking that this code could be streamlined further by simply passing in 'tableName' as a parameter. But what to about the fact that the method being called in 'DataObjects.Common' changes too?

Well, thankfully C#.net has a solution for this too! And its name is "Delegate". It essentially acts as a proxy for a method, allowing a method to also be passed in as a parameter.

Here's the simple code to make this work, using the same example as above:

private delegate DataTable DBFetchDelegate();

private static DataTable GetDataTable(string tableName, DBFetchDelegate dbMethod)
{
DataTable dataTable = (DataTable)Tools.GetCacheObject(tableName);
if (dataTable == null)
{
dataTable = dbMethod();
Tools.AddToCache(tableName, dataTable);
}

return dataTable;
}

public static DataTable GetContracts()
{
return GetDataTable(
"Contracts", DataObjects.Common.GetClosedMonths);
}

Wednesday, November 19, 2008

Obtaining the largest ID value from a DataTable

In the dotNet Framework data tables are often used to represent tables from a database. And in these there's often an "ID" column. A common desire is to obtain the largest ID value in the data table. I did some searching and couldn't find a direct answer. So I experimented and came up with a very simple solution:

public static int GetMaxID(DataTable dataTable)
{
// Sort the DataRow Array by descending ID
DataRow[] sortedRows = dataTable.Select(null, "ID DESC");

// and return the first ID, which will be the maximum
return (int)sortedRows[0]["ID"];
}

Monday, April 21, 2008

"Does not exist in the current context"

The fun & games with Visual Studio continue! I generally love the package but sometimes it frustrates me to no end.

So there I was testing a simple web page, adjusting this & that. Suddenly, when I tried to recompile it I got this message:

labelTagLine does not exist in the current context

This control resides in the header on the Master Page of my project. There's nothing fancy or special about it. I did a comprehensive search and found lots of others who had encountered similar problems with controls suddenly not being recognized. I tried all of their solutions but to no avail.

So eventually I copied main.Master and main.Master.cs outside of the project folder and deleted these two files within the project. Then I created a new, blank version of them. Slowly, I started copying the markup code and C# code back into their respective files, being sure to perform a "Rebuild Website" every so often. Lo and behold, when every last line of code was back, it worked! Something internally must have changed but nothing in my code files did. Another 2 hours wasted. :-(