Programming Journal C#, Java, SQL and to a lesser extent HTML, CSS, XML, and regex. I made this so other programmers could benefit from my experience.

Tuesday, December 23, 2008

Use a CustomControl to Set UserId and Use as a DataBindable Property

Use a CustomControl (named MembershipUser) to Set UserId and Use as a DataBindable Property. Create a MembershipUser CustomControl that has a property named Value that can be DataBound. For example MembershipUser1.Value would fetch the userId:


[Browsable (true)]
public string Value
{
get
{
MembershipUser currentUser;
currentUser = Membership.GetUser();
if (currentUser == null)
return string.Empty;
return currentUser.ProviderUserKey.ToString();
}
}
Reference: This is from http://aspnet.4guysfromrolla.com/articles/110106-1.aspx

Be Careful About uniqueId Parameter in Stored Procedure

Here is an error I received when my UniqueId parameter in a stored procedure was not valid:



Server Error in '/stockmonger.com' Application.
Conversion failed when converting from a character string to uniqueidentifier.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.SqlClient.SqlException: Conversion failed when converting from a character string to uniqueidentifier.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.


I corrected to make sure the parameter was a valid uniqueId paramater and not null.

Modifying DataBound Hyperlinks Text

Modifying a GridView's Databound Hyperlink text since I couldn't do this easily in the ItemTemplate, I moved the Replace function to the DataBound of the GridView:


protected void GridViewStocksInLists_DataBound(object sender, EventArgs e)
{
int rowId;
string imgUrl;
string navUrl;
for (rowId=0; rowId<GridViewStocksInLists.Rows.Count;rowId++) {
imgUrl = string.Empty;
navUrl = string.Empty;
imgUrl = ((HyperLink)GridViewStocksInLists.Rows[rowId].FindControl("hlChart")).ImageUrl.Replace('$', '^');
((HyperLink)GridViewStocksInLists.Rows[rowId].FindControl("hlChart")).ImageUrl = imgUrl;
navUrl = ((HyperLink)GridViewStocksInLists.Rows[rowId].FindControl("hlChart")).NavigateUrl.Replace('$', '^');
((HyperLink)GridViewStocksInLists.Rows[rowId].FindControl("hlChart")).NavigateUrl = navUrl;
}
}
This replaced $ with ^ in my HyperLink.

Here is the GridView that contains the HyperLink:

<asp:GridView ID="GridViewStocksInLists" runat="server" DataSourceID="SqlDataSourceGetStocksInList">
<RowStyle CssClass="tableAnalysisRow" />
<HeaderStyle CssClass="tableAnalysisHeader" />
<AlternatingRowStyle CssClass="tableAnalysisAlternatingRow" />
<Columns>
<asp:TemplateField HeaderText="Chart">
<ItemTemplate>
<asp:HyperLink ID="hlChart" runat="server"
ImageUrl='<%# Eval("Symbol", "http://chart.finance.yahoo.com/c/0b/d/{0}").ToLower() %>'
NavigateUrl='<%# Eval("Symbol", "http://finance.yahoo.com/q/bc?s={0}&t=1y").ToLower() %>' >
</asp:HyperLink>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>

References: http://forums.asp.net/p/1263726/2542303.aspx#2542303

Remove HTML Tags

Remove HTML Tags from a string:

public static string RemoveHtml(string txt)
{
return Regex.Replace(txt, @"<[^>]*>", string.Empty);
}


Reference: http://www.webpronews.com/expertarticles/2006/12/01/aspnet-remove-html-tags-from-a-string

Wednesday, December 17, 2008

Using AutoComplete Ajax Control With Separate ID Field from Name Field

In the previous post, I detailed how to use the SQL stored procedure to select matches.

AutoComplete Extender offers a convenient way to select values froma TextBox. One problem is that multiple details can be displayed, but the whole text is selected by default. I wanted to allow multiple AutoCompletes, select the id without the other details and append the appropriate delimeter in between entries. First, I setup the WebService method. Note that the Stock object has Symbol and Name properties:

[WebMethod]
public string[] GetStocksInPrefix(string prefixText, int count)
{
int curCount = 0;
if (count == 0)
{
count = 10;
}
List<string> items = new List<string>();
JavaScriptSerializer jss = new JavaScriptSerializer();
string[] stocks = null;
string connectionString = getConnectionString("ConnectionString");
SqlDataReader rdr = null;
SqlConnection conn = new SqlConnection(connectionString);
System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand("sproc_aspnet_GetStocksByPrefix", conn);
try
{
command.CommandType = System.Data.CommandType.StoredProcedure;
conn.Open();
command.Parameters.AddWithValue("@Prefix", prefixText+"%");
rdr = command.ExecuteReader();
string tmp = string.Empty;
Stock _stock = null;
while (rdr.Read() && curCount<count )
{
_stock = new Stock(rdr["Symbol"].ToString(), rdr["Name"].ToString(), rdr["Exchange"].ToString());
tmp = rdr["Symbol"] + "\t" + rdr["Name"];
items.Add(AutoCompleteExtender.CreateAutoCompleteItem(tmp, jss.Serialize(_stock)));
curCount++;
}
command.Parameters.Clear();
}
catch (Exception ex)
{
}
finally
{
if (rdr != null) rdr.Close();
if(conn!=null) conn.Close();
}
return items.ToArray();
}


Then, I add the javascript event, 'OnSymbolSelected',to the OnClientItemSelected event in the AutoComplete Extender:

<asp:TextBox ID="txtSymbols" runat="server" Width="300px"></asp:TextBox>
<cc1:AutoCompleteExtender ID="AutoCompleteExtender1" runat="server"
TargetControlID="txtSymbols" ServiceMethod="GetStocksInPrefix"
ServicePath="~/WebServiceAutoCompleteSymbol.asmx" MinimumPrefixLength="2"
CompletionListHighlightedItemCssClass="watermark" CompletionSetCount="10"
DelimiterCharacters=", "
CompletionListElementID="Symbol"
CompletionListItemCssClass="watermarkMatch"
EnableCaching="true" CompletionInterval="1000" OnClientItemSelected="OnSymbolSelected"
>
</cc1:AutoCompleteExtender>


Finally, add the javascript function to the aspx page. This function reads the Stock object into results, takes the SelectedText and gets the original string before the new text was completed by replacing the completed text with an empty string. If it is an additional field (that does not contain ' ' or ',', then only the id (symbol in this case) is added. Othewise, the original id plus the ' ' delimeter and the new id is added:

<script type="text/javascript" language="javascript">
function OnSymbolSelected(source, eventArgs)
{
var results = eval('(' + eventArgs.get_value() + ')');
if (results.symbol != null) {
var symbols = document.getElementById('<%= txtSymbols.ClientID %>').value;
var original = symbols.replace((results.symbol+'\t'+results.name),'');
if (original.indexOf(' ')>0 || original.indexOf(',')>0)
document.getElementById('<%= txtSymbols.ClientID %>').value = original + (' '+ results.symbol);
else
document.getElementById('<%= txtSymbols.ClientID %>').value = results.symbol;
}
}
</script>


References: http://ziqbalbh.wordpress.com/2008/06/11/google-like-autocomplete-suggestions/
http://www.tizag.com/javascriptT/javascript-string-replace.php
http://techron.blogspot.com/2008/04/reading-textbox-or-other-control-values.html

Using LIKE in SQL

Be careful in the order of parameters while using LIKE in T-SQL. The following stored procedure did not work @Prefix LIKE dbo.aspnet_Stocks.Name. It must be reversed:

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sproc_aspnet_GetStocksByPrefix]
@Prefix as varchar(50)=NULL
AS
BEGIN
SELECT *
FROM dbo.aspnet_Stocks
WHERE ((Symbol LIKE @Prefix) OR ( [Name]LIKE @Prefix))
RETURN 0
END

This was used in an AutoComplete AJAX control. The following reference describes the setup.

Reference: http://www.aspdotnetcodes.com/AutoComplete_From_Database.aspx

A more efficient stored procedure would use a FullText Catalog indexed on a Text Column as described here: http://support.microsoft.com/?kbid=916784

Tuesday, December 16, 2008

Sorting a GridView

Sorting a GridView with a DataSource requires an external storage variable for a the last SortDirection. First, I call the GridViewReport_Sorting in the Sorting event in the GridView. A GridView has e.SortExpression and e.SortDirection. I combine the two to form a unique Session variable and then set the SortDirection to compare at later sorts:

protected void GridViewReport_Sorting(object sender, GridViewSortEventArgs e)
{
DataTable dt;
DataView dv;
dt = (DataTable)Cache["dvStocks"];
dv = new DataView(dt);
string direction = SortDirection.Descending.ToString();
string curDirecton = SortDirection.Ascending.ToString(); // set to default
string key = string.Concat(e.SortExpression, e.SortDirection);
if(Session[key]!=null) {
curDirecton = (string)Session[key];
}
if (curDirecton.Contains("Ascending"))
{
direction = Global.DESC; //DESC
Session[key] = SortDirection.Descending.ToString();
}
else
{
direction = Global.ASC; //ASC
Session[key] = SortDirection.Ascending.ToString();
}
dv.Sort = e.SortExpression + " " + direction;
Cache["dtCalculated"] = dv.ToTable();
GridViewReport.DataSource = dv;
GridViewReport.DataBind();
}

Reference: http://forums.asp.net/t/825118.aspx