My colleague Kristof wrote an article about a Surface project he did for VTM (Belgian national television) and it was used during yesterdays' election show.
Here's some code for a server control "PropertyParameter" that you can use as a SelectParameter in your xyzDataSource that binds (one-way!) to a property value that is defined in code behind. This is something I need sometimes and isn't available out-of-the-box.
This method uses reflection and requires the property in code-behind to be public. There are two implementations, one for a Page and one for a UserControl. You will need to specify the Target property if you use it inside a UserControl.
This is how you use it in your markup:
<asp:ObjectDataSource ID="StatusObjectDataSource" runat="server"
TypeName="Components.WorkflowSecurity.WorkflowComponent"
SelectMethod="GetAvailableStatusesForEntityType">
<SelectParameters>
<controls:PropertyParameter Name="entityType"
PropertyName="EntityType" Target="Page" />
</SelectParameters>
</asp:ObjectDataSource>
Here is the the code for the server control:
public class PropertyParameter : Parameter
{
public enum PropertyTargetEnum
{
Page,
UserControl
}
public string PropertyName { get; set; }
public PropertyTargetEnum Target { get; set; }
public PropertyParameter()
{
Target = PropertyTargetEnum.Page;
}
protected override object Evaluate(
HttpContext context, Control control)
{
if (string.IsNullOrEmpty(PropertyName))
{
throw new ArgumentNullException("PropertyName");
}
Control targetControl = ResolvePropertyTarget(control);
Type pageType = targetControl.GetType();
PropertyInfo pi = pageType.GetProperty(PropertyName,
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic);
if (pi == null)
{
throw new InvalidOperationException(
string.Format("Property '{0}' not found on {1}.",
PropertyName, Target));
}
return pi.GetValue(targetControl, null);
}
private Control ResolvePropertyTarget(Control control)
{
switch (Target)
{
case PropertyTargetEnum.Page:
return control.Page;
case PropertyTargetEnum.UserControl:
return FindParentUserControl(control);
}
throw new ArgumentException("Invalid target type.");
}
private UserControl FindParentUserControl(Control control)
{
Control parent = control.Parent;
while (parent != null)
{
if (parent is UserControl)
{
return (UserControl)parent;
}
}
throw new ArgumentException(string.Format(
"'{0}' is not inside a UserControl.",
control.ID));
}
}
Btw, there are two common workarounds if you don't like this implementation:
- Store the value in a HiddenField and use a ControlParameter instead
- Add a Parameter control; implement the OnSelecting event of the DataSource to set the value on the InputParameters collection
Weird problem in ASP.NET again... With a weird solution:
In my apps I always have a folder UserControls where I put UserControls (not custom or server controls) that I use throughout the site. Examples are a date textbox with validation, formatted labels, upload fields, toggled image buttons, error message labels, etc. I register all these controls globally in the web.config pages section, like this:
<pages>
<controls>
<add tagPrefix="myapp" tagName="WebUserControl1"
src="~/UserControls/WebUserControl1.ascx"/>
<add tagPrefix="myapp" tagName="WebUserControl2"
src="~/UserControls/WebUserControl2.ascx"/>
</controls>
</pages>
The problem is that some user controls also use another user control from that same folder, which leads to the following ASP.NET parser error:
Parser Error Message: The page '/UserControls/WebUserControl1.ascx'
cannot use the user control '/UserControls/WebUserControl2.ascx', because it is
registered in web.config and lives in the same directory as the page.
The solution for that is to add a @Register directive to WebUserControl1.ascx like this:
<%@ Register TagPrefix="myapp" TagName="WebUserControl2" Src="~/UserControls/WebUserControl2.ascx" %>
But now I get the error from the blog post title, so if I add new controls to the user control, the designer file is not updated and I cannot access these in code behind (unless I update the designer file manually - NOT).
The key to resolve is to either pick a different TagPrefix or TagName in the @Register directive and use it like that, e.g.:
<%@ Register TagPrefix="myappLocal" TagName="WebUserControl2" Src="~/UserControls/WebUserControl2.ascx" %>
<myappLocal:WebUserControl2 runat="server" />
or
<%@ Register TagPrefix="myapp" TagName="WebUserControl2Local" Src="~/UserControls/WebUserControl2.ascx" %>
<myapp:WebUserControl2Local runat="server" />
Now the designer file keeps updating...
My (much more productive) colleague Davy beat me to post this problem we had today at work, so I'm gonna be lazy and link to him (and steal his title).
Btw yesterday we applied this "solution" to one of our biggest projects and -hooray- it worked...
Joseph Albahari created this great tool to execute LINQ (but also SQL) queries and review the results. In case of LINQ you also get to see the corresponding compiler translation and SQL query.
Some pro's:
-
Great for testing LINQ without having to build and debug your entire application in Visual Studio...
-
No installation (just exe)
-
Self updating
-
You can choose between C# and VB
-
Includes query organizer
-
...

Get it here: www.linqpad.net
PS: Also take the LINQ quiz here: www.albahari.com/nutshell/linqquiz.html
Here's a trick I often use in Visual Studio to speed up the trial-and-error process.
A disadvantage of using an ASP.NET Web Application Project instead of an ASP.NET Web Site is that you cannot edit code behind files (or basically any code file in your web project) while you are debugging. So to edit just minor things there, you need to stop the debugger (which closes the browser typically), edit, compile and start the debugger again, which could take some time.
I have written (recorded and adjusted) a small VS macro that attaches the debugger for me. It will actually look if the VS built-in webserver is active. If not it will attach to IIS.
Sub AttachDebugger()
Try
Dim dbg2 As EnvDTE80.Debugger2 = DTE.Debugger
Dim trans As EnvDTE80.Transport = dbg2.Transports.Item("Default")
Dim dbgeng(1) As EnvDTE80.Engine
dbgeng(0) = trans.Engines.Item("Managed")
Dim proc2 As EnvDTE80.Process2
Try
proc2 = dbg2.GetProcesses(trans, "").Item("WebDev.WebServer.EXE")
Catch ex As Exception
proc2 = dbg2.GetProcesses(trans, "").Item("w3wp.exe")
End Try
proc2.Attach2(dbgeng)
Catch ex As System.Exception
MsgBox(ex.Message)
End Try
End Sub
Copy this in one of your Macro modules and it should compile. The script could be a bit different on Windows XP, because the ASP.NET process is called "aspnet_wp" there...
And then assign shortcuts to your macro. For detaching you don't need a macro, it's a built-in command. My shortcut's are Ctrl+Shift+A for attaching and Ctrl+Shift+D for detaching.

Now you can detach your debugger (leaving the browser open), edit the code, build it (only build what you changed), attach and refresh the browser...
Here's a Vista feature some people have asked me about, the mysterious checkboxes... (Total Commander users: continue reading at your own risk :o)


How does it work?
-
The checkboxes appear when you have selected a file/folder or when you hover it
-
When you click on the file/folder name or icon, the selection changes to that file/folder (clearing the previous selection)
-
When you click the checkbox, the file/folder is added to the current selection (without holding Ctrl or Shift)
-
Works in any view mode (details, list, icons, thumbnails,...) although I haven't seen it in the command prompt...
Where to get it?
Now you'll never have to drop your sandwich again to hold the shift key!
I've been having this problem with ASP.NET themes that block my web applications from working. I always get this error when opening any page: CS0426: The type name 'Web' does not exist in the type 'ASP.Foo'.
When I set the default language to vb (web.config > compilation tag), the exception message is even more cryptic: BC30002: Type 'Foo.Web.WebControls.MyWebControl' is not defined.
The problem was that I always use the following project setup:
-
Foo.Web: class library project containing web controls, base master pages, base pages, base user controls,...
-
Foo.WebApplication: web application project that references Foo.Web. I also register the controls globally in web.config (<pages><controls><add tagPrefix="foo" assembly="Foo.Web" namespace="Foo.Web.WebControls" />...) but the problem is also there if you register your controls locally.
-
Then in Foo.WebApplication I have defined a theme named "Foo" (in the App_Themes folder). I configure this theme globally in web.config (<pages styleSheetTheme="Foo">) but that shouldn't matter where it is configured.
-
In that theme I have a skin file (or several) that style one of my web controls from the Foo.Web project.
The problem is that at runtime the theme Foo is compiled in a namespace ASP.Foo, and then it will look for the namespaces Web.WebControls below that one, which isn't what I want. The only solution that I found is renaming the theme to something different, e.g. FooStyle.
I'm not sure whether this problem can also occur if you have an ASP.NET website, but I guess
Operator overloading in C# is basically writing a public static method, like this:
public static bool operator ==(CustomObject object1, CustomObject object2)
{
return object1.SomeProperty == object2.SomeProperty;
}
Before you do this, it is necessary to check if both parameters are not null, otherwise you will get a NullReferenceException when doing this:
CustomObject object1 = GetCustomObject();
if (object1 == null)
But you cannot do the following because it will simply recurse the evaluation to the same method, and give you a StackOverflowException:
public static bool operator ==(CustomObject object1, CustomObject object2)
{
if (object1 == null || object2 == null)
{
//...
Instead you can cast your parameters to "object"s before evaluating, which will use the == operator for Object:
if ((object)object1 == null || (object)object2 == null)
Also, if you don't want to repeat the whole bunch again for the != operator, you can simply write this:
public static bool operator !=(CustomObject object1, CustomObject object2)
{
return !(object1 == object2);
}
I've been having this idea about a blog for a while now. Basically because I often run into a problem that I had before but then it's still hard to figure out how I fixed it back then. Now this will be my little public howto-database.
I'll try to post several bits a week about various topics of software development, but mainly about web development, ASP.NET, scripting,...
Thanks to Joe and the rest of the www.asp.net team for opening up some space for us...
More Posts