Saturday, November 22, 2008

VS.NET 2008 Javascript IntelliSense Troubleshooting

OK - maybe everyone else knows this, but after about a year of getting so-so support for intellisense in Javascript I downloaded the latest hotfix and hoped that this would fix it...it did not.  Since I have a very unhealthy addiction to Intellisense I figured out it was time to do a little troubleshooting and get this to work.  When adding the the script tags to my page to get intellisense, my Error List now contained the message:

Error updatign JScript IntelliSense: [SOMEPATH]\Temporary Internet Files\Content.IE5\[XXXX]\[XXX].js 'jQuery' is undefiend @ 9.1

So I figured if I could make this error go away chance are pretty good IntelliSense would start working again.  What I found is if you use the absolute path /FOO/FEE.JS in your script tag you get this error message.  If you don't and use a relative path ../FOO/FEE.JS all is well.

One other thing I found while searching the web (sorry I forgot the site so I can't give the person credit) is to get your Intellisense to work when using Masterpages

This allows for the design time to pickup the script file, but at run time the file won't be duplicated, especially since my JS files stored as a resource in.  I have my JS files compiled as an embedded resource and for some reason, adding those embedded resources to the ScriptManager, Scripts sections doesn't allow IntelliSense to add the files as you would expect.  Maybe in another year I'll get around to troubleshooting that, time to get some work done!

-ec

Custom String to Resource Refactoring with DXCore

In my ChaosFilter project, I'm factoring all the text and strings into a resource class.  The intent is to allow for customization by the end user for different installations and possibly translation at some point.  My solution is to place all my text into a class with properties that figure out how to get the text based based upon a unique key and context (which company/application they are running in).  Currently these are stored in the database and then loaded into a cached dictionary object.  This appears to work very fast, however with my architecture and a bit of code gen this could easily be replaced by more traditional resources. 

In the past it's been a pain the a** since I needed to create the label, put the marker into my class file then insert the field into the database.  A better solution certainly exists.  My solution is to build a DXCore custom refactoring.  The idea would be if I'm in a C# class within a string literal or an ASP.NET page within some static HTML, I could hit the refacting key and and take that text and convert it into a message or a simple label.  The difference being a label is only a few words where the message could be sentance or two.  For larger blocks of text I have a different approach.

My Solution

For a ASP.NET page: 
image

For a C# code file (in a library assembly or code behind):

Then to enter the text:

So how was this done?  Actually it was fairly trivial, I built part of it early this week in a couple hours, the rest this morning.  Probably < 4 hours for the whole thing and I haven't built a plugin for a couple years.

Getting Started

This link does a much better job than I could to show the basics of creating a DXCore plugin, but unless I missed it, it didn't cover too much about creating custom refactorings.

http://community.devexpress.com/forums/t/68994.aspx

So assuming that you have the frameworks setup for your plugin (I'm going to skip a few steps not relevant and found elsewhere), here's what you need to do:

1) Create a new DXCore Plug for your refactoring:

2) By default the Refactoring Provider that you need to create a custom Refactoring is not in the toolbox, it can be added by "Choose Items" and selecting the RefactoringProvider
image

3) Once you add that, drop that onto your custom design surface for your Refactoring plugin, it should look something boring like:

4) The click on Properties for your component and add appropriate values:

5) Now go in and click on the highlighed events to build up your stubs:

6) Finally go in and add your code (painting code from: http://tinyurl.com/5a3pe2):

private bool _handlingEditorForegroundPaint = false;
private SourceRange _previewRange;
private DevExpress.CodeRush.UserControls.CodePreviewWindow _previewWindow = null;

// DXCore-generated code...

#region InitializePlugIn
#region FinalizePlugIn

private void ConvertToCustomCaption_Apply(object sender, ApplyContentEventArgs ea)
{
var customLabel = new ui.CustomCaption();
   ea.Element.SelectFullBlock();
   customLabel.ShowWithString(CodeRush.Selection.Text, CodeRush.Caret.ScreenPosition);
if (!customLabel.Cancelled)
      ea.Selection.Text = string.Format("Customization.Captions.{0}", customLabel.LabelKey);
}

private void ConvertToCustomCaption_CheckAvailability(object sender, CheckContentAvailabilityEventArgs ea)
{
   ea.Available = CodeRush.Caret.InsideString && ea.Element.InsideClass;
}

private void ConvertToCustomCaption_HidePreview(object sender, HideContentPreviewEventArgs ea)
{
if (_previewWindow != null)
   {
      _previewWindow.HidePreview();
      _previewWindow = null;
   }
   _previewRange = SourceRange.Empty;
if (_handlingEditorForegroundPaint)
   {
      _handlingEditorForegroundPaint = false;
EventNexus.EditorPaintForeground += new EditorPaintEventHandler(EventNexus_EditorPaintForeground);
   }
}

private void ConvertToCustomCaption_PreparePreview(object sender, PrepareContentPreviewEventArgs ea)
{
PrimitiveExpression ele = ea.Element as PrimitiveExpression;
if (ele != null)
   {
      _previewRange = ele.Range.Clone();
EventNexus.EditorPaintForeground += new EditorPaintEventHandler(EventNexus_EditorPaintForeground);
      CreatePreviewWindow(ea, "Customization.Captions.[New]");
   }
}

private void CreatePreviewWindow(PrepareRefactoringPreviewEventArgs ea, string codeToPreview)
{
   _previewWindow = new CodePreviewWindow(ea.TextView, _previewRange.Top);
   _previewWindow.AddCode(codeToPreview);
   _previewWindow.ShowPreview();
}

private void InvalidatePreviews(RefactoringPreviewEventArgs ea)
{
if (_previewRange.IsEmpty)
return;

   int doubleSpaceWidth = ea.TextView.SpaceWidth * 2;
int textViewLineHeight = ea.TextView.LineHeight;
Rectangle previewRect = ea.TextView.GetRectangleFromRange(_previewRange);
   previewRect.Inflate(doubleSpaceWidth, textViewLineHeight);
ea.TextView.Invalidate(previewRect);
}

void EventNexus_EditorPaintForeground(EditorPaintEventArgs ea)
{
if (_previewRange.IsEmpty)
return;

   using (StrikeThrough strikeThrough = new StrikeThrough())
   {
      strikeThrough.TextView = ea.TextView;
      strikeThrough.FillColor = Color.Red;
      strikeThrough.Range = _previewRange;
      strikeThrough.Paint(ea.Graphics);
   }
}

The "thingy" that let's me enter the text is just a simple WinForm represented by ui.CustomCaption, I won't bore you with that implemetentation since it is fairly tightly coupled intergrated into my architecture.

Enjoy!

-ec

Sunday, November 16, 2008

Another Reason why I like XAML and Silverlight/WPF

Early on in my career I was mentored on deferring execution.  This was in a language called FORTH and the idea was build your program structure and abstract the details into WORDS to be filled in later.  Repeat until complete.  This simple process still holds true today in OO languages like C# where I do most of my work.

Now back to why I like Silverlight/WPF, as I'm just starting to get beyond the basics, the more I'm starting to see that this is an extremely well thought out architecture. As I'm developing my functionality I can easily "defer execution" or really in this case, care zero about the style and then go in later and make it pretty. Or if I'm really lucky find someone that knows what they are doing to give it a polished look. Although the same can be done with HTML and CSS, this just seems like it's just a bit cleaner and since we are targetting only one type of client (Silverlight or WPF) instead of the different browsers the results are much more repeatable.

The other thing I'm really impressed with is the separation of UI and code behind. Although time will tell on the actual business value (read ability to maintain and extend) it seems like the ability to create CLR instances in the XAML and then glue everything together with dependency properties, just feels good to do.

Now back to getting some work done with this!

-ec

Wednesday, November 5, 2008

SoapExceptions and Global Exception Logging

In all my web sites my pages inherit from a custom base page that inherits from System.Web.UI.Page and overrides OnError.  This method then uses Server.GetLastError() to get the exception generated by the offending code.  At this point, it's probably too late to do anything about it, but at least I can take a snap shot of the current user context, log that information present a friendly message to the user.  Although the bug made it into production at least it's not happening 200 times a day and I don't know about.

A good portion of my site is driven by web services via AJAX and as I start to implement Silverlight for a few sophisticated UI components, I'm relying more and more on web services.  My problem until now was I didn't have any sort of catch-all that notified me about problem in a web service call.

After a bit of research I found something called a SoapExtension in the System.Web.Services.Protocols namespace.

This provides calls at different points in the processing of the soap message:

We can implement some functionality in the AfterSerialize stage.  If we detect a non-null value in the message.Exception property it's probably safe to assume something bad happened within the web service.

The final step is to register this in the web.config file.

In addition SoapExtensions can be associated with specific methods via attributes.  This might be interesting to provide an interesting solution for attaching custom authentication and authorization.

-ec

Monday, November 3, 2008

Local Silverlight Development and clientaccesspolicy.html

I've finally found a good excuse to implement something using Silverlight 2.0 within my web product.  After spending about 3-4 hours attempting to get my Silverlight app to talk to a local web service, I found out something that hopefully will save you some time.  First a little about my environment.  I'm using IIS 7.0 on a 64 bit machine.  I have IIS setup with two sites for my Silverlight development, the first hosts my web service, the second hosts the web site containing my Silverlight application.  I have my hosts file setup as follows

127.0.0.1   webservice
127.0.0.1   website

Then in IIS I bind the the web sites to those host names.

After setting up the clientaccesspolicy.xml file in my webservice site to allow Silverlight to access I just couldn't get my Silverlight app to talk to my web service.  I tried this in both IE and Firefox with no luck.  After doing a little testing, I found it worked when I used the development web server (I think this was formally called cassini) it also seemed to work when when I used the host name "localhost".  Next I fired up fiddler and watched the network traffic.  Anytime I used cassini or local host, the clientaccesspolicy.xml was downloaded and the call to the web service succeeded.  If I tried this through my site, no request was made.

After trying a number of things I found I was able to get this working by opening the security settings within Internet Explorer

Tools->Internet Options->Security Tab, then click on Local Intranet and add my two sites.  This seemed to do the trick for both Internet Explorer and Firefox.

Hope this saves you a bit of time

-ec