Friday, July 25, 2008
It's been a long time since I blogged anything specific on WPF -- I've been doing a lot of it lately, along with some Silverlight.  Recently I was experimenting with dragging tabs around on a TabControl at runtime.  My end goal is really to implement it with Silverlight 2, but I've found it's much easier to prototype things in WPF and then port them over because the debugging experience is easier with WPF.

I didn't want to create derived implementations of any classes - I wanted something that was non-intrusive to my code so I decided to use an attached property.  Attached properties are basically property values "attached" to a class at runtime - where the property itself isn't defined on the target but instead on some other type.  The cool thing about attached properties is they can register a change notification handler which gives them a reference to the object they are being placed on -- this is how the Spell Checker works with the TextBox in WPF.  All the code for the spell checking lives in the SpellChecker class and when you add the SpellCheck.IsEnabled property onto the TextBox, it adds handlers to the TextBlock's TextChanged property and adds all the nifty spell checking goodness without changing the code in TextBox.

Back to my drag/drop prototype.  So with this code, I can add the property to any TabControl and get a nice, simple drag/drop experience.  It's far from complete - it would be cooler if the tabs moved around as you dragged (they don't), but I was just prototyping here.

Here's the code:




using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace WpfApplication1
{
    public class DragDropTabManager
    {
        private static readonly DependencyProperty ManagerProperty =
            DependencyProperty.Register(typeof (DragDropTabManager).ToString(), typeof (DragDropTabManager),
                                        typeof (DragDropTabManager));

        public static readonly DependencyProperty EnabledProperty = 
            DependencyProperty.RegisterAttached("Enabled", typeof(bool), 
                                                typeof(DragDropTabManager),
                                                new PropertyMetadata(false, DDTM_EnabledChanged));

        private static void DDTM_EnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var tc = d as TabControl;
            if (tc != null)
            {
                var oldValue = (bool) e.OldValue;
                var newValue = (bool) e.NewValue;

                if (oldValue == true && newValue == false)
                {
                    var ddtm = tc.GetValue(ManagerProperty) as DragDropTabManager;
                    if (ddtm != null)
                    {
                        tc.PreviewMouseDown -= ddtm.TabItem_PreviewMouseDown;
                        tc.SetValue(ManagerProperty, null);
                    }
                }
                else if (oldValue == false && newValue == true)
                {
                    var ddtm = new DragDropTabManager();
                    tc.SetValue(ManagerProperty, ddtm);
                    tc.PreviewMouseDown += ddtm.TabItem_PreviewMouseDown;
                }
            }
        }

        public static bool GetEnabled(DependencyObject obj)
        {
            return (bool)obj.GetValue(EnabledProperty);
        }

        public static void SetEnabled(DependencyObject obj, bool value)
        {
            obj.SetValue(EnabledProperty, value);
        }

        private bool isMoving;
        private TabItem movingTabItem;
        private TabItem lastTab;
        private Point ptStart;

        void TabItem_PreviewMouseDown(object sender, MouseEventArgs e)
        {
            var ti = e.Source as TabItem;
            if (ti != null && e.LeftButton == MouseButtonState.Pressed)
            {
                var tc = ti.Parent as TabControl;
                if (tc != null)
                {
                    tc.MouseMove += tc_MouseMove;
                    tc.MouseLeftButtonUp += tc_MouseLeftButtonUp;

                    ptStart = e.GetPosition(tc);
                    movingTabItem = ti;
                }
            }
        }

        void tc_MouseMove(object sender, MouseEventArgs e)
        {
            var tc = sender as TabControl;
            if (tc == null)
                return;

            Point pt = e.GetPosition(tc);

            if (isMoving == false)
            {
                if (Math.Abs(pt.X - ptStart.X) > 10)
                {
                    movingTabItem.IsHitTestVisible = false;
                    movingTabItem.RenderTransformOrigin = new Point(.5, .5);
                    movingTabItem.RenderTransform = new TranslateTransform(0, 0);
                    tc.Cursor = Cursors.Hand;
                    Panel.SetZIndex(movingTabItem, 1);
                    isMoving = true;
                    tc.CaptureMouse();
                }
                return;
            }

            TabItem newPos = FindTabItem(tc, pt);
            if (newPos == null)
                tc.Cursor = Cursors.No;
            else
            {
                lastTab = newPos;
                var xform = movingTabItem.RenderTransform as TranslateTransform;
                if (xform != null)
                    xform.X = pt.X - ptStart.X;
                tc.Cursor = Cursors.Hand;
            }
        }

        void tc_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            var tc = sender as TabControl;
            Debug.Assert(tc != null);

            tc.ReleaseMouseCapture();
            tc.MouseMove -= tc_MouseMove;
            tc.MouseLeftButtonUp -= tc_MouseLeftButtonUp;

            if (isMoving == true)
            {
                isMoving = false;
                tc.Cursor = Cursors.Arrow;
                movingTabItem.RenderTransform = null;
                movingTabItem.IsHitTestVisible = true;

                Panel.SetZIndex(movingTabItem, 0);

                if (lastTab != null)
                {
                    if (lastTab != null && movingTabItem != lastTab)
                    {
                        int targetIndex = tc.Items.IndexOf(lastTab);
                        tc.Items.Remove(movingTabItem);
                        tc.Items.Insert(targetIndex, movingTabItem);

                        movingTabItem.Focus();
                    }
                }
            }
            movingTabItem = lastTab = null;
        }

        private static TabItem FindTabItem(UIElement parent, Point pt)
        {
            var fe = parent.InputHitTest(pt) as FrameworkElement;
            while (fe != null && fe.GetType() != typeof(TabItem))
                fe = VisualTreeHelper.GetParent(fe) as FrameworkElement;
            return fe as TabItem;
        }
    }
}
posted on 7/25/2008 2:09:45 PM (Central Standard Time, UTC-06:00)  #   
 Wednesday, July 23, 2008

It's been a while since I updated the ATAPI assembly, I've fixed a couple of minor bugs that were reported and updated the samples to compile with VS2008.  I've also added two new events onto the TapiPhone class so you can see state changes and button presses - somehow I missed that when I added the phone support.

Finally, there's now online documentation available at

http://www.julmar.com/atapi_help/index.aspx

You can download the updated assembly, help file and samples from

http://www.julmar.com/tapi/atapinet.zip

Have fun!

posted on 7/23/2008 11:54:45 AM (Central Standard Time, UTC-06:00)  #   
 Tuesday, May 13, 2008

I often show students in my classes how to add ILDasm to their tools menu - it's handy because you can then click on ILDasm and it will open the current module allowing you to look at the manifest, IL, etc.

1. Select Tools | External Tools. You’ll get the tools dialog:

Tools1.png

2. Enter ILDASM for the title and set the Command to the path for ILDASM. The path varies a bit from version to version of Visual Studio. For VS2008 you will find it at C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\ildasm.exe

3. Click the button to the right of Arguments and select "Target Path" and select the "Use Output Window" checkbox.



5. You should now be able to access Tools | ILDASM.

One student in another class recently asked how to get a keyboard shortcut to this. Michael Kennedy - our resident VS.NET shortcut king showed exactly how to do this. It turns out to be really simple -- go to Tools | Options and select Keyboard. Then 1,2,3:


posted on 5/13/2008 1:13:09 PM (Central Standard Time, UTC-06:00)  #   
 Tuesday, May 06, 2008


Last month, my colleague, Pinku Surana, wrote an article about .NET AppDomains and how they can be used to provide component isolation and make your applications more reliable.  If you missed it, you can read the Developments archives on Developmentor’s website.   This month, I’d like to continue exploring reliability and extensibility by introducing you to a new framework included with .NET 3.5: The Managed Add-in Framework (MAF), sometimes referred to as System.AddIn.

Developers (and managers) have long desired a way to easily create extensible applications that allow new features to be added without jeopardizing the stability of the existing code base.  The .NET framework has provided the underlying support to accomplish this from the very beginning through the reflection API and AppDomain support Pinku examined last month.  MAF builds on that fundamental support to provide a higher-level service that allow you to dynamically discover, load, secure and interact with external assemblies used to provide features for your application.  Several common architectural requirements are made trivial with MAF:

1)   Isolate aspects of your code for security reasons or partial-trust scenarios.

2)   Allow business partners to extend your application safely without access to the source code; Adobe Illustrator is a great example of this style of application.

3)   Separate volatile sections of your application out – where depending on the customer the application needs to execute different sets of code.

4)   Add or change code without unloading the application – for example pay-to-play scenarios, or where you need to update an assembly but the application must continue to run.

5)   Develop and evolve different sections of the application in parallel without any fear of destabilizing one based on the other.

If any of these scenarios sound like something you could utilize in your application then you should know about MAF!

At the center of the MAF-based application is the pipeline.

The Pipeline

Communication between add-ins and the host application is strictly regulated by the pipeline.  The pipeline is dynamically created by MAF through a set of loosely-coupled components which are used to version, marshal and transform the data as it passes back and forth between the host and each loaded add-in.

image001.png

Each section of the pipeline is contained in a separate assembly, loaded as necessary to manage the specific add-in.  MAF discovers each component and then loads them on request using reflection.   

For reliability, MAF allows optional isolation boundaries to be created between the add-in and host – everything to the left of the contract is loaded into the main (host) AppDomain and everything to the right loaded into a newly created AppDomain which has its own security permission set.  The isolation can also be done through cross-process calls if true process-level isolation is necessary.  Under the covers, the system uses traditional remoting calls to do the work of marshaling calls back and forth.

Breaking down the pipeline, there are three main parts starting in the center with the contract.

Contract

As you might expect, MAF is based on interface contracts.  Interfaces allow classes in an application to be loosely coupled, reducing the risk when changes are made between dependent sections of the code.   The interface contract is shared between the host and add-in and once it has been established, it should never be changed. 

Consider the simple example of a translator program.  The host will expect to load one or more translator add-ins that will do some work on an inbound string and return the results.  To accomplish this, I might create an ITranslator interface that looks like:

[AddInContract]

public interface ITranslator : IContract

{

   string Translate(string input);   
}


Notice that the interface derives from IContract.   This is a requirement for add-in contracts and is what will be used to provide marshalling support when the pipeline is established.  We also need to decorate the interface with the [AddInContract] attribute – this is the marker used by MAF to identify the contract when it is dynamically constructing the pipeline.  Both of these types come from the System.AddIn.Pipeline namespace in System.AddIn.Contract.dll.

Views

Moving to the edges, we find the views.  This is the code that the add-in and host directly interact with.  It represents a host or add-in specific “view” of the contract and, like the contract, is contained in a separate assembly.

Both of the view classes will echo the structure of the contract, but not actually be dependent upon the contract.  For example, our host side view might look like this:

public abstract class TranslatorHostView

{

   public abstract string Translate(string input);
}


The view is commonly exposed as an abstract class to make it look more natural when used by the host, but an interface will work just as well.  The host uses the view directly when communicating with any add-in based on the contract.

On the add-in side, we have an almost identical class – except we decorate this type with the [AddInBase] attribute so MAF knows which side of the pipeline this view is for (the add-in).   This is located in the System.AddIn.Pipeline namespace in the System.AddIn assembly.

When we create each add-in, the implementation will use this type as the base class.

[AddInBase]

public abstract class TranslatorAddInView

{

   public abstract string Translate(stringinp);
}

Adapters

The last piece of the pipeline is the adapters.  The adapters play a very special role in the pipeline – they are the glue that binds the contract to the view: implementing lifetime management and any necessary data conversion between the two ends. 

It may seem redundant to have this class, but by separating the view from the contract we introduce version tolerance into our architecture – the host can version independently of the add-in and vice-versa.  Depending on the situation, the adapters can be as simple as a pass-through class, or can provide higher-level services to allow non-serializable types to be marshaled across the isolation boundaries.

On the host side, the adapter will implement the view (remember it is either an interface or abstract class).  It will be passed a reference to the contract in the constructor and it is responsible for connecting the two together.

[HostAdapter]

public class TranslatorHostViewToContract :  

                     TranslatorHostView

{

   ITranslator _contract;

   ContractHandle _lifetime;

 

   public TranslatorHostViewToContract(

              ITranslator contract)  

   {

      _contract = contract;

      _lifetime = new ContractHandle(contract);

   }

 

   public override string Translate(string inp) 

   {

      return _contract.Translate(inp);
   }
}


In this simple example, the code caches off the contract interface.  This is a remoting proxy to the actual loaded add-in.  The adapter then implements the Translate method and passes it forward to the contract for implementation.  If we had non-serializable types, then the adapter would be responsible for converting them into something that was serializable. 

It also provides some lifetime management for the contract.  Because the contract is a remoting proxy and is likely running in a separate AppDomain (or even process) we have to be concerned with how long it lives.  MAF provides all the support for this through the ContractHandle class.  Most of the time, all you will need to do is store a ContractHandle in your host adapter and then pass it the inbound contract to wrap in your constructor.

Finally, in order for MAF to identify this class, it must be decorated with the [HostAdapter] attribute from the System.AddIn.Pipeline namespace out of the System.AddIn.dll.

The add-in side looks very similar, but does the opposite: it makes the add-in view look like the contract.

[AddInAdapter]

public class TranslatorAddInViewToContract :

                  ContractBase, ITranslator

{

   TranslatorAddInView _view;

 

   public TranslatorAddInViewToContract(

             TranslatorView view)  

   {

      _view = view;

   }

 

   public string Translate(string inp)  { 

      return _view.Translate(inp); 

   }

}

 
MAF passes the view to the constructor and the class caches the reference off in a field.  It implements the contract (ITranslator) and ContractBase which provides the implementation of the IContract interface for us (remember this was a required interface on our contract).  As the host makes calls to the contract interface, this class will translate those calls to the add-in view, which as you will see is the implementation provided by the add-in itself.  Note how the [AddInAdapter] attribute is used to mark this class so MAF can discover it.

If it seems like all the above is a lot of repetitious, boilerplate code well.. you’re right!  To make it easier on the developer to create the pipeline components, the MAF team has created a pipeline builder available at http://www.codeplex.com/clraddins.  It takes the contract assembly and then generates the views and adapters from it:

image003.jpg

Creating an add-in

Once the pipeline pieces are built you can create add-ins.  Each add-in provides an implementation of the abstract add-in view.  For example, we might provide a BabelFish add-in for universalized translation:

[AddIn(“BabelFishTranslator”,

       Description=“Universal translator”,

       Version=“1.0.0.0”, Publisher=“Zaphod Beeblebrox”]

public class BabelFishAddIn : TranslatorAddInView

{

   public string Translate(string input)

   {

     ...

   }
}


The add-in implements the AddInView, providing a concrete implementation of the Translate method.  It is decorated with the [AddIn] attribute that allows it to provide a name, version, description and other data the host can use to decide whether it is a useful translator.

Putting the pieces together – the directory structure

To properly identify each of the components necessary, MAF enforces a particular directory structure you need to follow for deployment.  Each piece is stored in a sub-directory off the root of the pipeline directory (this is typically the APPBASE where the host executable is stored). 

image006.png

The directory names are required, but not case sensitive – each directory holding a single piece of the pipeline that MAF will load dynamically when it is loading the add-in.  The host-side view is always located in the same directory as the host executable itself so it does not need a dedicated directory.  When creating your Visual Studio project, it is important to set the output directories appropriately so that you create the above directory structure.  In addition, all references between the components should be marked as CopyLocal = “false” in Visual Studio to ensure a local copy of the assembly isn’t placed into the sub-directory: 

image008.png

Discovering and loading add-ins from the host

The last piece of the puzzle is the actual loading of add-ins from the host.  This is done in three basic steps:

1.     Identify and catalog the add-ins

2.     Retrieve the list of specific add-ins based on view or name

3.     Activate and use the add-in

The first step is to identify the available add-ins for the host.  This is done through the System.AddIn.AddInStore class:

string[] errorList = AddInStore.Rebuild(

         PipelineStoreLocation.ApplicationBase);

 

Calling Rebuild forces MAF to walk the directory structure and create the pipeline database.  It stores this information in the pipeline root directory and returns a list of errors, if any occur.  The most common errors are missing pipeline components – where MAF is unable to locate some required portion of the pipeline such as a View or Adapter.

Next, the host will retrieve a list of add-ins based on the host view through the FindAddIns method – these will be the add-ins conforming to a specific contract (whatever the view/adapter is bound to):

Collection<AddInToken> addinTokens = AddInStore.FindAddIns(

                typeof(TranslatorHostView),

                PipelineStoreLocation.ApplicationBase);

 

The first parameter is the host view type – so MAF knows what add-ins we are looking for, the second is the pipeline root directory, which is the same directory passed to the Rebuild method and indicates where the pipeline database is stored.

 

The returning collection represents a series of tokens that are used to identify and control the add-ins.  This is how the host can examine, activate and unload the add-in.  To activate a specific add-in, you can take the token and call Activate:

 

Collection<AddInToken> addinTokens;

    ...

foreach (AddInToken token in addinTokens)
{

   TranslatorHostView view =

       token.Activate<TranslatorHostView>(

             AddInSecurityLevel.Internet);

  

   string hello = view.Translate(“Bonjour”);

     ...
}

 

The Activate call loads all the required components, instantiates the add-in type and returns the host view used to interact with the add-in.  Calls made to this object will be marshaled back and forth to the add-in using the pipeline.

 

Notice that the parameter passed to Activate indicates the security level required.  There are several overrides that allow you to dictate exactly how the add-in is loaded.  You can load the add-in into a specific AppDomain – the current one for best performance:

 

token.Activate<TranslatorHostView>(AppDomain.CurrentDomain)

 

You can specify a specific permission set to restrict the things the add-in can do on your behalf:

PermissionSet pset = ...;

token.Activate<TranslatorHostView>(pset);


Or you can specify a known permission set based on the CAS zones:

image010.png

Once the add-in is activated, the host can call it just like any other object – but never forget that you are likely crossing an isolation barrier!  Make sure to design your contracts so that you minimize the number of calls between the host and add-ins to ensure your performance doesn’t suffer.

When you are finished with the add-in, you can tell MAF to unload it through the AddInController associated with the view:

AddInController ctrl =    

      AddInController.GetAddInController(view);

ctrl.Shutdown();


This will unload the add-in side pipeline and then destroy the containing AppDomain so you release the resources associated with it.

There are many other capabilities MAF provides such as versioning, passing collections and WPF visuals, passing non-serializable types, etc.  You can get a pretty decent overview of the features from MSDN and the MAF team has a blog at http://blogs.msdn.com/clraddins/ that has some great, practical information on using this new framework.

posted on 5/6/2008 3:46:26 PM (Central Standard Time, UTC-06:00)  #   
 Thursday, April 03, 2008
Creating popup windows in browser-hosted applications (XBAP)
posted on 4/3/2008 8:47:05 AM (Central Standard Time, UTC-06:00)  #   
 Wednesday, February 06, 2008
Today at the GNET Michael Kennedy, Jason Whittington and I discovered a pretty interesting change in .NET 2.0 SP1 (included in .NET 3.5).

We knew they increased the thread pool limit from 25 threads/cpu to 250 threads/cpu, but we also found they changed the heuristics for creating threads to avoid creating a bunch of threads in a short period of time. In previous versions of .NET, threads were allocated at a rate of 1 every 500ms assuming there were queued items. This is documented behavior in MSDN.

In .NET 3.5, it appears to have been changed to a sliding scale where they slow down thread creation as more demand is placed on the pool. This is a change even from the beta and must have been slipped in at the last minute.

Michael documented the whole change on his blog here.. recommended reading.
posted on 2/6/2008 11:35:52 PM (Central Standard Time, UTC-06:00)  #   
 Wednesday, December 19, 2007

After a long beta period Microsoft pushed Visual Studio .NET 2008 (code named “Orcas”) out to MSDN in November - keeping their promise to deliver it by the end of the year. We’ve been using Orcas in many of our .NET classes for a while now and I for one, am pretty excited that it’s finally here. There’s a ton of new features and enhancements in this release that makes it almost a no-brainer to upgrade - I thought I’d take a moment and list my top ten favorites in no particular order:

#10: WPF designer (“Cider”)

I spend a lot of time on rich-client island and compared to the embarrassing support for WPF provided for VS2005 (through an add-on) VS2008 just plain rocks. Not only does the designer just work, the XAML intellisense is full featured and driven through a real XAML parser and not an XSD file. That means custom types and namespace completion actually functions how you would expect them to! The visual designer and XAML views stay synchronized and “mini” thumbnails and scaling bars are used to help you see what you are building. You can also see how the new Cider environment has borrowed from Blend - a Search box is present to filter properties down and the selection in the XAML pane is synchronized with the visual designer so if you highlight a tag, it selects the appropriate object in the visual pane.

#9: WPF and Windows Forms integration (“Crossbow”)

Hand in hand with the designer view, VS2008 improves the designer experience for Windows Forms to WPF integration. Specifically, ElementHost is now present in the toolbox and you can drag and drop WPF user controls right onto your Form! You get a live preview of the control and can edit the content right in the designer.

#8: ClickOnce improvements
Microsoft introduced a simplified way to deploy Windows Forms clients from the web with Visual Studio 2005 called ClickOnce. With 2008, they’ve improved the experience by allowing deployment through FireFox (previously only IE was supported), file associations so your application can be launched by activating a data file, better support for certificate expiration and changing the deployment location without resigning the application and support for Office add-in and WPF XAML Browser application deployment.

#7: Multi-framework targeting

This release of Visual Studio has a much-needed feature that I wish had been there before - the ability to target multiple versions of .NET. Specifically, you can select your target framework (2.0 SP1, 3.0 or 3.5) and the project types, toolbox, references, intellisense and features will be appropriately set to that version. This selection can occur during project creation - in the project templates dialog or on the project properties dialog. Be aware that selecting .NET Framework 2.0 actually means 2.0 SP1 and not the original .NET 2.0 framework released with Visual Studio 2005.

#6: Better intellisense support

Web developers benefit from this release too - Visual Studio now has intellisense and code completion support for JavaScript! It’s smart enough to look at the underlying type currently assigned and correctly infer the methods which are available. So, for example if you assign a numeric type, the dropdown will be populated with the proper methods available for the type. Visual Studio will also look at the comments applied to your methods - just like in C# and VB.NET, it will pull descriptions out of XML based comments on your JavaScript methods and display descriptions as tooltips when you are navigating the intellisense dropdown. Another cool feature added to Intellisense is the filtering applied - now if you press a letter and start typing, it will begin to restrict your choices displayed, not just scroll to that section. This filters the output and makes it easier to find what you are looking for.

#5: Organize your using statements

Visual Studio now has the ability to organize your using statements. As a project evolves, you often end up with a ton of using statements at the top of each file which are not really being used - either because the project template added it, or because you originally did use something from that namespace which was then removed later. Now you can right click on your using statements and sort and/or remove unused namespaces.

#4: Refactoring enhancements
The refactoring support was a welcome addition to VS 2005 - and Microsoft has enhanced the support in 2008 to support C# 3.0 features and allow you to refactor anonymous types, extension methods and lambda expressions.

#3: C# 3.0 support
Speaking of C# 3.0, that has got to be one of the coolest features - functional programming seems to be all the rage these days and introducing these features into C# 3.0 will allow you to be the coolest programmer on your floor. Things like automatic properties, lambda expressions (which reduce your typing for anonymous delegates), partial methods, anonymous types and extension methods will radically change how you can build applications. They can seem weird at first for some people, but don’t be afraid of them! They will cut down your typing and provide for some really cool ways to enhance and evolve your projects.

#2: Visual Studio Split View

It is becoming increasingly more common to have multiple monitors on developers desks. Building on the “split” window feature of VS 2005, Visual Studio 2008 now allows you to tile the window horizontally or vertically so you can split your design and code view across monitors! This works in any of the designers (ASP.NET, WinForms, WPF, etc.)

#1: Debugging the .NET source code
There are actually several debugging enhancements (try debugging JavaScript on the client-side for example - it works great!) but I think the coolest feature by far has got to be the debugging enhancements which will allow you to actually debug into the source code of the .NET framework. Scott Guthrie announced a few weeks ago on his blog that they intend to release the source code under the Microsoft Reference License. As part of that release, VS2008 will have integrated debugging support so that you can step into the framework libraries (ever want to know why your DataBind isn’t working??). Visual Studio will automatically download the source file from Microsoft’s server and you will get full watch and breakpoint support. This isn’t quite ready yet, but it’s coming soon and promises to be one of the biggest timesavers added to Visual Studio!


There’s a ton of other features that make VS 2008 a worthwhile upgrade - you can experience it yourself by downloading a free copy today (Express editions have been released) or upgrading from MSDN. VS 2008 co-exists just fine with 2005 and 2003 but be aware that the project format has changed and it’s not backward compatible so sharing projects with your coworkers may be slightly painful for you until everyone moves to the new release as you will have to maintain two project and solution files.
posted on 12/19/2007 10:56:11 AM (Central Standard Time, UTC-06:00)  #   
 Monday, November 19, 2007
Thanks to all those who attended the GNET in Tampa last week. We had a blast with all of you and wish you the best of luck going back and using all the cool tricks and technologies we showed you! As promised, here are the demos we build during the week - Demos. Enjoy!
posted on 11/19/2007 5:01:13 PM (Central Standard Time, UTC-06:00)  #   
 Thursday, September 06, 2007

Another interesting feature I hadn't noticed about VS.NET 2008 is the "Organize Using" command.  It allows you to sort and filter your using blocks on a file-by-file basis.  For large projects that have changed over time I could see how it could be very useful.

 

posted on 9/6/2007 8:47:15 AM (Central Standard Time, UTC-06:00)  #   
 Monday, August 13, 2007

If you right click on "References" in a VS2008 project you will find only two options now - "Add Reference" and "Add Service Reference".  The first is for local assemblies, and the second generates WCF-compatible proxies through SVCUTIL.EXE.  This happens to be one of the most obviously changed things in Orcas - the dialog presented is a more professional version than what was supplied with the original WCF CTP for VS.2005:

The "Discover" button above can locate IIS-hosted web services (not Self Hosted however) and it now shows operations directly which is pretty cool.  The best part is the "Advanced" button though.  Clicking that gives you:

Here you can control all the details of SVCUTIL.EXE -- how to reuse types, whether to generate public/private types, whether to genrate lower-level message contracts (giving complete control over the SOAP structure), and whether to generate asynchronous methods (ala the original web services references in .NET 2.0).

The bottom section - "Compatibility" allows you to generate a web service proxy using that original WS technology.  Clicking this will yield the traditional dialog you are probably used to:

So, if you were confused by the lack of a "Add Web Reference" in Orcas, fear no more - it's still there just a little harder to get to!

You might be asking - "Why would I want to generate one of those?  Why not just use WCF?"  Well, we have to keep in mind that not all our clients want to move to WCF just yet (as much as we want them to, there is a cost in deployment), and second, WCF isn't supported on pre-XP platforms.  There are still a lot of Windows 2000 systems out there!  For those clients, we have no choice but to use legacy web service proxies.

posted on 8/13/2007 1:58:09 PM (Central Standard Time, UTC-06:00)  #   
 Monday, July 30, 2007
Microsoft apparently didn't resign all the tools in the SDK supplied with Beta 2 of Visual Studio 2008.  This is actually documented in the readme that UISpy has this issue, which of course, I didn't read..
 
SVCUtil.exe also has this problem and it causes any proxy-generation from VS.NET 2008 to fail (as well as killing the client tester process which is spawned for WCF testing).  You'll know you've hit this error when the program crashes with a "System.IO.FileException" and indicates that it has failed strong-name validation.