I recent was spinning out a quick Windows Form application to help an orders team look up product information and found myself questioning how to wrap text in a label rather than have it venturing off the application into nowheresville.
As a web developer, I always assume my text will find its place within the boundaries of a panel or table cell, whatever medium houses it. But the same should not be assumed for Windows Forms applications. Luckily, there is a simple solution.
Set the label's AutoSize property to Off.
The label becomes a resizable control that you can drag to whatever limits you want the text contained within.
Friday, August 20, 2010
Thursday, July 1, 2010
How Google Works
Ever wonder how Google really works? This is a fun little flow chart published by PPCBLOG that shows you how much goes on behind the plain white background and colorful graphic every second of every day and how fast the Google machine finds new websites, indexes them, rates them and presents them to users searching for a particular string on the internet.
Click Picture To Enlarge
Easy Way To Email Errors From Your ASP.Net Site To You
Errors on your site are never good, but it's best when you know about them the second they happen rather than a user telling you- or worse- not telling you.
This is a quick a dirty method I use for emailing myself details about errors when they occur on my sites.
If you don't already have one, add a Global Application Class (Global.asax) to your site. It should come with a sub procedure named Application_Error, but if it doesn't go ahead and create one. In the code I've provided below, I use a custom email class I use to templatize my emails, but you can easily adapt it to any email class you choose.
Obviously, you'll need to add your email address in and use either the default email class or another class if you'd prefer. You'll need the email to be set up in the web.config - which may be another good topic I could touch on another time. And then, it wouldn't hurt to go intentionally create an error. I made a blank page with the on load function that calls a function s() which doesn't exist:
Public Sub Form_Load() Handles Me.Load
s()
End Sub
This is a quick a dirty method I use for emailing myself details about errors when they occur on my sites.
If you don't already have one, add a Global Application Class (Global.asax) to your site. It should come with a sub procedure named Application_Error, but if it doesn't go ahead and create one. In the code I've provided below, I use a custom email class I use to templatize my emails, but you can easily adapt it to any email class you choose.
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
' Code that runs when an unhandled error occurs
Dim mm As New MMEmail()
mm.ToRecipients.Add("")
mm.Subject = "[ERROR] " & Request.Url.AbsoluteUri.ToString
Dim ex As Exception = Server.GetLastError().GetBaseException()
Dim body As String = ""
'Get URL where error occured
body &= "[URL]"
body &= "<br/>"
body &= Request.Url.AbsoluteUri.ToString
body &= "<br/>"
body &= "<br/>"
'Brief message of what went wrong and line where error occured in code.
body &= "[MESSAGE]"
body &= "<br/>"
body &= ex.Message.Replace(vbCrLf, "<br/>")
body &= "<br/>"
body &= "<br/>"
body &= "[SOURCE]"
body &= "<br/>"
body &= ex.Source.Replace(vbCrLf, "<br/>")
body &= "<br/>"
body &= "<br/>"
body &= "[STACK TRACE]"
body &= "<br/>"
body &= ex.StackTrace.Replace(vbCrLf, "<br/>")
body &= "<br/>"
body &= "<br/>"
mm.Body = body
mm.Send()
End Sub
Obviously, you'll need to add your email address in and use either the default email class or another class if you'd prefer. You'll need the email to be set up in the web.config - which may be another good topic I could touch on another time. And then, it wouldn't hurt to go intentionally create an error. I made a blank page with the on load function that calls a function s() which doesn't exist:
Public Sub Form_Load() Handles Me.Load
s()
End Sub
Labels:
Application_error,
asp.net,
email,
error handling,
errors,
Global.asax,
vb.net
If your VB.NET Application won't run on Windows 7
Despite being primarily a web developer, every now and then at my office, I need to create small little Windows Form based applications that perform one or two simple tasks to help out the every day processes of the staff.
Recently a couple people at our office were upgraded to Windows 7, and have experienced issues with those Visual Studio 2008 based applications, either during installation or running them after installing.
After some investigation, I stumbled upon the cause of the problem and a solution. Whether it is the machine or the operating system or gremlins sneaking around in this guy's new computer; for some reason, code compiled to work with the Target CPU value "AnyCPU" won't run on this machine.
To correct the issue, I opened the project settings in Visual Studio and clicked through to the Advanced Compiler Settings:
Project -> Compile -> Advanced Compile Options
Change the "Target CPU" field to "x86" and click OK.
Publish your application and it should install and run on the Windows 7 computer now.
Recently a couple people at our office were upgraded to Windows 7, and have experienced issues with those Visual Studio 2008 based applications, either during installation or running them after installing.
After some investigation, I stumbled upon the cause of the problem and a solution. Whether it is the machine or the operating system or gremlins sneaking around in this guy's new computer; for some reason, code compiled to work with the Target CPU value "AnyCPU" won't run on this machine.
To correct the issue, I opened the project settings in Visual Studio and clicked through to the Advanced Compiler Settings:
Project -> Compile -> Advanced Compile Options
Change the "Target CPU" field to "x86" and click OK.
Publish your application and it should install and run on the Windows 7 computer now.
Labels:
Compile,
Project,
Target CPU,
vb.net,
Visual Studio 2008,
Windows 7,
Windows Forms
Monday, June 28, 2010
How To Fill a GridView with Custom Objects in an Arraylist using a DataTable
Usually when I use a GridView, I pull and write to a database table, which is the easiest way to use them. All of the Edit and Delete commands are pretty much prepackaged together with the SqlDataSource control.
However, I recently found myself building a shopping cart tool that didn't touch a database until the user submits the order. I personally like to write to the database right away in case the client gets disconnected, but in this particular case, I didn't have that pleasure.
So, I needed to be able to pull an arraylist of objects of type ORDER (a custom class I build) out of the session variable SHOPPINGCART (another custom class I built) and feed them into a gridview for the user to edit quantity, remove items from the cart, etc before checking out.
I first put together my GridView:
I only want users to be able to edit the Quantity field and remove orders from the cart, so I've made all other columns readonly.
Now, to start populating my gridview, I'll need to create a DataTable and populate it with the fields that are relavant to what I'll be displaying.
Private _dt As Data.DataTable
Public Sub Form_Load() Handles Me.Load
CType(Master.FindControl("nav_order"), HtmlTableCell).Attributes.Add("class", "nav_highlight")
_dt = New Data.DataTable()
_dt.Columns.Add("OrderID")
_dt.Columns.Add("Quantity")
_dt.Columns.Add("Product Model")
_dt.Columns.Add("Total One Time Cost")
gvYourCart.DataSource = _dt
If Not IsPostBack Then
BindData()
End If
End Sub
Public Sub BindData()
For Each thisOrder As Order In CType(Session("ShoppingCart"), ShoppingCart).Orders
Dim newRow As Data.DataRow = _dt.NewRow()
newRow.Item("OrderID") = thisOrder.ID
newRow.Item("Quantity") = thisOrder.Quantity
newRow.Item("Product Model") = thisOrder.Product.Name
newRow.Item("Total One Time Cost") = FormatCurrency(thisOrder.OneTimeCost(), 2)
_dt.Rows.Add(newRow)
Next
gvYourCart.DataBind()
End Sub
Because I know that I will need to bind data to my gridview every time I edit or delete from the control, I went ahead and created a BindData function that will pull data from my Shopping Cart in sessions and tie its data to the DataTable.
In order to edit and delete rows of my GridView, I will need to create 4 new functions (these are usually handled by the UpdateCommand and DeleteCommand of the SqlDataSource, but we'll need to build our own since we're doing things a little more custom than usual.
Add the following tags to the GridView control:
AutoGenerateEditButton="true"
AutoGenerateDeleteButton="true"
OnRowEditing="EditQty"
OnRowCancelingEdit="CancelEditQty"
OnRowUpdating="UpdateEditQty"
OnRowDeleting="DeleteOrder"
Now, at this point your code may look nothing like mine, but this should give you a pretty good start.
The EditQty sub procedure will pretty much always look like this. All we are doing is telling the GridView which row we will be editing. Because we set some columns as ReadOnly, they will not be editable.
Public Sub EditQty(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs)
gvYourCart.EditIndex = e.NewEditIndex
BindData()
End Sub
Again, the CancelEditQty sub procedure is pretty standard. We are just letting the gridview know that we don't want to edit any rows by setting the EditIndex to -1.
Public Sub CancelEditQty()
gvYourCart.EditIndex = -1
BindData()
End Sub
Because I didn't allow for sorting on my GridView, I know that the shopping cart's order are displayed on the GridView in the same order as they are stored in the shopping cart, so I can afford to make the assumption that the index of the ArrayList in my ShoppingCart object's Orders property matches the e.RowIndex of the GridView, but be careful of how you make your edits.
Public Sub UpdateEditQty(ByVal sender As Object, ByVal e As GridViewUpdateEventArgs)
Dim thisRow As GridViewRow = gvYourCart.Rows(e.RowIndex())
CType(CType(Session("ShoppingCart"), ShoppingCart).Orders(e.RowIndex), Order).Quantity = CType(thisRow.Cells(1).Controls(0), TextBox).Text
gvYourCart.EditIndex = -1
BindData()
End Sub
Ditto.
Public Sub DeleteOrder(ByVal sender As Object, ByVal e As GridViewDeleteEventArgs)
CType(Session("ShoppingCart"), ShoppingCart).Orders.RemoveAt(e.RowIndex())
BindData()
End Sub
And that is it! You should now be able to view the contents of your ShoppingCart or other ArrayList in GridView form, and make easy edits and deletes.
However, I recently found myself building a shopping cart tool that didn't touch a database until the user submits the order. I personally like to write to the database right away in case the client gets disconnected, but in this particular case, I didn't have that pleasure.
So, I needed to be able to pull an arraylist of objects of type ORDER (a custom class I build) out of the session variable SHOPPINGCART (another custom class I built) and feed them into a gridview for the user to edit quantity, remove items from the cart, etc before checking out.
I first put together my GridView:
<asp:GridView
ID="gvYourCart"
DataKeyNames="OrderID"
AutoGenerateColumns="false"
runat="server">
<Columns>
<asp:BoundField
HeaderText="Quantity"
DataField="Quantity" />
<asp:BoundField
HeaderText="Device Model"
DataField="Device Model"
ReadOnly="true" />
<asp:BoundField
HeaderText="Rate Plan"
DataField="Rate Plan"
ReadOnly="true" />
<asp:BoundField
HeaderText="Total One Time Cost"
DataField="Total One Time Cost"
ReadOnly="true" />
<asp:BoundField
HeaderText="Total Recurring Costs"
DataField="Total Recurring Costs"
ReadOnly="true" />
</Columns>
</asp:GridView>
I only want users to be able to edit the Quantity field and remove orders from the cart, so I've made all other columns readonly.
Now, to start populating my gridview, I'll need to create a DataTable and populate it with the fields that are relavant to what I'll be displaying.
Private _dt As Data.DataTable
Public Sub Form_Load() Handles Me.Load
CType(Master.FindControl("nav_order"), HtmlTableCell).Attributes.Add("class", "nav_highlight")
_dt = New Data.DataTable()
_dt.Columns.Add("OrderID")
_dt.Columns.Add("Quantity")
_dt.Columns.Add("Product Model")
_dt.Columns.Add("Total One Time Cost")
gvYourCart.DataSource = _dt
If Not IsPostBack Then
BindData()
End If
End Sub
Public Sub BindData()
For Each thisOrder As Order In CType(Session("ShoppingCart"), ShoppingCart).Orders
Dim newRow As Data.DataRow = _dt.NewRow()
newRow.Item("OrderID") = thisOrder.ID
newRow.Item("Quantity") = thisOrder.Quantity
newRow.Item("Product Model") = thisOrder.Product.Name
newRow.Item("Total One Time Cost") = FormatCurrency(thisOrder.OneTimeCost(), 2)
_dt.Rows.Add(newRow)
Next
gvYourCart.DataBind()
End Sub
Because I know that I will need to bind data to my gridview every time I edit or delete from the control, I went ahead and created a BindData function that will pull data from my Shopping Cart in sessions and tie its data to the DataTable.
In order to edit and delete rows of my GridView, I will need to create 4 new functions (these are usually handled by the UpdateCommand and DeleteCommand of the SqlDataSource, but we'll need to build our own since we're doing things a little more custom than usual.
Add the following tags to the GridView control:
AutoGenerateEditButton="true"
AutoGenerateDeleteButton="true"
OnRowEditing="EditQty"
OnRowCancelingEdit="CancelEditQty"
OnRowUpdating="UpdateEditQty"
OnRowDeleting="DeleteOrder"
Now, at this point your code may look nothing like mine, but this should give you a pretty good start.
The EditQty sub procedure will pretty much always look like this. All we are doing is telling the GridView which row we will be editing. Because we set some columns as ReadOnly, they will not be editable.
Public Sub EditQty(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs)
gvYourCart.EditIndex = e.NewEditIndex
BindData()
End Sub
Again, the CancelEditQty sub procedure is pretty standard. We are just letting the gridview know that we don't want to edit any rows by setting the EditIndex to -1.
Public Sub CancelEditQty()
gvYourCart.EditIndex = -1
BindData()
End Sub
Because I didn't allow for sorting on my GridView, I know that the shopping cart's order are displayed on the GridView in the same order as they are stored in the shopping cart, so I can afford to make the assumption that the index of the ArrayList in my ShoppingCart object's Orders property matches the e.RowIndex of the GridView, but be careful of how you make your edits.
Public Sub UpdateEditQty(ByVal sender As Object, ByVal e As GridViewUpdateEventArgs)
Dim thisRow As GridViewRow = gvYourCart.Rows(e.RowIndex())
CType(CType(Session("ShoppingCart"), ShoppingCart).Orders(e.RowIndex), Order).Quantity = CType(thisRow.Cells(1).Controls(0), TextBox).Text
gvYourCart.EditIndex = -1
BindData()
End Sub
Ditto.
Public Sub DeleteOrder(ByVal sender As Object, ByVal e As GridViewDeleteEventArgs)
CType(Session("ShoppingCart"), ShoppingCart).Orders.RemoveAt(e.RowIndex())
BindData()
End Sub
And that is it! You should now be able to view the contents of your ShoppingCart or other ArrayList in GridView form, and make easy edits and deletes.
Monday, June 14, 2010
AJAX and URL Rewrite: Sys is undefined Issue
I created a website on a subfolder of my domain, and later moved it to a subdomain as its own application. Oddly, after I did this, I saw an issue pop up wherein javascript errors were thrown every time I opened a page that:
Message: Syntax error
Line: 3
Char: 1
Code: 0
URI: http://(domain)/WebResource.axd?d=3sB1WgLxUgrovkMyz-aqWw4&t=633626442871064790
... along with 2 errors for the ScriptResource.axd file and 2 instances of a 'Sys' is undefined error.
After some research, I found that this is caused by the combination of AJAX and URL Rewrite. I'm not sure if it is specific to GoDaddy hosting, or global. It's strange that it didn't surface until I moved my project. Regardless, I have found a solution.
The major problem is that my application is not recognizing the WebResource.axd and ScriptResource.axd as files; therefore tying to rewrite their urls using them as keywords.
My current URL Rewrite structure looks like this:
I'm already checking for files and directories, but now I need to check for these .axd files before casting a url into the rewriter. Therefore, I'll use the bounce URLs off a pattern *.axd to negate them from being processed by the Rewrite function.
I re-ran my application and every worked like a charm!
Message: Syntax error
Line: 3
Char: 1
Code: 0
URI: http://(domain)/WebResource.axd?d=3sB1WgLxUgrovkMyz-aqWw4&t=633626442871064790
... along with 2 errors for the ScriptResource.axd file and 2 instances of a 'Sys' is undefined error.
After some research, I found that this is caused by the combination of AJAX and URL Rewrite. I'm not sure if it is specific to GoDaddy hosting, or global. It's strange that it didn't surface until I moved my project. Regardless, I have found a solution.
The major problem is that my application is not recognizing the WebResource.axd and ScriptResource.axd as files; therefore tying to rewrite their urls using them as keywords.
My current URL Rewrite structure looks like this:
<rewrite>
<rules>
<rule name="RewriteCheck" stopProcessing="true">
<match url="^(.+)$" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}"
matchType="IsFile"
negate="true" />
<add input="{REQUEST_FILENAME}"
matchType="IsDirectory"
negate="true" />
</conditions>
<action type="Rewrite" url="UserProfile.aspx?key={R:0}" />
</rule>
</rules>
</rewrite>
I'm already checking for files and directories, but now I need to check for these .axd files before casting a url into the rewriter. Therefore, I'll use the bounce URLs off a pattern *.axd to negate them from being processed by the Rewrite function.
<rewrite>
<rules>
<rule name="RewriteCheck" stopProcessing="true">
<match url="^(.+)$" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}"
matchType="IsFile"
negate="true" />
<add input="{REQUEST_FILENAME}"
matchType="IsDirectory"
negate="true" />
<add input="{URL}"
pattern=".*.axd$"
negate="true" />
</conditions>
<action type="Rewrite" url="UserProfile.aspx?key={R:0}" />
</rule>
</rules>
</rewrite>
I re-ran my application and every worked like a charm!
Labels:
ajax,
asp.net,
errors,
godaddy,
url rewrite,
vanity urls,
vb.net,
web.config
Wednesday, June 2, 2010
Vanity URLs with GoDaddy Hosting using URL Rewrite
GoDaddy is a very affordable web hosting provider and domain registrar. I often recommend them to most of my clients who don't already have hosting set up. Unfortunately, GoDaddy does not allow you to access the IIS panel directly which sometimes makes a simple task slightly more complex as you have to navigate through their hosting control center, or sometimes entirely unfeasible. However, for the most part, they provide you with everything you would need to host most websites for a nominal fee.
I've recently had the need to create a portal that used vanity urls for each user profile on the site. For instance, if Aaron, Bob and Carl all have profiles on the portal, their respective portals may be:
Aaron: digitalplaydoh.com/profile.aspx?id=2
Bob: digitalplaydoh.com/profile.aspx?id=5
Carl: digitalplaydoh.com/profile.aspx?id=31
The client would like users to be able to select a unique ID for their account that can be typed out after the domain that will take users straight to their profile, similar to what you would find on Facebook or MySpace. ie:
Aaron: digitalplaydoh.com/aaronsmith
Bob: digitalplaydoh.com/bigbob
Carl: digitalplaydoh.com/carl

Microsoft's URL Rewrite module comes preinstalled on GoDaddy accounts with IIS 7.0. Here's where things get a little unfortunate for the GoDaddy developer. While running IIS 7.0, you will be unable to use Front Page Extensions, which means if you're using Visual Studio, you'll need to now connect to the site with the FTP method. Not terrible, but definitely a little slower.
Once you're running IIS 7.0, we're ready to start using the Microsoft URL Rewrite module. Open your web.config file and go to the <system.webServer> and add the following:
This allows us to find any path that is not a file or directory, and redirect it to Profile.aspx?key=(path). This means I need to now make sure that Profile.aspx can support me passing a "key" request parameter in instead of an "id", which is just a little work in how we pull from the database, but we've otherwise now go the ability to go to http://digitalplaydoh.com/carl and get Carl's profile page.
Note that you also need to be ready for an incorrect "key". Should I accidently type http://digitalplaydoh.com/calr, you need to be able to catch this as a incorrect keyword and show a proper error page.
I've recently had the need to create a portal that used vanity urls for each user profile on the site. For instance, if Aaron, Bob and Carl all have profiles on the portal, their respective portals may be:
Aaron: digitalplaydoh.com/profile.aspx?id=2
Bob: digitalplaydoh.com/profile.aspx?id=5
Carl: digitalplaydoh.com/profile.aspx?id=31
The client would like users to be able to select a unique ID for their account that can be typed out after the domain that will take users straight to their profile, similar to what you would find on Facebook or MySpace. ie:
Aaron: digitalplaydoh.com/aaronsmith
Bob: digitalplaydoh.com/bigbob
Carl: digitalplaydoh.com/carl

Microsoft's URL Rewrite module comes preinstalled on GoDaddy accounts with IIS 7.0. Here's where things get a little unfortunate for the GoDaddy developer. While running IIS 7.0, you will be unable to use Front Page Extensions, which means if you're using Visual Studio, you'll need to now connect to the site with the FTP method. Not terrible, but definitely a little slower.
Once you're running IIS 7.0, we're ready to start using the Microsoft URL Rewrite module. Open your web.config file and go to the <system.webServer> and add the following:
<system.webServer>
<rewrite>
<rules>
<rule name="RewriteCheck" stopProcessing="true">
<match url="^(.*)$" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}"
matchType="IsFile"
negate="true" />
<add input="{REQUEST_FILENAME}"
matchType="IsDirectory"
negate="true" />
</conditions>
<action type="Rewrite" url="Profile.aspx?key={R:0}" />
</rule>
</rules>
</rewrite>
</system.webServer>
This allows us to find any path that is not a file or directory, and redirect it to Profile.aspx?key=(path). This means I need to now make sure that Profile.aspx can support me passing a "key" request parameter in instead of an "id", which is just a little work in how we pull from the database, but we've otherwise now go the ability to go to http://digitalplaydoh.com/carl and get Carl's profile page.
Note that you also need to be ready for an incorrect "key". Should I accidently type http://digitalplaydoh.com/calr, you need to be able to catch this as a incorrect keyword and show a proper error page.
Labels:
asp.net,
facebook,
godaddy,
hosting,
myspace,
url rewrite,
vanity urls,
vb.net,
web.config