# Saturday, November 13, 2010

messeberlin2010

What was hot:

Windows Phone 7. The stand where you could try the various handsets was always busy. Several people had their own sets (last year, it was iPhones everywhere). There were adverts all over Berlin too. It won't worry Apple, and Android has reached enough mass to secure second place, but Microsoft should easily beat Symbian and Blackberry especially if they keep up the momentum on updates and new features (HTML 5 browser, apps through private company portals rather than Microsoft's public marketplace).

The cloud. Lots of focus on aspects of Azure, including the announcement of private clouds (Hyper V Cloud) with hosting available. On the developer track, I was pretty interested to learn about the future of distributed LINQ, based on the Microsoft Research DryadLinq project. That project allows you to run distributed (Map/Reduce) queries over an HPC cluster. The future goal is to run it on Azure nodes. Just like in PLINQ where you can append "AsParallel" to a linq statement, you will be able to add "AsDistributed" and the linq statement will be broken up into computation units and run on different nodes (your app.config will also have configuration to help the create the job graph).

Kinect. The graphics are simple and Wii-like, but the demo stand was very popular.

Silverlight. There were quite a few technical Silverlight sessions. From talking to people, it seems Silverlight has taken off for line of business apps within intranets, at least in Microsoft environments where Windows Forms or WPF would have been used before. On the public web, it hasn't dented Flash and Flex, and the only public success is Netflix. But for .Net based IT departments it seems the obvious choice for RIA. The risk from the recent publicity is that managers won't want to invest in it because they think it's dead. Based on the number of sessions here, it's still key to Microsoft.

Berlin. It was cold and grey, and you could only get out after dark, but it's an interesting city to explore. Transport was easy too.

What was not:

Developers. There isn't a lot new this year for developers. We have no new Visual Studio/.Net version.

Although I complained earlier about the heavy "IT Pro" bias, I heard that some infrastructure guys were also disappointed by the content- too much simple introductions and marketing and not enough meat.

Internet connectivity (at the start of the week). Wireless was terrible on the first day, but it did improve - it was excellent on Thursday and Friday. There seemed to be many more power and wired connections this year. Okay, perhaps this point should also be in the "hot" category!

Crowds, It was very busy, and the huge space and confusing layout of the Messe made navigating to sessions a little tricky at times.

Loot. Crap. A stupid little swim-bag, and lots of T-shirts. The organizers and exhibitors have a major lack of imagination and, obviously, money. Where were the USB sticks and other useful give-aways from previous years?

Where's HTML 5?

There were a couple of HTML 5 sessions showing off IE 9, and pretty impressive it is too. There was a pleasing acknowledgement that other browsers exist and also target HTML 5, and IE 9 is just catching up. But there is no tooling, or apparently any due dates. In MVC you can just write the HTML and reference Modenizr etc to trigger down-level support, but there is no intellisense or code colouring yet, let alone HtmlHelpers. It wouldn't be difficult to traditional ASP.Net webcontrols for key HTML 5 tags. It's not just <video>; what I'd like is <input type="date" with an browser datepicker (and down-level detection to add jQuery UI). iPhone and Android (but not Windows Phone 7 yet) have strong HTML 5 features which we want to use. Oh, and the next version of Windows Phone needs to have HTML 5 capabilities to match the desktop IE9. Maybe longer term could Silverlight XAML be converted to SVG?

Where's Alt.Net?

As traditional, this was a Microsoft-only conference. Several presenters used Resharper,  and there was the odd passing mention of NHibernate. But when you talk to other attendees, NHibernate, log4Net and other testing frameworks such as NUnit and MbUnit are in widespread use - my impression is that Entity Framework is some way behind. With Oracle doing it's best to alienate Java developers, Microsoft could start showing a friendlier face with a few sessions for Mono, IoC, mocking, BDD, NoSQL etc.

Future

I enjoyed TechEd, but I think the moral of this year is, only go to a TechEd Europe when Microsoft are launching a new Visual Studio or other major developer tool.

posted on Saturday, November 13, 2010 5:51:23 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Tuesday, November 09, 2010

I'm back in Berlin for the Microsoft TechEd conference. It's huge- thousands of people, and even though the exhibition halls are like aircraft hangars they are packed with people, desperate to get free croissants, T-shirts and other baubles.

berlintechedThere's a lot of "IT pro" (windows admins) content, in the exhibitors and session schedule, and developers like me have thinner pickings (unless you just want croissants and T shirts, which evidently most do).

The only new stuff for developers is Windows Phone 7. The demo stand with various windows phones to try was always packed, and yes, they do look nice. Some cloud stuff, which is clearly a key target for Microsoft. Otherwise it's a rehash of the Visual Studio 2010 and .Net 4 introductory stuff from last year, which was fine last year when it was new but it's a little tired now. Very thin pickings on ASP, a single (introductory) session on MVC, a little on parallel. There is quite a bit on Silverlight, but everyone outside this audience now thinks Microsoft have killed it.

The goody bag is just a simple sports sack (I'm still carrying the rather nice laptop bag from last year). No free Windows 7 Ultimate disc this year (well, you do get a TechNet subscription so that's 5 licenses there, but still.) The free stuff from the vendors in the exhibition hall is showing signs of the economic downturn too. Lots of competitions though.

Only 3 more days of crowds, food, beer, and T-shirt collecting to go.

posted on Tuesday, November 09, 2010 10:26:28 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Monday, November 01, 2010

A long time ago I wrote my database schema reader. It was 2005, .Net 2.0 was just out and I was learning about the new features. .Net 2.0's ADO DbProviderFactory had a nice idea, GetSchema, to get database independent schema information. But the GetSchema collections were slightly different for each database, and they were data tables. So I wrote a simple facade over them to get proper classes.

Initially I used it to generate CRUD stored procedures, and then to generate data access code, even including NHibernate mappings. I even did some (simple) database conversions. All this was quite icky, and each time I did the code-gen a different way, but the core facade worked well.

After getting a couple of queries about the extracts I'd already posted, I thought I might as well put up the entire source code. So here's the codeplex project with the source code, and even some basic SQL code-gen.

I picked Codeplex just because it's more .Net-centric than Google code or github, plus I'm more familiar with TFS and Subversion source control. It was pretty easy. I don't expect many downloads (if any!), but maybe a couple of people can steal the relevant bits of source code.

Check out the database schema reader codeplex project.

posted on Monday, November 01, 2010 12:59:33 PM (Romance Standard Time, UTC+01:00)  #    Comments [0]
# Monday, September 20, 2010
Finding out all users on Team Foundation Server turns out to be pretty easy.
I found the answer at http://blogs.msdn.com/b/jmanning/archive/2006/05/02/588648.aspx

You need a reference to C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.dll plus the Microsoft.TeamFoundation.Client.dll and Microsoft.TeamFoundation.Common.dll

var server = new TeamFoundationServer(tfsUrl);
var groupSecurityService =
    (IGroupSecurityService)server.GetService(typeof(IGroupSecurityService));

var validUserSid =
    groupSecurityService.ReadIdentity(
        SearchFactor.AccountName,
        @"[Server]\Team Foundation Valid Users",
        QueryMembership.Expanded);

Identity[] identities =
    groupSecurityService.ReadIdentities(
        SearchFactor.Sid,
        validUserSid.Members,
        QueryMembership.None);

//exclude Administrators and system accounts
foreach (Identity id in identities.Where(x=>
    x.Type == IdentityType.WindowsUser &&
    x.Deleted == false &&
    !x.Description.StartsWith("Built-in account", StringComparison.OrdinalIgnoreCase) &&
    !string.IsNullOrEmpty(x.MailAddress) &&
    !x.AccountName.StartsWith("sys_", StringComparison.OrdinalIgnoreCase)))
{
        Debug.WriteLine(id.AccountName + " " + id.DisplayName);
}


posted on Monday, September 20, 2010 3:51:34 PM (Romance Daylight Time, UTC+02:00)  #    Comments [0]
# Tuesday, August 10, 2010
Next step in my adventures in deploying to IIS 7

This time I try to access my webservice (MyService.svc).

HTTP Error 404.3 - Not Found

The page you are requesting cannot be served because of the extension of the configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map.

The IIS handlers window shows no handler for *.svc.

Solution:
Open a command window as administrator
Go to C:\windows\Microsoft.Net\Framework\v3.0\Windows Communication Foundation
Type ServiceModelReg –i

No need to restart, it just works.


UPDATE .Net 4
Open a command window as administrator
Go to %windir%\Microsoft.NET\Framework\v4.0.30319
Type ServiceModelReg –i -c:httpnamespace

posted on Tuesday, August 10, 2010 9:44:57 AM (Romance Daylight Time, UTC+02:00)  #    Comments [0]
When installing a website built on XP/ IIS6 or the built-in Visual Studio Cassini, you put httpHandlers and httpModules in system.web.
On Windows Server 2008/ IIS7, you use system.webServer, with modules and handlers.

If you use the web.config in the standard Ajax-enabled template, this just works.

If not, it doesn't. IIS7 throws an error page.
Description: This application is running in an application pool that uses the Integrated .NET mode. This is the preferred mode for running ASP.NET applications on the current and future version of IIS.

In this mode, the application should not specify ASP.NET module components in the <system.web>/<httpModules> configuration section. Instead, it should use the <system.webServer>/<modules> configuration section to load ASP.NET module components.


The fix is in that Ajax web.config.
    <system.webServer>
        <validation validateIntegratedModeConfiguration="false"/>

It just turns off the validation, allowing system.web/httpModules etc to exist, but ignores them and uses the system.webServer/modules etc version.

Documentation is here.

Yes, this just happened to me. Doh.

posted on Tuesday, August 10, 2010 8:15:08 AM (Romance Daylight Time, UTC+02:00)  #    Comments [0]
# Monday, August 09, 2010
I had a Windows Server 2008 R2 but no IIS. So how to install it? It isn't obvious.

It's in Administrative Tools/ Server Manager / scroll to Roles then Add Roles.
In the roles wizard is a checkbox for Web Server (IIS)
There are various sub options including Common HTTP Features (static content for your images, css), ASP.Net, Security (Windows authentication), Performance (compression), Management Tools (IIS manager)

Here's the details



posted on Monday, August 09, 2010 11:52:17 AM (Romance Daylight Time, UTC+02:00)  #    Comments [0]
# Sunday, August 01, 2010

I was looking at the mess of another programmer's code and realised how much we have individual code styles.

Actually his code wasn't a mess. I turn on code wrapping, he didn't. His nested lambda expressions were suddenly all over the place on my screen, but looked tidy on a wide scrolled screen. I would have introduced more line breaks, splitting parameters onto different lines, but creating more vertical scrolling.

Most colleagues don't write unit tests. That's changing. I've been writing unit tests since .net 1. While I still see people exercising their code by writing console projects or winforms, I've always written unit tests. It's a primitive sort of TDD, at least the part about creating the API in a test. My code is generally old style Asserts against classes and methods, having used vintage NUnit and now MsTest. Lately I try to break up the test into arrange/act/assert sections, and sometimes the BDD language of "Given"/"When"/"Then". Recently company style is to use a custom BDD-type framework on MsTest, but I think the underscores in naming are just plain ugly (the Ruby RSpec style with quoted strings is much nicer and more natural).

I mostly use manual mocks, and whenever I've tried to use a mocking framework I've ripped it out after a while- if you have to write a manual mock it simplifies your API, and that seems neater to me. Recently I've been using Moq for stitching together some components that need use services, and it makes quite heavyweight tests (mostly because we're not allowed to use a DI framework).

Resharper, and more recently CodeRush, are hugely influential in code style- getting the right margin to go green is pretty satisfying, even for very minor things like deleting unused "using" statements. When Resharper 3 suggested using "var" instead of explicit types, I followed them, even though many developers dislike it intensely. The majority opinion seems to be to use "var" with constructors ("var x = new Entity()") but to specify the type returned from methods ("Entity x = Create();"). For the last 2 years or so (when I moved to CodeRush), my code has mostly followed the latter style. When quickly sketching together code, I use var, because I'll be changing methods to use interfaces or base classes, and you don't even have to do rename refactoring.

Resharper has got quite good at suggesting variable names, and usually I take the suggestions. Otherwise some of my variable names can be short - I'm particularly like to use "x" in Linq expressions because brevity makes them much easier to read (as long as methods are short enough):

var result = list.Where(x => x.IsValid); //simple variable names work fine in short methods & linq

ICommonCustomerEntity commonCustomerEntity = commonCustomerEntityCollection.Where(commonCustomerEntity => commonCustomerEntity.IsValid); //ugh

One thing that is characteristic of my code are chunks of specs/user stories (or emails/ work items/ bug reports as applicable) pasted in as internal comments. It doesn't always stay there after I've finished, but it's the first thing I do before coding. It can be nice to see the business logic text immediately above the code (much better than in a Word document, TFS or some task management tracker). It's particularly useful to quote emails when tracking the changing business requirements as you go through acceptance.

posted on Sunday, August 01, 2010 7:48:52 AM (Romance Daylight Time, UTC+02:00)  #    Comments [0]
# Sunday, June 06, 2010

One of the really useful Visual Studio add-ins is Smart Paster. It adds a "Paste As." context menu that allows you to paste in the clipboard text as a comment, a correctly quoted string or a string builder.

smartpaster

There are versions for VS 2003, 2005 and 2008. But not 2010.

Sometimes you can just copy in the dll and addin file into the VS 2010 Addins folder (.\Documents\Visual Studio 2010\Addins) and edit the addin file (it's just xml) to say "10.0" instead of "9.0". But that doesn't work for SmartPaster - VS 2010 shows an error and insists on disabling the addin.

The VS 2008 download includes the source, so I tried to upgrade it.

It turns out the problem is when it creates the context menus it sets the CommandBarButton.FaceId property (to show an image next to the text). But in VS2010 that throws a DeprecatedException.

Ok, simple fix, but the original source is old code with a fairly high WTF-per-line ratio (well, it was written 2004, .Net 1.1). Before long I had ported it from VB.Net to C# (thanks Telerik) and rewritten large parts (mostly refactoring with Coderush). I simplified by dropping the "regionize" stuff (never use it), the VB support and the configuration form. Here's my code- you can create a new Extensibility Addin project, replace the Connect class and add the SmartPaster class- see below.

It's still a port, so certainly not as clean as something just written from scratch. And perhaps VS2010 has nicer ways of doing all these things now the code window is a WPF control - the EnvDTE objects are ugly and hard to use. Anyway, thanks to Alex Papadimoulis for the original code.

Update: download the binary and unzip into your Addins folder. 

Update 2: source and binary are also on Codeplex. (It's exactly the same as shown here.)


   1:  using System;
   2:  using System.Collections;
   3:  using EnvDTE;
   4:  using EnvDTE80;
   5:  using Extensibility;
   6:  using Microsoft.VisualStudio.CommandBars;
   7:   
   8:  namespace SmartPaster2010
   9:  {
  10:      ///<summary>The object for implementing an Add-in.</summary>
  11:      ///<seealso class='IDTExtensibility2' />
  12:      public class Connect : IDTExtensibility2
  13:      {
  14:          private readonly ArrayList _pasteAsButtons;
  15:          private readonly SmartPaster _smartPaster;
  16:          private CommandBarPopup _pasteAsPopup;
  17:   
  18:          ///<summary>Implements the constructor for the Add-in object. Place your initialization code within this method.</summary>
  19:          public Connect()
  20:          {
  21:              _pasteAsButtons = new ArrayList();
  22:              _smartPaster = new SmartPaster();
  23:          }
  24:   
  25:          ///<summary>Implements the OnConnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being loaded.</summary>
  26:          ///<param term='application'>Root object of the host application.</param>
  27:          ///<param term='connectMode'>Describes how the Add-in is being loaded.</param>
  28:          ///<param term='addInInst'>Object representing this Add-in.</param>
  29:          ///<seealso class='IDTExtensibility2' />
  30:          public void OnConnection(object application, ext_ConnectMode connectMode, 
object addInInst, ref Array custom)
  31:          {
  32:              _applicationObject = (DTE2)application;
  33:              _addInInstance = (AddIn)addInInst;
  34:   
  35:   
  36:              //check for the commands
  37:              bool cmdExists = false;
  38:              foreach (Command cmd in _applicationObject.Commands)
  39:              {
  40:                  if (cmd.Name.EndsWith("PasteAsComment", StringComparison.OrdinalIgnoreCase))
  41:                  {
  42:                      cmdExists = true;
  43:                      break;
  44:                  }
  45:              }
  46:   
  47:              try
  48:              {
  49:                  if (!cmdExists)
  50:                  {
  51:                      AddPasteAsCommands();
  52:                  }
  53:   
  54:                  if (connectMode == ext_ConnectMode.ext_cm_Startup && _pasteAsPopup == null)
  55:                  {
  56:                      //Add items to the Context (Right-Click) Menu
  57:                      //find the position of the &Paste command
  58:                      int position = 0;
  59:   
  60:                      CommandBar codeWindow = _applicationObject.CommandBars["Code Window"];
  61:   
  62:                      for (int i = 1; i <= codeWindow.Controls.Count; i++)
  63:                      {
  64:                          if (codeWindow.Controls[i].Caption == "&Paste")
  65:                          {
  66:                              position = i;
  67:                              break;
  68:                          }
  69:                      }
  70:   
  71:                      //add the popup menu "Paste As...", which may already be on the menu
  72:                      _pasteAsPopup = (CommandBarPopup)codeWindow.Controls.Add(
(int)MsoControlType.msoControlPopup, 1, Type.Missing, position + 1, Type.Missing);
  73:                      _pasteAsPopup.Caption = "Paste As ...";
  74:                      AddPasteAsButtons();
  75:                  }
  76:   
  77:              }
  78:              catch (Exception ex)
  79:              {
  80:                  System.Diagnostics.Debug.WriteLine(ex.Message);
  81:              }
  82:          }
  83:   
  84:          private void AddPasteAsCommands()
  85:          {
  86:              //no configure or regionize because I never use 'em
  87:              _applicationObject.Commands.AddNamedCommand(_addInInstance, "PasteAsComment", "Paste As Comment", "Pastes clipboard text as a comment.", true, 22, null, Convert.ToInt32(vsCommandStatus.vsCommandStatusSupported) + Convert.ToInt32(vsCommandStatus.vsCommandStatusEnabled));
  88:   
  89:              _applicationObject.Commands.AddNamedCommand(_addInInstance, "PasteAsString", "Paste As String", "Pastes clipboard text as a string literal.", true, 22, null, Convert.ToInt32(vsCommandStatus.vsCommandStatusSupported) + Convert.ToInt32(vsCommandStatus.vsCommandStatusEnabled));
  90:   
  91:              _applicationObject.Commands.AddNamedCommand(_addInInstance, "PasteAsStringBuilder", "Paste As StringBuilder", "Pastes clipboard text as a stringbuilder.", true, 22, null, Convert.ToInt32(vsCommandStatus.vsCommandStatusSupported) + Convert.ToInt32(vsCommandStatus.vsCommandStatusEnabled));
  92:          }
  93:   
  94:   
  95:          private void AddPasteAsButtons()
  96:          {
  97:   
  98:              //now the buttons
  99:              CommandBarButton pasteAsButton;
 100:   
 101:              //add "Comment"
 102:              pasteAsButton = AddCommandButton();
 103:              pasteAsButton.Caption = "Comment";
 104:              pasteAsButton.TooltipText = "Inserts clipboard with each line prefixed with a comment character";
 105:              pasteAsButton.Click += PasteAsComment;
 106:              _pasteAsButtons.Add(pasteAsButton);
 107:   
 108:              //add "String"
 109:              pasteAsButton = AddCommandButton();
 110:              pasteAsButton.Caption = "String";
 111:              pasteAsButton.TooltipText = "Inserts enquoted clipboard text with line breaks and other characters escaped";
 112:              pasteAsButton.Click += PasteAsString;
 113:              _pasteAsButtons.Add(pasteAsButton);
 114:   
 115:              //add "StringBuilder"
 116:              pasteAsButton = AddCommandButton();
 117:              pasteAsButton.Caption = "StringBuilder";
 118:              pasteAsButton.TooltipText = "Inserts enquoted clipboard text built up by a stringbuilder.";
 119:              pasteAsButton.Click += PasteAsStringBuilder;
 120:              _pasteAsButtons.Add(pasteAsButton);
 121:   
 122:          }
 123:   
 124:          private CommandBarButton AddCommandButton()
 125:          {
 126:              var pasteAsButton = (CommandBarButton)_pasteAsPopup.Controls.Add((int)MsoControlType.msoControlButton);
 127:              //in 2010, CommandBarButton.FaceId throws a DeprecatedException.
 128:              //pasteAsButton.FaceId = 22;
 129:              pasteAsButton.Visible = true;
 130:              return pasteAsButton;
 131:          }
 132:   
 133:          #region "PasteAs Handlers"
 134:   
 135:          ///<summary>
 136:          ///Occurs when the user clicks the PasteAsString button.
 137:          ///</summary>
 138:          ///<param name="ctrl">
 139:          ///Denotes the CommandBarButton control that initiated the event. 
 140:          ///</param>
 141:          ///<param name="cancelDefault">
 142:          ///False if the default behavior associated with the CommandBarButton control occurs, unless its canceled by another process or add-in. 
 143:          ///</param>
 144:          private void PasteAsString(CommandBarButton ctrl, ref bool cancelDefault)
 145:          {
 146:              _smartPaster.PasteAsString(_applicationObject);
 147:          }
 148:   
 149:   
 150:          ///<summary>
 151:          ///Occurs when the user clicks the PasteAsComment button.
 152:          ///</summary>
 153:          ///<param name="ctrl">
 154:          ///Denotes the CommandBarButton control that initiated the event. 
 155:          ///</param>
 156:          ///<param name="cancelDefault">
 157:          ///False if the default behavior associated with the CommandBarButton control occurs, unless its canceled by another process or add-in. 
 158:          ///</param>
 159:          private void PasteAsComment(CommandBarButton ctrl, ref bool cancelDefault)
 160:          {
 161:              _smartPaster.PasteAsComment(_applicationObject);
 162:          }
 163:   
 164:          ///<summary>
 165:          ///Occurs when the user clicks the PasteAsStringBuilder button.
 166:          ///</summary>
 167:          ///<param name="ctrl">
 168:          ///Denotes the CommandBarButton control that initiated the event. 
 169:          ///</param>
 170:          ///<param name="cancelDefault">
 171:          ///False if the default behavior associated with the CommandBarButton control occurs, unless its canceled by another process or add-in. 
 172:          ///</param>
 173:          private void PasteAsStringBuilder(CommandBarButton ctrl, ref bool cancelDefault)
 174:          {
 175:              _smartPaster.PasteAsStringBuilder(_applicationObject);
 176:          }
 177:          #endregion
 178:   
 179:          #region "Exec"
 180:   
 181:          ///<summary>
 182:          /// Implements the Exec method of the IDTCommandTarget interface.
 183:          /// This is called when the command is invoked.
 184:          ///</summary>
 185:          ///<param name='commandName'>
 186:          ///        The name of the command to execute.
 187:          ///</param>
 188:          ///<param name='executeOption'>
 189:          ///        Describes how the command should be run.
 190:          ///</param>
 191:          ///<param name='varIn'>
 192:          ///        Parameters passed from the caller to the command handler.
 193:          ///</param>
 194:          ///<param name='varOut'>
 195:          ///        Parameters passed from the command handler to the caller.
 196:          ///</param>
 197:          ///<param name='handled'>
 198:          ///        Informs the caller if the command was handled or not.
 199:          ///</param>
 200:          ///<seealso class='Exec' />
 201:          public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
 202:          {
 203:              handled = false;
 204:              if ((executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault))
 205:              {
 206:                  handled = true;
 207:                  switch (commandName)
 208:                  {
 209:                      //case "SmartPaster.Connect.Configure":
 210:                      //    //show the config form
 211:                      //    SmartPasterForm myForm = new SmartPasterForm();
 212:                      //    myForm.ShowDialog();
 213:                      //    //since config may have changed, show/hide buttons
 214:                      //    EnableContextMenuButtons();
 215:   
 216:                      //    break;
 217:                      case "SmartPaster.Connect.PasteAsComment":
 218:                          _smartPaster.PasteAsComment(_applicationObject);
 219:                          break;
 220:                      case "SmartPaster.Connect.PasteAsString":
 221:                          _smartPaster.PasteAsString(_applicationObject);
 222:                          break;
 223:                      case "SmartPaster.Connect.PasteAsStringBuilder":
 224:                          _smartPaster.PasteAsStringBuilder(_applicationObject);
 225:                          break;
 226:                      //case "SmartPaster.Connect.PasteAsRegion":
 227:                      //    _smartPaster.PasteAsRegion(_applicationObject);
 228:                      //    break;
 229:                      default:
 230:                          handled = false;
 231:                          break;
 232:                  }
 233:              }
 234:          }
 235:          #endregion
 236:   
 237:          #region Standard Template Stuff
 238:          ///<summary>
 239:          ///Implements the OnDisconnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being unloaded.
 240:          ///</summary>
 241:          ///<param name="disconnectMode">The disconnect mode.</param>
 242:          ///<param name="custom">The custom.</param>
 243:          ///<seealso class="IDTExtensibility2"/>
 244:          public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
 245:          {
 246:              if (_pasteAsPopup != null && 
 247:                  (disconnectMode == ext_DisconnectMode.ext_dm_UserClosed || disconnectMode == ext_DisconnectMode.ext_dm_HostShutdown))
 248:              {
 249:                  _pasteAsPopup.Delete(true);
 250:              }
 251:          }
 252:   
 253:          ///<summary>Implements the OnAddInsUpdate method of the IDTExtensibility2 interface. Receives notification when the collection of Add-ins has changed.</summary>
 254:          ///<param term='custom'>Array of parameters that are host application specific.</param>
 255:          ///<seealso class='IDTExtensibility2' />        
 256:          public void OnAddInsUpdate(ref Array custom)
 257:          {
 258:          }
 259:   
 260:          ///<summary>Implements the OnStartupComplete method of the IDTExtensibility2 interface. Receives notification that the host application has completed loading.</summary>
 261:          ///<param term='custom'>Array of parameters that are host application specific.</param>
 262:          ///<seealso class='IDTExtensibility2' />
 263:          public void OnStartupComplete(ref Array custom)
 264:          {
 265:          }
 266:   
 267:          ///<summary>Implements the OnBeginShutdown method of the IDTExtensibility2 interface. Receives notification that the host application is being unloaded.</summary>
 268:          ///<param term='custom'>Array of parameters that are host application specific.</param>
 269:          ///<seealso class='IDTExtensibility2' />
 270:          public void OnBeginShutdown(ref Array custom)
 271:          {
 272:          }
 273:   
 274:          private DTE2 _applicationObject;
 275:          private AddIn _addInInstance;
 276:          #endregion
 277:   
 278:          ///<summary>
 279:          ///Queries the status.
 280:          ///</summary>
 281:          ///<param name="commandName">Name of the command.</param>
 282:          ///<param name="neededText">The needed text.</param>
 283:          ///<param name="statusOption">The status option.</param>
 284:          ///<param name="commandText">The command text.</param>
 285:          public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus statusOption, ref object commandText)
 286:          {
 287:              if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
 288:              {
 289:                  if (commandName.StartsWith("SmartPaster.Connect"))
 290:                  {
 291:                      if (((_applicationObject.ActiveDocument != null)) && ((_applicationObject.ActiveDocument.Object("TextDocument") != null)))
 292:                      {
 293:                          statusOption = vsCommandStatus.vsCommandStatusEnabled | vsCommandStatus.vsCommandStatusSupported;
 294:                      }
 295:                      else
 296:                      {
 297:                          statusOption = vsCommandStatus.vsCommandStatusSupported;
 298:                      }
 299:                  }
 300:                  else
 301:                  {
 302:                      statusOption = vsCommandStatus.vsCommandStatusUnsupported;
 303:                  }
 304:              }
 305:          }
 306:      }
 307:  }

Here's the SmartPaster class.

   1:  using System;
   2:  using System.IO;
   3:  using System.Text;
   4:  using System.Windows.Forms; //clipboard
   5:  using EnvDTE;
   6:  using EnvDTE80;
   7:   
   8:  namespace SmartPaster2010
   9:  {
  10:      /// <summary>
  11:      /// Class responsible for doing the pasting/manipulating of clipdata.
  12:      /// </summary>
  13:      internal sealed class SmartPaster
  14:      {
  15:          /// <summary>
  16:          ///  Convient property to retrieve the clipboard text from the clipboard
  17:          /// </summary>
  18:          private static string ClipboardText
  19:          {
  20:              get
  21:              {
  22:                  IDataObject iData = Clipboard.GetDataObject();
  23:                  if (iData.GetDataPresent(DataFormats.Text))
  24:                      return Convert.ToString(iData.GetData(DataFormats.Text));
  25:                  return string.Empty;
  26:              }
  27:          }
  28:   
  29:          #region "Stringinize"
  30:          /// <summary>
  31:          /// Stringinizes text passed to it for use in C#
  32:          /// </summary>
  33:          /// <param name="txt">Text to be Stringinized</param>
  34:          /// <returns>C# Stringinized text</returns>
  35:          private static string StringinizeInCs(string txt)
  36:          {
  37:              //c# quote character -- really just a "
  38:              const string qChr = "\"";
  39:   
  40:              //sb to work with
  41:              var sb = new StringBuilder(txt);
  42:   
  43:              //escape appropriately
  44:              //escape the quotes with ""
  45:              sb.Replace(qChr, qChr + qChr);
  46:   
  47:              //insert " at beginning and end
  48:              sb.Insert(0, "@" + qChr);
  49:              sb.Append(qChr);
  50:              return sb.ToString();
  51:          }
  52:          #endregion
  53:   
  54:          #region "Commentize"
  55:          /// <summary>
  56:          /// Commentizes text passed to it for use in C#
  57:          /// </summary>
  58:          /// <param name="txt">Text to be Stringinized</param>
  59:          /// <returns>C# Commentized text</returns>
  60:          private static string CommentizeInCs(string txt)
  61:          {
  62:              const string cmtChar = "//";
  63:   
  64:              var sb = new StringBuilder(txt.Length);
  65:   
  66:              //process the passed string (txt), one line at a time
  67:              //the original was horrible WTF code
  68:              using (var reader = new StringReader(txt))
  69:              {
  70:                  string line;
  71:                  while ((line = reader.ReadLine()) != null)
  72:                  {
  73:                      sb.AppendLine(cmtChar + line);
  74:                  }
  75:              }
  76:   
  77:              return sb.ToString();
  78:          }
  79:          #endregion
  80:   
  81:          #region "Stringbuilderize"
  82:          private static string StringbuilderizeInCs(string txt, string sbName)
  83:          {
  84:              //c# quote character -- really just a "
  85:              const string qChr = "\"";
  86:   
  87:              //sb to work with
  88:              var sb = new StringBuilder(txt);
  89:   
  90:              //escape \,", and {}
  91:              sb.Replace(qChr, qChr + qChr);
  92:   
  93:              //process the passed string (txt), one line at a time
  94:   
  95:              //dump the stringbuilder into a temp string
  96:              string fullString = sb.ToString();
  97:              sb.Clear(); //lovely .net 4 - sb.Remove(0, sb.Length);
  98:   
  99:              //the original was horrible WTF code
 100:              using (var reader = new StringReader(fullString))
 101:              {
 102:                  string line;
 103:                  while ((line = reader.ReadLine()) != null)
 104:                  {
 105:                      sb.Append(sbName + ".AppendLine(");
 106:                      sb.Append("@" + qChr);
 107:                      sb.Append(line.Replace("\t", "\\t"));
 108:                      sb.AppendLine(qChr + ");");
 109:                  }
 110:              }
 111:   
 112:              //TODO: Better '@"" + ' replacement to not cover inside strings
 113:              sb.Replace("@" + qChr + qChr + " + ", "");
 114:   
 115:              //add the dec statement
 116:              sb.Insert(0, "StringBuilder " + sbName + " = new StringBuilder(" + txt.Length + ");" + Environment.NewLine);
 117:   
 118:              //and return
 119:              return sb.ToString();
 120:          }
 121:          #endregion
 122:   
 123:          /// <summary>
 124:          /// Inserts text at current cursor location in the application
 125:          /// </summary>
 126:          /// <param name="application">application with activewindow</param>
 127:          /// <param name="text">text to insert</param>
 128:          private static void Paste(DTE2 application, string text)
 129:          {
 130:              //get the text document
 131:              var txt = (TextDocument)application.ActiveDocument.Object("TextDocument");
 132:   
 133:              //get an edit point
 134:              EditPoint ep = txt.Selection.ActivePoint.CreateEditPoint();
 135:   
 136:              //get a start point
 137:              EditPoint sp = txt.Selection.ActivePoint.CreateEditPoint();
 138:   
 139:              //open the undo context
 140:              bool isOpen = application.UndoContext.IsOpen;
 141:              if (!isOpen)
 142:                  application.UndoContext.Open("SmartPaster");
 143:   
 144:              //clear the selection
 145:              if (!txt.Selection.IsEmpty)
 146:                  txt.Selection.Delete();
 147:   
 148:              //insert the text
 149:              //ep.Insert(Indent(text, ep.LineCharOffset))
 150:              ep.Insert(text);
 151:   
 152:              //smart format
 153:              sp.SmartFormat(ep);
 154:   
 155:              //close the context
 156:              if (!isOpen)
 157:                  application.UndoContext.Close();
 158:          }
 159:   
 160:          #region "Paste As ..."
 161:   
 162:          /// <summary>
 163:          /// Public method to paste and format clipboard text as string the cursor 
 164:          /// location for the configured or active window's langage .
 165:          /// </summary>
 166:          /// <param name="application">application to insert</param>
 167:          public void PasteAsString(DTE2 application)
 168:          {
 169:              Paste(application, StringinizeInCs(ClipboardText));
 170:          }
 171:   
 172:          /// <summary>
 173:          /// Public method to paste and format clipboard text as comment the cursor 
 174:          /// location for the configured or active window's langage .
 175:          /// </summary>
 176:          /// <param name="application">application to insert</param>
 177:          public void PasteAsComment(DTE2 application)
 178:          {
 179:              Paste(application, CommentizeInCs(ClipboardText));
 180:          }
 181:   
 182:   
 183:          /// <summary>
 184:          /// Public method to paste format clipboard text into a specified region
 185:          /// </summary>
 186:          /// <param name="application">application to insert</param>
 187:          public void PasteAsRegion(DTE2 application)
 188:          {
 189:              //get the region name
 190:              const string region = "myRegion";
 191:   
 192:              //it's so simple, we really don't need a function
 193:              string csRegionized = "#region " + region + Environment.NewLine + ClipboardText + Environment.NewLine + "#endregion";
 194:   
 195:              //and paste
 196:              Paste(application, csRegionized);
 197:          }
 198:   
 199:          /// <summary>
 200:          /// Public method to paste and format clipboard text as stringbuilder the cursor 
 201:          /// location for the configured or active window's langage .
 202:          /// </summary>
 203:          /// <param name="application">application to insert</param>
 204:          public void PasteAsStringBuilder(DTE2 application)
 205:          {
 206:              const string stringbuilder = "sb";
 207:              Paste(application, StringbuilderizeInCs(ClipboardText, stringbuilder));
 208:          }
 209:   
 210:          #endregion
 211:      }
 212:  }
posted on Sunday, June 06, 2010 5:30:02 PM (Romance Daylight Time, UTC+02:00)  #    Comments [3]
# Tuesday, May 04, 2010

In a large Visual Studio solution, there are better ways of finding a particular class or method than visually hunting through the solution explorer tree.

Visual Studio 2008


Good old Goto Definition (F12) is great.
To quickly go back to where you pressed F12, Control - (minus) is useful but not very well known.

 vs_findallrefsBut what if you want to find where an interface is implemented, a method overridden or called from elsewhere?
Find All References (Control K, R) is painfully slow and it dumps everything in the Find Symbol Results window with the full file path (you can hack the registry to fix the format, but still...).

Within a source file, incremental search (Control I) is a great hidden secret - no dialog box, just start type and it immediately jumps to the first match. It has a big flaw: it doesn't look in hidden sections (closed regions).

To find a class or method which isn't in the source in front of you, you use Quick Find (Control F) or Find In Finds (Control Shift F). It's slow, and opens in a docked Find window. All those docked windows quickly become confusing.

 vs_quickfind

Visual Studio 2010


VS 2010 still has Slowly Find All References, but it now has a couple of extra goodies.

vs2010_callhierarchy View Call Hierarchy (Control K, T) has a nice graph of calls to/ calls from which is recursive. In other words, just like Reflector's Analyzer.

vs2010_navigateto There's a neat new Navigate To window (Control comma). It's not a docked window, and has a really cool incremental search of the solution. The search box allows camel hump searching (just the capitals), as well as sub-strings.

Coderush


coderush_referencesIf you're using Coderush Pro, you've had a nice version of call hierarchy in VS 2005 and 2008- it's the References window. There's a live-sync mode (overkill really), or you update it with Shift-F12 (or use the Refresh button). 

coderush_jumpto Also in Coderush, the jump to... context menu also shows context-sensitive overrides/ implementations and the next/prev reference can be tabbed to within a file (VS 2010 catches up with some features of this by highlighting other uses of a variable when the caret is on it).

My favourite feature of Coderush (and the free version, Coderush Xpress) is the Quick Nav dialog (Control Shift Q), which VS2010's Navigate To emulates. Quick incremental search, with camel-humps and substrings, and filter by members. coderush_quicknav

UPDATE: In the forthcoming Coderush Xpress for 2010, Quick Nav is gone. Damn. You must have the full Coderush or Resharper (see below). Okay, the built-in Navigate-To does the basic task, without the filtering, but still...

Resharper


resharper_findsymbol resharper_findtype

Resharper also has an equivalent of QuickNav/NavigateTo, with all the camel humps goodness (no substrings, but you can do * and ? wildcards). (Note that Resharper has two keyboard mappings; I'm using the Visual Studio mappings here).
Find Symbol - Shift Alt T  - finds types and type members. Find Type - Control T - just looks at types. 

resharper_contextnav The right-click context menus for implementors, base and especially usages very handy.
resharper_findusages You can get to Find Usages with Shift-F12. If there's only one usage (or base or inheritor), you go straight to it.

The reason that Coderush and Resharper can do the quick navigation is that they parse the solution, so for the first minute or so a large solution will show one of their processing messages (theoretically you can type away, but things are slow while they are working).

NDepend

I've just started playing with NDepend. There's a lot here to find your way around large code bases - even graphs - but for now I'll just note that the Visual Studio integration includes context menus which have similar functionality to the find usages described above:

ndepend_selectmethods

 

Wrap Up

Having been a Resharper user for many years, the powerful find symbol / type commands became something I used practically every few minutes while coding. It was a shock to find myself in an office with no Resharper, but fortunately the free Coderush Xpress came to the rescue. Visual Studio 2010 vanilla edition with no Resharper or Coderush is actually not as bad as VS 2008 and earlier were- Navigate To/control comma is quite handy and an easy key combo too. Of course I then installed the betas/ trials of the latest Coderush/ Resharper and saw what I was missing. Please boss, can I have a license??? :)

posted on Tuesday, May 04, 2010 10:18:54 PM (Romance Daylight Time, UTC+02:00)  #    Comments [0]