|
|
These blog entries are written by industry experts and leaders. We consider this content to be a good read for any software developer or web technologist.
August 2004 - Posts
-
Most databound controls are written from the perspective that they will be databound to something, but the control writer doesn’t know what that would be. It is assumed that the control’s container will set the datasource and databind it. Therefore the same container knows about and handles the datasource, databinding, viewstate usages, event handling, etc. This works great. But then application developers come along and want self contained controls, like, say… A composite control which contains a DropDownList that always contains items databound from a database, and a button that performs magic on those items. The app developer of course calls on his local control guru to make him this control. This gives us 3 different people with 3 very different perspectives. There is the app developer, the custom control developer, and the asp.net team control developer who made the DropDownList control. The asp.net team guy does the Right Thing in exposing a DataSource property which DataBind uses, and an EnableViewState property which says whether or not the items created should be serialized to ViewState. He’s thinking, “If a consumer of my control does want to use viewstate, they can just bind on every request.” This is a reasonable position. Then on the other end we have the app developer who is using the custom control. He had the custom control made exactly BECAUSE he doesn’t want to think about datasources and databinding and such. However, the trickiness presents itself because he does still want to be in control of ViewState usage. Maybe some specific page is hit so often that ViewState should be disabled to save on bandwidth. “My custom control should just get the data every hit, I can cache at the data-access level to make up for it.” This is also a reasonable position. So now the custom control developer is in a bit of a pickle. The break in the chain is that the lower levels assume that one person would be both databinding and deciding on ViewState policy. That isn’t the case here. Ignoring the ViewState issue for a second, the straightforward solution to this is that the composite control does a “if not postback then databind” thing during Load, and override its DataBind method to databind its child DropDownList to the database. But then what happens if the app developer sets EnableViewState to false? Obviously the control is broken. So the control dev needs to databind during load in both first-request and disabled-viewstate states. This mostly solves the problem; however there is one last problem that I just don’t see a good solution to. Think of the case where an app dev sets the EnableViewState property to false during PreRender. In this case the Load-time DataBind never runs on subsequent requests even though there won’t be ViewState to load and thus the control is broken. Now don’t think that I imagine this is a common scenario, but it’s unfortunate. The only thing I can think is that you’d have to push the responsibility of DataBind() onto the app dev in this case. It’s not something I particularly like, because I think the app dev shouldn’t have to worry about the datasources of controls contained by it’s composite control children. However, it’s the best I’ve come up with so far. Any other ideas?

|
-
So one change in IE that came with sp2, that I haven't seen anybody comment on, is that about:mozilla is gone. Before I installed sp2, i had my homepage set to about:mozilla because it gave a nice colored window as the default. But now all it does it show the word "mozilla" on a white background. If you are annoyed that your nice default html is no longer around, there is a little trick that not many people know, which is that you can put any html you want in an about url, and IE will show it. So just write a simple html page that displays any color/design you want and set that text as your homepage.

|
-
Of all the features cut from asp.net v2, the only one that I'm really going to miss is the loss of DyanmicImage and the Image Generation Service. Not so much because it's a little harder to do dynamic images in my app without them, but because it's much harder to do dynamic images in my 3rd party server controls without them. The fact that you will be able progmatticly register custom IHttpHandlers in web.config mitigates the situation a bit, but not enough.

|
-
It's not often that a contractor says, "wow, I’m so incredibly happy about my latest client", but I see myself in that position. And seeing that I have a voice, I just need to tell y’all. I recently signed up to help out Telligent with their Community Server: Forums project. I can’t express how big the smile on my face was when I started working with Rob, Jason, Scott, and Terry Denham. You know, when I started MetaBuilders as a business, I had plans that that selling server control products would be my main business, but the bottom line has lead me to selling my services as an asp.net/control guru, helping others get their business done. It’s worked out pretty well for me, but I’ve never been more excited about work than I am right now. You’ll have to excuse me for the complete lack of useful technical information in this post… I know that’s what regular readers are used to seeing… but this is really such a high point in my recent history that I really need to publish it.

|
-
I've had these control dev macros sitting around for a while, and I thought I'd share them finally. First I looked for a wiki I could post them to, but I was surprised to not find one for vstudio addins/macros/whatnot. The closest I found was the gotdotnet developer powertoy site, but the whole submission/wait-for-approval thing just bores me. So, I decided to post the macro file here. I hope you find them useful:
Imports EnvDTE
Imports System
Imports System.Diagnostics
Imports System.IO
Imports System.Text
Public Module CodeGen
' Creates A Public Style Property
' Write the following line into the code area:
' <StyleType> <PropertyName>
' Select the text, run this macro
Public Sub CreateStyleProperty()
CheckLanguage()
Dim ts As TextSelection = DTE.ActiveDocument.Selection
Dim selection As String = ts.Text.Replace(vbTab, " ").Trim(" ")
Dim words() As String = selection.Split(" ")
If words.Length <> 2 Then
Return
End If
Dim typeName As String = words(0)
Dim propertyName As String = words(1)
Dim memberName As String = "_" & propertyName.Substring(0, 1).ToLower() & propertyName.Substring(1)
Dim code As New StringBuilder
Dim codeWriter As New StringWriter(code)
Try
codeWriter.WriteLine("[")
codeWriter.WriteLine("Category( ""Style"" ),")
codeWriter.WriteLine("Description( """" ),")
codeWriter.WriteLine("DefaultValue( null ),")
codeWriter.WriteLine("DesignerSerializationVisibility(DesignerSerializationVisibility.Content),")
codeWriter.WriteLine("PersistenceMode(PersistenceMode.InnerProperty),")
codeWriter.WriteLine("]")
codeWriter.WriteLine("public {0} {1} {{", typeName, propertyName)
codeWriter.WriteLine("get {")
codeWriter.WriteLine("if ( {0} == null ) {{", memberName)
codeWriter.WriteLine("{0} = new {1}();", memberName, typeName)
codeWriter.WriteLine("if ( IsTrackingViewState ) {")
codeWriter.WriteLine("((IStateManager){0}).TrackViewState();", memberName)
codeWriter.WriteLine("}")
codeWriter.WriteLine("}")
codeWriter.WriteLine("return {0};", memberName)
codeWriter.WriteLine("}")
codeWriter.WriteLine("}")
codeWriter.WriteLine("private {0} {1};", typeName, memberName)
codeWriter.Flush()
Finally
If Not codeWriter Is Nothing Then
DirectCast(codeWriter, IDisposable).Dispose()
End If
End Try
ReplaceCode(code.ToString(), "CreateStyleProperty" & propertyName)
End Sub
' Creates a Public ITemplate Property
' Write the following line into the code area:
' <PropertyName> [<TemplateContainerType>]
' Select the text, run this macro
Public Sub CreateTemplateProperty()
CheckLanguage()
Dim ts As TextSelection = DTE.ActiveDocument.Selection
Dim selection As String = ts.Text.Replace(vbTab, " ").Trim(" ")
Dim words() As String = selection.Split(" ")
If 1 > words.Length OrElse words.Length > 2 Then
Return
End If
Dim propertyName As String = words(0)
Dim templateContainer As String = ""
If words.Length = 2 Then
templateContainer = words(1)
End If
Dim memberName As String = "_" & propertyName.Substring(0, 1).ToLower() & propertyName.Substring(1)
Dim code As New StringBuilder
Dim codeWriter As New StringWriter(code)
Try
codeWriter.WriteLine("[")
codeWriter.WriteLine("Browsable( false ),")
codeWriter.WriteLine("DefaultValue( null ),")
codeWriter.WriteLine("Description( """" ),")
codeWriter.WriteLine("PersistenceMode(PersistenceMode.InnerProperty),")
If templateContainer <> "" Then
codeWriter.WriteLine("TemplateContainer( typeof( {0} ) ),", templateContainer)
End If
codeWriter.WriteLine("]")
codeWriter.WriteLine("public ITemplate {0} {{", propertyName)
codeWriter.WriteLine("get {")
codeWriter.WriteLine("return {0};", memberName)
codeWriter.WriteLine("}")
codeWriter.WriteLine("set {")
codeWriter.WriteLine("{0} = value;", memberName)
codeWriter.WriteLine("ChildControlsCreated = false;")
codeWriter.WriteLine("}")
codeWriter.WriteLine("}")
codeWriter.WriteLine("private ITemplate {0};", memberName)
codeWriter.Flush()
Finally
If Not codeWriter Is Nothing Then
DirectCast(codeWriter, IDisposable).Dispose()
End If
End Try
ReplaceCode(code.ToString(), "CreateStyleProperty" & propertyName)
End Sub
' Creates A Public ViewState-Backed Property
' Write the following line into the code area:
' <PropertyType> <PropertyName> = <DefaultValue>;
' Select the text, run this macro
Public Sub CreateViewStateProperty()
CheckLanguage()
Dim ts As TextSelection = DTE.ActiveDocument.Selection
Dim selection As String = ts.Text.Replace(vbTab, " ").Trim(" ")
Dim words() As String = selection.Split(" ")
If words.Length < 3 Then
Return
End If
Dim typeName As String
Dim propertyName As String
Dim defaultValue As String
typeName = words(0)
propertyName = words(1)
defaultValue = selection.Substring(selection.IndexOf("=") + 2)
defaultValue = defaultValue.Substring(0, defaultValue.Length - 1)
Dim code As New StringBuilder
Dim codeWriter As New StringWriter(code)
Try
codeWriter.WriteLine("[")
codeWriter.WriteLine("Bindable(true),")
codeWriter.WriteLine("Category( """" ),")
codeWriter.WriteLine("Description( """" ),")
codeWriter.WriteLine("DefaultValue( {0} ),", defaultValue)
codeWriter.WriteLine("]")
codeWriter.WriteLine("public virtual " & typeName & " " & propertyName & " {")
codeWriter.WriteLine("get {")
codeWriter.WriteLine("Object state = ViewState[""{0}""];", propertyName)
codeWriter.WriteLine("if ( state != null ) {")
codeWriter.WriteLine("return ({0})state;", typeName)
codeWriter.WriteLine("}")
codeWriter.WriteLine("return {0};", defaultValue)
codeWriter.WriteLine("}")
codeWriter.WriteLine("set {")
codeWriter.WriteLine("ViewState[""{0}""] = value;", propertyName)
codeWriter.WriteLine("}")
codeWriter.WriteLine("}")
codeWriter.Flush()
Finally
If Not codeWriter Is Nothing Then
DirectCast(codeWriter, IDisposable).Dispose()
End If
End Try
ReplaceCode(code.ToString(), "CreateViewStateProperty" & propertyName)
End Sub
' Creates A Public Event and Protected OnEvent Method
' Write the following line into the code area:
' <EventName> [<DelegateType> <EventArgsType>]
' Select the text, run this macro
Public Sub CreateEvent()
CheckLanguage()
Dim ts As TextSelection = DTE.ActiveDocument.Selection
Dim selection As String = ts.Text.Replace(vbTab, " ").Trim(" ")
Dim words() As String = selection.Split(" ")
If words.Length <> 1 AndAlso words.Length <> 3 Then
Return
End If
Dim eventName As String = words(0)
Dim delegateTypeName As String = "EventHandler"
Dim eventArgsTypeName As String = "EventArgs"
If words.Length = 3 Then
delegateTypeName = words(1)
eventArgsTypeName = words(2)
End If
Dim raiserName As String = "On" & eventName
Dim eventObjectName As String = "event" & eventName
Dim code As New StringBuilder
Dim codeWriter As New StringWriter(code)
Try
codeWriter.WriteLine("#region {0} Event", eventName)
codeWriter.WriteLine()
codeWriter.WriteLine("public event {0} {1} {{", delegateTypeName, eventName)
codeWriter.WriteLine("add {")
codeWriter.WriteLine("Events.AddHandler( {0}, value );", eventObjectName)
codeWriter.WriteLine("}")
codeWriter.WriteLine("remove {")
codeWriter.WriteLine("Events.RemoveHandler( {0}, value );", eventObjectName)
codeWriter.WriteLine("}")
codeWriter.WriteLine("}")
codeWriter.WriteLine()
codeWriter.WriteLine("protected void {0}( {1} e ) {{", raiserName, eventArgsTypeName)
codeWriter.WriteLine("{0} handler = Events[ {1} ] as {0};", delegateTypeName, eventObjectName)
codeWriter.WriteLine("if ( handler != null ) {")
codeWriter.WriteLine("handler( this, e );")
codeWriter.WriteLine("}")
codeWriter.WriteLine("}")
codeWriter.WriteLine()
codeWriter.WriteLine("private static readonly Object {0} = new Object();", eventObjectName)
codeWriter.WriteLine()
codeWriter.WriteLine("#endregion")
codeWriter.Flush()
Finally
If Not codeWriter Is Nothing Then
DirectCast(codeWriter, IDisposable).Dispose()
End If
End Try
ReplaceCode(code.ToString(), "CreateEvent" & eventName)
End Sub
' Surrounds the selected code with a try/catch/finally block
Public Sub InsertTryCatchFinally()
Dim endCode As New StringBuilder
Dim writer As New StringWriter(endCode)
Try
writer.WriteLine("")
writer.WriteLine("} catch( Exception ex ) {")
writer.WriteLine("")
writer.WriteLine("} finally {")
writer.WriteLine("")
writer.WriteLine("}")
writer.Flush()
Finally
If Not writer Is Nothing Then
DirectCast(writer, IDisposable).Dispose()
End If
End Try
SurroundSelectionWithCode("try {" & Environment.NewLine, endCode.ToString(), "InsertTryCatchFinally")
End Sub
' Replaces the selected code with the given new code
Private Sub ReplaceCode(ByVal newCode As String, ByVal undoContextName As String)
Dim selection As TextSelection = DTE.ActiveDocument.Selection
Dim undoContextOpened As Boolean = False
Try
If Not DTE.UndoContext.IsOpen Then
Call DTE.UndoContext.Open(undoContextName, False)
undoContextOpened = True
End If
' Replace the text and SmartFormat it
selection.Delete()
selection.Insert(newCode, vsInsertFlags.vsInsertFlagsInsertAtStart)
selection.TopPoint.CreateEditPoint.SmartFormat(selection.BottomPoint)
Catch ex As Exception
MsgBox("Unable To Perform Code Alteration:" & Environment.NewLine & ex.Message, MsgBoxStyle.OKOnly, "Problem Running Macro")
Finally
If undoContextOpened Then
Call DTE.UndoContext.Close()
End If
End Try
End Sub
' Surrounds the selected code with the given prefix and suffix code
Private Sub SurroundSelectionWithCode(ByVal startCode As String, ByVal endCode As String, ByVal undoContextName As String)
Dim selection As TextSelection = DTE.ActiveDocument.Selection
Dim undoContextOpened As Boolean = False
Try
If Not DTE.UndoContext.IsOpen Then
Call DTE.UndoContext.Open(undoContextName, False)
undoContextOpened = True
End If
Dim tp As EditPoint = selection.TopPoint.CreateEditPoint()
selection.TopPoint.CreateEditPoint().Insert(startCode)
Dim bp As EditPoint = selection.BottomPoint.CreateEditPoint()
bp.Insert(endCode)
tp.SmartFormat(bp)
Catch ex As Exception
MsgBox("Unable To Perform Code Alteration:" & Environment.NewLine & ex.Message, MsgBoxStyle.OKOnly, "Problem Running Macro")
Finally
If undoContextOpened Then
Call DTE.UndoContext.Close()
End If
End Try
End Sub
' Ensures that the macro is modifying only c# documents
Private Sub CheckLanguage()
If DTE.ActiveDocument.Language <> "CSharp" Then
Throw New System.Exception("This Macro only works on C# code")
End If
End Sub
End Module

|
|
|
|