Janice went all out this year and got me an Ibanez JS1000 (Joe Satriani series) guitar, a Line 6 POD X3 Live effects board and a pair of Roland CM-30 amplified monitors. My fingers are all tore up now since I've been out of practice for some time now. But it sure is fun to get back to some jamming. The JS1000 is pretty light and has easy action. Combined with the POD X3 I can get quite a variety of amazing sounds. I even got the X3 hooked up to my MacBook Pro and finally was able to try out Garage Band. I was able to lay down the rhythm track for Crushing Day (what I could remember from back in the day) and then play the lead part over it with no lag. The roland CM-30s are nice because I can run my Alesis QS8 and the POD X3 into them at the same time. This is probly the best setup I've ever had.
Later today I hooked up a microphone to the X3 and the kids had a blast talking and play singing into it. "Daddy it sounds kinda like I'm in a cave...". Perhaps I should cut down some of that reverb. :)
Santa Clause was good to me this year. (Thanks Janice)
With a simple extension method to ControlCollection to flatten the control tree you can use LINQ to query the control tree:
public static class PageExtensions
{
public static IEnumerable<Control> All(this ControlCollection controls)
{
foreach (Control control in controls)
{
foreach (Control grandChild in control.Controls.All())
yield return grandChild;
yield return control;
}
}
}
Now I can do things like this:
// get the first empty textbox
TextBox firstEmpty = accountDetails.Controls
.All()
.OfType<TextBox>()
.Where(tb => tb.Text.Trim().Length == 0)
.FirstOrDefault();
// and focus it
if (firstEmpty != null)
firstEmpty.Focus();
Pretty cool! I can do all sorts of querying of the control tree now. LINQ you are my hero.
The ASP.NET validators have this nice property called "SetFocusOnError" that is supposed to set the focus to the first control that failed validation. This all works great until your validator control is inside a naming container. I ran into this recently when using validators in a DetailsView. Take this simple example:
<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
DataBind();
}
</script>
<!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="_frm" runat="server">
<asp:DetailsView
ID="dv1"
DefaultMode="Edit"
DataSource='<%# new object[1] %>'
runat="server"
>
<Fields>
<asp:TemplateField HeaderText="First Name:">
<EditItemTemplate>
<asp:TextBox ID="FirstNameTextBox" runat="server" />
<asp:RequiredFieldValidator
ID="FirstNameValidator1"
ControlToValidate="FirstNameTextBox"
ErrorMessage="First name is required."
Display="Dynamic"
EnableClientScript="false"
SetFocusOnError="true"
ValidationGroup="bug"
Text="*"
runat="server"
/>
</EditItemTemplate>
</asp:TemplateField>
</Fields>
<FooterTemplate>
<asp:ValidationSummary
ID="vs1"
DisplayMode="List"
ValidationGroup="bug"
runat="server"
/>
<asp:Button
ID="Button1"
Text="Post Back"
ValidationGroup="bug"
runat="server"
/>
</FooterTemplate>
</asp:DetailsView>
</form>
</body>
</html>
If you run this page and do a view source you'll see that the FirstNameTextBox gets rendered like this:
<input name="dv1$FirstNameTextBox" type="text" id="dv1_FirstNameTextBox" />
If you just do a post back without entering a value to cause the validator to fail it will output this line of java script in an attempt to set the focus to the invalid element:
WebForm_AutoFocus('FirstNameTextBox');
See anything wrong with this? It would seem that the validators just use the string value you typed in for the ControlToValidate property rather than doing a FindControl and using the UniqueID. This is exactly what happens and I verified it with reflector. The Validate method on BaseValidator does this:
if ((!this.IsValid && (this.Page != null)) && this.SetFocusOnError)
{
this.Page.SetValidatorInvalidControlFocus(this.ControlToValidate);
}
If you follow the call to SetValidatorInvalidControlFocus you'll see that it never resolves the full UniqueID of the control that its going to set focus to.
Ok, so this sucks. How do I work around it. My solution was to simply ditch using the SetFocusOnError property and implement the focus logic myself which is actually pretty easy. I overrode Validate method on my Page like this:
public override void Validate(string group)
{
base.Validate(group);
// find the first validator that failed
foreach (IValidator validator in GetValidators(group))
{
if (validator is BaseValidator && !validator.IsValid)
{
BaseValidator bv = (BaseValidator)validator;
// look up the control that failed validation
Control target =
bv.NamingContainer.FindControl(bv.ControlToValidate);
// set the focus to it
if (target != null)
target.Focus();
break;
}
}
}If your using C# 3 this is even easier using LINQ:
public override void Validate(string group)
{
base.Validate(group);
// get the first validator that failed
var validator = GetValidators(group)
.OfType<BaseValidator>()
.FirstOrDefault(v => !v.IsValid);
// set the focus to the control
// that the validator targets
if (validator != null)
{
Control target = validator
.NamingContainer
.FindControl(validator.ControlToValidate);
if (target != null)
target.Focus();
}
}
I hope this saves someone the headache of tracking this down.
I try to follow a coding standard for organizing my using statements. System.* goes at the top and then other namespaces grouped together like this:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Web;
using System.Web.Script.Services;
using System.Web.Services;
using System.Web.Services.Protocols;
using Microsoft;
using Microsoft.CSharp;
using MyCompany;
using MyCompany.Web;
I finally got tired enough of keeping this all sorted out that I made a VS.NET macro to do it for me. This macro will take the current selection, parse it for using statements, group and sort them like the above example.
Here's the macro code:
Dim _usingPattern As Regex = New Regex( _
"\s*(?<using>using\s*(?<group>\w+)[^;]*);", _
RegexOptions.IgnoreCase _
Or RegexOptions.Multiline _
Or RegexOptions.ExplicitCapture _
Or RegexOptions.CultureInvariant _
Or RegexOptions.Compiled _
)
Public Sub SortUsing()
If Not DTE.ActiveDocument Is Nothing Then
Dim sel As TextSelection = DTE.ActiveDocument.Selection
If sel.Text.Contains(vbCrLf) Then
If sel.ActivePoint Is sel.BottomPoint Then sel.SwapAnchor()
sel.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn, True)
sel.SwapAnchor()
sel.EndOfLine(True)
Dim groups As New SortedList(Of String, List(Of String))()
For Each match As Match In _usingPattern.Matches(sel.Text)
Dim u As String = match.Groups("using").Value
Dim g As String = match.Groups("group").Value
' System usings sort at the top
If g = "System" Then g = "_" + g
Dim list As List(Of String)
If Not groups.TryGetValue(g, list) Then
list = New List(Of String)()
groups.Add(g, list)
End If
list.Add(u)
Next
Dim builder As New StringBuilder()
For Each group As KeyValuePair(Of String, List(Of String)) In groups
If builder.Length > 0 Then builder.AppendLine()
group.Value.Sort()
For Each line As String In group.Value
builder.Append(line)
builder.AppendLine(";")
Next
Next
sel.DeleteLeft()
sel.Insert(builder.ToString())
End If
End If
End Sub
Scenario: You have two web applications www.mydomain.com and login.mydomain.com. The login site provides a centralized login application and www contains any number of web applications that should use the auth ticket issued by the login site.
The auth ticket can be setup to be shared across the two 3rd level domains no problem. The problem with this setup is that when the user requests a page on www and gets redirected to login the ReturnUrl query string parameter contains a relative path. As far as I know there are not any extensibility points on the FormsAuthenication or FormsAuthenticationModule classes that you can use to fix this. A quick and dirty fix is to use the EndRequest event in your global.asax like this:
1: protected void Application_EndRequest(object sender, EventArgs e)
2: { 3: string redirectUrl = this.Response.RedirectLocation;
4: if (!string.IsNullOrEmpty(redirectUrl))
5: { 6: this.Response.RedirectLocation = Regex.Replace(redirectUrl, "ReturnUrl=(?'url'.*)", delegate(Match m)
7: { 8: string url = HttpUtility.UrlDecode(m.Groups["url"].Value);
9: Uri u = new Uri(this.Request.Url, url);
10: return string.Format("ReturnUrl={0}", HttpUtility.UrlEncode(u.ToString())); 11: }, RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
12: }
13: }
The basic idea is to intercept the redirect and process the returnurl query string parameter with a regex. This could also be wrapped up in it's own HttpModule. It's kind of cheezy I know but it seems to work.
I needed to create a new machine key for an asp.net site. I found a couple of command line utils out on the web that would create a new key but I thought it would be easier to just have it avail in VS.NET. So, I threw together this little macro that will generate the machine key and insert it. Just run the macro while you have you web.config open in VS.NET. If you already have a machinekey it will find it and replace it. If not it will just add it right after the <system.web> node. It should do the proper indents and everything too.
1: Imports System
2: Imports EnvDTE
3: Imports EnvDTE80
4: Imports System.Diagnostics
5:
6: Public Module AspNetUtils
7:
8: #Region "Helper Code"
9:
10: Dim _rng As New System.Security.Cryptography.RNGCryptoServiceProvider()
11:
12: Private Function GetRandomData(ByVal size As Integer) As Byte()
13: Dim randomData(size) As Byte
14: _rng.GetBytes(randomData)
15: Return randomData
16: End Function
17:
18: Private Function ToHex(ByVal data() As Byte) As String
19: Dim sb As New System.Text.StringBuilder()
20: For i As Integer = 0 To data.Length - 1
21: sb.AppendFormat("{0:X2}", data(i)) 22: Next
23: Return sb.ToString()
24: End Function
25:
26: Private Sub WriteNewMachineKey(ByVal sel As TextSelection)
27: sel.Insert("<machineKey") 28: sel.NewLine()
29: sel.Insert(String.Format("validationKey='{0}'", ToHex(GetRandomData(63)))) 30: sel.NewLine()
31: sel.Insert(String.Format("decryptionKey='{0}'", ToHex(GetRandomData(23)))) 32: sel.NewLine()
33: sel.Insert("decryption='SHA1'") 34: sel.NewLine()
35: sel.Insert("/>", vsInsertFlags.vsInsertFlagsContainNewText) 36: sel.Unindent()
37: sel.Collapse()
38: End Sub
39:
40: #End Region
41:
42: #Region "Macros"
43:
44: Public Sub GenerateMachineKey()
45: If Not DTE.ActiveDocument Is Nothing AndAlso DTE.ActiveDocument.Name.ToLower() = "web.config" Then
46: Dim sel As TextSelection = DTE.ActiveDocument.Selection
47:
48: If sel.FindPattern("\<machineKey((:b|\n)*:i=:q)*(:b|\n)*/\>", vsFindOptions.vsFindOptionsFromStart Or vsFindOptions.vsFindOptionsMatchInHiddenText Or vsFindOptions.vsFindOptionsRegularExpression) Then 49: ' Replace an existing <machineKey /> element
50: sel.Delete()
51: WriteNewMachineKey(sel)
52: ElseIf sel.FindText("<system.web>", vsFindOptions.vsFindOptionsFromStart Or vsFindOptions.vsFindOptionsMatchInHiddenText) Then 53: ' insert the new machineKey just after the <system.web> element
54: sel.Collapse()
55: sel.NewLine()
56: WriteNewMachineKey(sel)
57: Else
58: ' no <system.web> element found so just collapse the current selection and insert
59: ' the new key at the cursor location
60: sel.Collapse()
61: WriteNewMachineKey(sel)
62: End If
63: End If
64: End Sub
65:
66: #End Region
67:
68: End Module
We recently launched our video tutorial site for Expression Web Designer. Dustin, our resident graphic artist is pumping out the video tutorials as well as designing the site using the tool. It's good to have some how to vids from a designers point of view. Watch this site throughout 2007 as he continues to expand the content.
Scott gave a whirlwind presentation to a standing room only crowd at the North Dallas Dot Net User Group tonight. A wide range of topics were covered from IDE tips and tricks to ASP.NET tips to MS AJAX to LINQ and DLINQ (I still like to call it DLINQ rather than LINQ to SQL). I'm still not sure how all this got packed into a little over 2 hours. :)
This was the first meeting that I've attended at the new Intuit location. It was quite an upgrade in venue from the previous SMU campus location. Lots of projector screens and flat panels so it was very easy to follow along even if you were in the nose bleed section.
While there's a ton of cool things going on with AJAX, seeing LINQ in action again reminds me of just how revolutionary LINQ is.
So, thanks for the great presentation Scott and don't be a stranger to Dallas! :)
I just ran across this over on parallels site:
http://parallelsvirtualization.blogspot.com/2006/11/installing-windows-on-mac-is-now.html
This sounds like a great feature for their virtualization product. Parallels for OSX is a top notch virtualization product and they just keep on adding features.
We recently had this question posted to our forums over at LVS:
Dear Forum Experts:
I am looking for very specialized solution:
I have various Items which I store into a table in a Relational DB.
I would like to do a custom calculation, specific for each item at it's instance. Because the calculation is specific for the item, and items are soo many I wold like to store the calculation formula into a relational DB. The problem is to convert the string of formula into a real programming command and to actually perform the calculation. I do not want to use Excel or additional software in order to gain calculation speed e.g.
ItemID = 5001, ItemSize = "a - b"
ItemID = 5002, ItemSize = "a - 2*b"
ItemID = 5003, ItemSize = "a + b"
So, ItemSize is actually the formula expression that would calculate various instances of a and b variables ... I have tryed this:
int a = 10;
int b = 5;
string formula = "a + b" // This comes from ItemSIze of DB,SQL, etc.
int Result = a + b; // This is a second line for test only - hard coded...
int CalcResult = int.Parse(formula); //I wish this was working ...
MessageBox.Show(Result.ToString()); // This works ...
MessageBox.Show(CalcResult.ToString()); // Never got that far.
The result will be stored in different DB with the instances of a and b.
Could you please post any information on how should I approach this problem.
Thanks a lot.
Several options immediately came to mind: code up a simple expression interpreter, evaluate the expression with dynamic SQL (yuck), use lightweight code gen. Then I remembered this little thing I saw at last years PDC called IronPython. Solving this problem with IronPython was "like butta".
using System;
using System.Collections.Generic;
using System.Text;
using IronPython.Hosting;
namespace PythonDemo
{
class Program
{
delegate int MyExpressionDelegate(int a, int b);
static void Main(string[] args)
{
PythonEngine pe = new PythonEngine();
MyExpressionDelegate expression =
pe.CreateLambda<MyExpressionDelegate>("a + b");
int a = 10;
int b = 5;
int c = expression(a,b);
Console.WriteLine(c);
}
}
}
That's all there was to it! The API for the PythonEngine was very intuitive. I could immediately see where and how I could integrate this with any number of applications that I've worked on in the past. Tip of the hat to the IronPython guys!
Now I haven't tested this against a simple interpreter but I would imagine as long as you are smart and keep a cache of the expressions and don't re-parse them every time that it would perform just as well as any interpreted solution if not better. Just follow the make it work, make it work right and make it work fast model and you'll be ok.
I wonder if this would also be possible by referenceing the PowerShell runtime. I'll have to take a look at that next and see how it compares.
P.S. Microsoft, if you're listening, please include IronPython in the Orcas/NETFX3.5 release! :) I'd love to see IDE support for python scripts and such.
More Posts
Next page »