Tuesday, June 15, 2010

MvvmFoundation Extension – ValidationViewModelBase

Does some validation of the properties and has some structures to display the errors

MvvmFoundation @ codeplex

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace MvvmFoundation.Wpf
{
    /// <summary>
    /// ViewModelBase with Validation via IDataErrorInfo.
    /// </summary>
    public abstract class ValidationViewModelBase : ObservableObject, IDataErrorInfo
    {
        #region IDataErrorInfo Code
        /// <summary>
        /// Implement this in the Implementing Class. Throw an exception to allow validation to show on the UI.
        /// </summary>
        /// <param name="propertyName">The Property to Validate</param>
        public abstract string this[string propertyName] { get; }

        /// <summary>
        /// IDataErrorInfo.Error
        /// </summary>
        public string Error
        {
            get
            {
                if (HasValidationErrors)
                    return "Invalid";
                else
                    return "";
            }
        }

        /// <summary>
        /// Add an Error
        /// </summary>
        /// <param name="key">The key of the Error</param>
        /// <param name="value">The Value/Reason of the Error</param>
        public void AddError(string key, string value)
        {
            RemoveError(key);
            errorMessages.Add(key, value);
            RaisePropertyChanged("ValidationErrors");
            RaisePropertyChanged("HasValidationErrors");
        }

        /// <summary>
        /// Remove an Error by key
        /// </summary>
        /// <param name="key">the error key to remove</param>
        public void RemoveError(string key)
        {
            if (errorMessages.ContainsKey(key))
                errorMessages.Remove(key);
            RaisePropertyChanged("ValidationErrors");
            RaisePropertyChanged("HasValidationErrors");

        }

        /// <summary>
        /// String Representation of the Validation Errors
        /// </summary>
        public string ValidationErrors { get { return getErrorString(); } }

        /// <summary>
        /// Has Validation errors ?
        /// </summary>
        public bool HasValidationErrors { get { return errorMessages.Count > 0; } }

        #region Implementation Details

        private IDictionary<string, string> errorMessages = new Dictionary<string, string>();
        private string getErrorString()
        {
            StringBuilder value = new StringBuilder();
            foreach (KeyValuePair<string, string> item in errorMessages)
                value.AppendLine(string.Format("{0} - {1}", item.Key, item.Value));
            return value.ToString();
        }

        #endregion

        #endregion
    }
}

MVVM, ViewModel-DataContext and WPF

 

There seems to be a view taken by people,strategies-of-binding, that data binding from the View to the ViewModel should be done in a standard way and NO OTHER WAY.

This seems silly to me, as there are some many different ways to do it, and many more contexts when you need to use a different approach.

I preferred using UserControls for my views. The binding method I commonly use is to use the XAML <UserControl.DataContext>, NOT THE CODE BEHIND FILE

i.e.

<UserControl x:Class=”SomeNamespace.View.AControl” xmlns:ViewModel="clr-namespace:ANamespace.ViewModel"... >

<UserControl.DataContext><vm:AControlViewModel/></UserControl.DataContext>

</UserControl>

But in some cases this is limited. For Example, I want a ViewModeled UserControl “UC_B” within another ViewModeled UserControl “UC_A” and have “UC_A”s ViewModel use “UC_B”s ViewModel Properties

i.e. A Reusable DICOM Connection Parameters UserControl

<UserControl>

<UserControl.DataContext><vm:UC_SearchStudyControlViewModel/></UserControl.DataContext> <View:DicomConnectionParametersControl x:Name="SearchConnectionParameters" DataContext="{Binding Path=UC_ConnectionParametersViewModel}" DockPanel.Dock="Top" /> ...

</UserControl>

So now I’m still binding via the DataContext of the View to the View’s ViewModel, but in a different way i.e. the ViewModel is a Property of another ViewModel. This could not be done with the previous example, the Views ViewModel is within a different Views ViewModel.

i.e. B.DataContext = A.DataContext.Bs_ViewModel

Note there are other ways to pass data between ViewModel,i.e. Messenger , and there are some drawbacks, Binding to Large CLR Objects, in this implementation if you have a large ViewModel. The Dependencies between the ViewModels is also increased by this, i.e. if Bs_ViewModel changes A might need refactoring, but the simplicity of this solution, compared implementing a Messenger, outweighs the minor dependency increase.

Image Render Transformations

Image binding to Viewmodel :

  • ImageData (bitmap data)
  • ZOOM (zooming)
  • TX / TY (translation)
<Image Stretch="None" Source="{Binding Path=ImageData, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" > 
<Image.LayoutTransform>
<ScaleTransform ScaleX="{Binding Path=ZOOM}" ScaleY="{Binding Path=ZOOM}" />
</Image.LayoutTransform>
<Image.RenderTransform>
<TransformGroup>
<TranslateTransform X="{Binding Path=TX}" Y="{Binding Path=TY}"/>
</TransformGroup>
</Image.RenderTransform>
</Image>

WPF - Multiple Display

The Method

Use the System.Windows.Forms.Screen API to setup the displays.

The PrimaryScreen Property is the primary display, Other Displays are given by the AllScreens Property (System.Windows.Forms.Screen[])

Example with three windows (ImageViewer Application)

1)Take the StartUri element out of App.xaml and add the Startup event

<Application x:Class="DicomImageViewer.App"
             xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml"
             Startup="Application_Startup">
    <Application.Resources>

    </Application.Resources>
</Application>

2)Add the Startup event handler

private void Application_Startup(object sender, StartupEventArgs e)
{
    setDisplayRects(MainDisplayWindow, ImageBox1, ImageBox2);
}

2)This is an example of the ImageViewer for triple display
(note if 1 or 2 displays it subdivides the displays)

public void setDisplayRects(MainWindow main, ImageWindow img1, ImageWindow img2)
{
    System.Windows.Forms.Screen s0;
    System.Windows.Forms.Screen s1;
    System.Windows.Forms.Screen s2;
    switch (System.Windows.Forms.Screen.AllScreens.Count())
    {
        case 3:
            s0 = System.Windows.Forms.Screen.PrimaryScreen;
            main.Top = s0.WorkingArea.Top;
            main.Left = s0.WorkingArea.Left;
            main.WindowState = WindowState.Maximized;
            main.Show();

            s1 = System.Windows.Forms.Screen.AllScreens[1];
            img1.Top = s1.WorkingArea.Top;
            img1.Left = s1.WorkingArea.Left;
            img1.WindowState = WindowState.Maximized;
            img1.Show();
            img1.Owner = main;

            s2 = System.Windows.Forms.Screen.AllScreens[2];
            img2.Top = s2.WorkingArea.Top;
            img2.Left = s2.WorkingArea.Left;
            img2.WindowState = WindowState.Maximized;
            img2.Show();
            img2.Owner = main;

            break;

        case 2:
            s0 = System.Windows.Forms.Screen.PrimaryScreen;
            main.Top = s0.WorkingArea.Top;
            main.Left = s0.WorkingArea.Left;
            main.Show();

            s1 = System.Windows.Forms.Screen.AllScreens[1];
            img1.Top = s1.WorkingArea.Top;
            img1.Left = s1.WorkingArea.Left;
            img1.Width = s1.WorkingArea.Width;
            img1.Height = s1.WorkingArea.Height / 2;
            img1.Show();
            img1.Owner = main;

            img2.Top = s1.WorkingArea.Top + s1.WorkingArea.Height / 2;
            img2.Left = s1.WorkingArea.Left;
            img2.Width = s1.WorkingArea.Width;
            img2.Height = s1.WorkingArea.Height / 2;
            img2.Show();
            img2.Owner = main;

            break;

        case 1:
            s0 = System.Windows.Forms.Screen.PrimaryScreen;
            main.Top = s0.WorkingArea.Top;
            main.Left = s0.WorkingArea.Left;
            main.Width = s0.WorkingArea.Width;
            main.Height = s0.WorkingArea.Height / 3;
            main.Show();

            img1.Top = s0.WorkingArea.Top + s0.WorkingArea.Height / 3;
            img1.Left = s0.WorkingArea.Left;
            img1.Width = s0.WorkingArea.Width;
            img1.Height = s0.WorkingArea.Height / 3;
            img1.Show();
            img1.Owner = main;

            img2.Top = s0.WorkingArea.Top + (2 * s0.WorkingArea.Height / 3);
            img2.Left = s0.WorkingArea.Left;
            img2.Width = s0.WorkingArea.Width;
            img2.Height = s0.WorkingArea.Height / 3;
            img2.Show();
            img2.Owner = main;

            break;
    }
}

Wednesday, June 9, 2010

Some Medical Display Specs

 

http://news.soft32.com/nvidia-and-planar-to-fight-breast-cancer_5645.html
//16MP: 3200 x 5120(WHXGA), 10-bit gray                  -   (30.0') -   200 dpi
//[16384000] pixels (16/10 1.6 ratio)
*NOTE: Strikethrough denotes assumed values

//http://www.ndssi.com/products/dome/ex-grayscale/index.php
//5MP: 2048 x 2560(QSXGA), 8-bit gray                  -   (21.3') -   154 dpi
//5242880 pixels (5/4 1.25 ratio)
//3MP: 1536 x 2048(QXGA), 8-bit and 24-bit per pixel  -   (20.8') -   123 dpi
//3145728 pixels (4/3 1.33 ratio)
//2MP: 1200 x 1600(UXGA), 8-bit and 24-bit per pixel  -   (19.6') -   102 dpi
//1920000 pixels (5/4 1.25 ratio)

http://en.wikipedia.org/wiki/File:Vector_Video_Standards2.svg

http://en.wikipedia.org/wiki/Display_resolution

ScrollViewer Templates

 

Used in the resources, <UserControl.Resources> / <Window.Resources>, to allow scrollbars to be positioned differently

i.e. <ScrollViewer Style="{StaticResource ResourceKey=ScrollViewer_VerticalScrollBarRight_HorizontalScrollBarBottom}">

 

<UserControl.Resources>

<!-- ScrollViewer with VerticalScrollBar on the Left and HorizontalScrollBar at the Bottom -->
<Style x:Key="ScrollViewer_VerticalScrollBarLeft_HorizontalScrollBarBottom" TargetType="{x:Type ScrollViewer}">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ScrollViewer}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>

                    <ScrollContentPresenter Grid.Column="1"/>

                    <ScrollBar Name="PART_VerticalScrollBar"
                               Grid.Row="0"
                               Grid.Column="0"
                               Value="{TemplateBinding VerticalOffset}"
                               Maximum="{TemplateBinding ScrollableHeight}"
                               ViewportSize="{TemplateBinding ViewportHeight}"
                               Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
                    <ScrollBar Name="PART_HorizontalScrollBar"
                               Orientation="Horizontal"
                               Grid.Row="1"
                               Grid.Column="1"
                               Value="{TemplateBinding HorizontalOffset}"
                               Maximum="{TemplateBinding ScrollableWidth}"
                               ViewportSize="{TemplateBinding ViewportWidth}"
                               Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<!-- ScrollViewer with VerticalScrollBar on the Right and HorizontalScrollBar at the Bottom -->
<Style x:Key="ScrollViewer_VerticalScrollBarRight_HorizontalScrollBarBottom" TargetType="{x:Type ScrollViewer}">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ScrollViewer}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>

                    <ScrollContentPresenter Grid.Column="0"/>

                    <ScrollBar Name="PART_VerticalScrollBar"
                               Grid.Row="0"
                               Grid.Column="1"
                               Value="{TemplateBinding VerticalOffset}"
                               Maximum="{TemplateBinding ScrollableHeight}"
                               ViewportSize="{TemplateBinding ViewportHeight}"
                               Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
                    <ScrollBar Name="PART_HorizontalScrollBar"
                               Orientation="Horizontal"
                               Grid.Row="1"
                               Grid.Column="0"
                               Value="{TemplateBinding HorizontalOffset}"
                               Maximum="{TemplateBinding ScrollableWidth}"
                               ViewportSize="{TemplateBinding ViewportWidth}"
                               Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<!-- ScrollViewer with VerticalScrollBar on the Left and HorizontalScrollBar at the Top -->
<Style x:Key="ScrollViewer_VerticalScrollBarLeft_HorizontalScrollBarTop" TargetType="{x:Type ScrollViewer}">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ScrollViewer}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>

                    <ScrollContentPresenter Grid.Column="1" Grid.Row="1"/>

                    <ScrollBar Name="PART_VerticalScrollBar"
                               Grid.Row="1"
                               Grid.Column="0"
                               Value="{TemplateBinding VerticalOffset}"
                               Maximum="{TemplateBinding ScrollableHeight}"
                               ViewportSize="{TemplateBinding ViewportHeight}"
                               Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
                    <ScrollBar Name="PART_HorizontalScrollBar"
                               Orientation="Horizontal"
                               Grid.Row="0"
                               Grid.Column="1"
                               Value="{TemplateBinding HorizontalOffset}"
                               Maximum="{TemplateBinding ScrollableWidth}"
                               ViewportSize="{TemplateBinding ViewportWidth}"
                               Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<!-- ScrollViewer with VerticalScrollBar on the Right and HorizontalScrollBar at the Top -->
<Style x:Key="ScrollViewer_VerticalScrollBarRight_HorizontalScrollBarTop" TargetType="{x:Type ScrollViewer}">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ScrollViewer}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>

                    <ScrollContentPresenter Grid.Column="0" Grid.Row="1"/>

                    <ScrollBar Name="PART_VerticalScrollBar"
                               Grid.Row="1"
                               Grid.Column="1"
                               Value="{TemplateBinding VerticalOffset}"
                               Maximum="{TemplateBinding ScrollableHeight}"
                               ViewportSize="{TemplateBinding ViewportHeight}"
                               Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
                    <ScrollBar Name="PART_HorizontalScrollBar"
                               Orientation="Horizontal"
                               Grid.Row="0"
                               Grid.Column="0"
                               Value="{TemplateBinding HorizontalOffset}"
                               Maximum="{TemplateBinding ScrollableWidth}"
                               ViewportSize="{TemplateBinding ViewportWidth}"
                               Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

</UserControl.Resources>

DICOM Image Viewer – Useful Links

 

Imaging Overview - http://msdn.microsoft.com/en-us/library/ms748873.aspx

Optimizing Performance: 2D Graphics and Imaging - http://msdn.microsoft.com/en-us/library/bb613591.aspx

WPF Graphics Rendering Overview - http://msdn.microsoft.com/en-us/library/ms748373.aspx

Optimizing WPF Application Performance - http://msdn.microsoft.com/en-us/library/aa970683.aspx

Medical Display Specs - http://www.slideshare.net/PlanarEmbedded/trends-in-medical-displays-planar-systems-adi-abileah

WriteableBitmap - http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.aspx The new WriteableBitmap has much more performance than the original.  It provides tear-free, raster updates of a bitmap, which you can use an <Image> to display.

WPF Performance Tips

 

http://www.michaelflanakin.com/Weblog/tabid/142/articleType/ArticleView/articleId/1015/WPF-Performance-Tips.aspx

WPF Performance Tips

First off, let me say that this list isn't mine. I saw it on a presentation and wanted to make sure I had it to reference later. Enjoy!

  1. Reduce unnecessary invocations of the layout pass -- update a Transform rather than replacing it
  2. Use the most efficient Panel where possible -- don't use Grid if you need is Canvas
  3. Use Drawing which are cheaper than Shape objects
  4. Use StreamGeometry, which is a light-weight alternative to PathGeometry for creating geometric shapes
  5. Use DrawingVisual to render shapes, images, or text
  6. If you need thumbnail images, create a reduced-sized version of the image
    Note: By default, WPF loads your image and decodes it to its full size
  7. Decode the image to desired size and not to the default size
  8. Combine images into a single image, such as a film strip composed of multiple images
  9. Set BitmapScalingMode to LowQuality for smother animating when scaling bitmap to instruct WPF to use speed-optimized algorithm
  10. Set CachingHint when possible, to conserve memory and increase perf
  11. Brush selection is critical to good performance; don’t use a VisualBrush or DrawingBrush when an ImageBrush or SolidColorBrush would be sufficient
  12. To draw complex, static vector content that may need to be repeatedly re-rendered, consider rendering it into a RenderTargetBitmap first and drawing it as an image instead
    Note: Rendering images is usually faster than rendering vector content
  13. Remember to clear event handlers to prevent object from remaining alive (see this post on finding memory leaks
  14. Use Freezable objects when possible to reduce working set and improve perf
  15. Remember to virtualize
  16. Share resources -- if you use custom controls, define control resources at the application or window level or in the default theme for the custom controls
  17. Share a brush w/o copying by defining it as a resource
  18. Use static vs. dynamic resources
  19. Don’t use FlowDocument element when all you need is a Label or TextBlock
  20. Avoid binding to Label.Content property, use TextBlock instead if source is frequently changes.
    Note: String is immutable, so Label object’s ContentPresenter must regenerate new content
  21. Don't convert CLR object to XML if the only purpose is binding
    Note: Binding to XML content is slower than CLR objects
  22. Always prefer binding to an ObservableCollection<T> vs. List<T>
  23. When a brush is used to set the Fill or Stroke of an element, use the brush's Opacity value rather than element's Opacity property
  24. Disable hit testing (IsHitTestVisible = false) when possible
    Note: Hit test on large 3D surfaces is expensive

 

If you're hungry for more, feel free to dig thru the Optimizing WPF Application Performance section on MSDN.

C++ Signing

1)Use Linker Advanced options,(Project Properties> Linker> Advanced), “Delay Sign”=yes and “Key File”=$(ProjectDir)<key.snk>

2)Add a custom build step

sn -R $(TargetDir)$(TargetFileName) $(ProjectDir)<key.snk>

Note: Visual Studio will ignore the above command because an “outputs” value is not set. Just type a value in the field (I typed Success)

1>mt.exe : general warning 810100b3: x64\Debug\ClearCanvas.Dicom.Codec.Jpeg.dll is a strong-name signed assembly and embedding a manifest invalidates the signature. You will need to re-sign this file to make it a valid assembly.

https://connect.microsoft.com/VisualStudio/feedback/details/464524/mt-error-810100b3?wa=wsignin1.0

http://social.msdn.microsoft.com/Forums/en/vcprerelease/thread/61b58555-3579-40c9-9625-b17a9955e3f5

XBAP – content files

 

Test the difference between these and explain was the different “Build Action”’s do to these types of loading

// In project itself = "same directory on the server as my .xbap file"  
Uri uri = new Uri("pack://siteoforigin:,,,/StorageSCP.ass.xml");
// In project itself = compiled into main exe  
Uri uri2 = new Uri("pack://application:,,,/StorageSCP.ass.xml");
// In project itself = included as "loose content"  
Uri uri3 = new Uri("StorageSCP.ass.xml", UriKind.Relative);
StreamResourceInfo assXML = Application.GetRemoteStream(uri);
StreamResourceInfo assXML3 = Application.GetRemoteStream(uri3);
StreamResourceInfo assXML2 = Application.GetContentStream(uri2);

 

Also see what this does ?

Application.GetResourceStream(uri)

Get out of here

I’ve got a back log of topics here I’m just going to post, cos writer is getting messy

Wednesday, April 28, 2010

XBAP - SiteOfOrigin Files

 

I used this little snippet after a couple of hours hunting MSDN forums

StreamResourceInfo f = Application.GetRemoteStream(new Uri("pack://siteoforigin:,,,/"+fileSolutionPath));

//f.Stream //to your IO with this

Note: siteoforigin files are downloaded from the host on demand, so should be downloaded as needed.

Cool little beasty

http://msdn.microsoft.com/en-us/library/aa970494.aspx#Site_of_Origin_Files

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/9df060ca-019e-4443-8167-ccf511a73f84

I actually used this to copy some configuration files, DICOM Server Association files, to my Full Trusted XMAP home. I had made the Full Trust XMAP home (Environment.CurrentDirectory) a Temp folder i.e. System.IO.Path.Combine(System.IO.Path.GetTempPath(), "MyAppName") and needed to get the association list for a Storage SCP.

Thus

f.Stream.CopyTo((File.OpenWrite(Path.Combine(Environment.CurrentDirectory, fileSolutionPath))));

Tuesday, April 27, 2010

XBAP – Full Trust on the net

http://weblogs.asp.net/scottgu/archive/2009/10/26/wpf-4-vs-2010-and-net-4-0-series.aspx

Full Trust XBAP Deployment

Starting in WPF 4, the ClickOnce elevation prompt is also enabled for XAML Browser Applications (XBAPs) in Intranet and Trusted Zones, making it easier to deploy full-trust XBAPs. For XBAPs that require security permissions greater than the minimum code access security (CAS) permission grantset of the Intranet and Trusted Zones, the user will be able to click 'Run' on the ClickOnce elevation prompt when they navigate to the XBAP to allow the XBAP to run with the requested permissions.

While developing DVD Burner Application, I wondered how many people are going to use the application then I came across XBAP and Full Trust. Although not a standard application of a Web App, it seems that XBAP is allowing me to get Full Trust via the internet. Very promising…

The key testability point for me was the “Intranet” AND “Trusted Zones”.

I could deploy the application on my laptop with IIS7.0 over the internet, and test it using my remote IP. This allowed me to test without being on a domain.

The only thing to do was to add my Remote IP to the “Trusted Zones” in IE, as long as your app is signed.

Very Cool Stuff

VS2010 – cmd prompt .NET v3.5

I was trying to sign an assembly that I did have direct access to the code, in a Domain constrained SVN repo, and came across this:
http://buffered.io/2008/07/09/net-fu-signing-an-unsigned-assembly-without-delay-signing/
Well I did it all and found that VS2010 has ilasm 4.0, thus making an assembly that is .NET 4.0 targeted. Now I cant reference the assembly from a .NET 3.5 project.
I decided to create a shortcut that executed the older .NET tools i.e. “Visual Studio Command Prompt” 3.5
The two file, vcvarsall.bat and vcvars32.bat, control the way VS cmd prompt sets up the environment and should be copied as follows:
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\vcvars32.35.bat
C:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.35.bat

vcvars32.35.bat


@if not "%WindowsSdkDir%" == "" (
    @set "PATH=%WindowsSdkDir%bin;%WindowsSdkDir%bin\NETFX 4.0 Tools;%PATH%"
    @set "INCLUDE=%WindowsSdkDir%include;%INCLUDE%"
    @set "LIB=%WindowsSdkDir%lib;%LIB%"
)


@if exist "%VCINSTALLDIR%VCPackages" set PATH=%VCINSTALLDIR%VCPackages;%PATH% @set PATH=%FrameworkDir%%FrameworkVersion%;%PATH% @set PATH=%FrameworkDir%v2.0.50727;%PATH%
@set PATH=%FrameworkDir%%Framework35Version%;%PATH%

@set PATH=%VSINSTALLDIR%Common7\Tools;%PATH%



vcvars32.35.bat is modified to place the 3.5 tools first before 4.0.
Note: ilasm before v4.0 is v2.0.50727 so this is placed between the 3.5 and 4.0 versions
         i.e. 3.5 then 2.0 then 4.0

vcvarsall.35.bat


:x86
if not exist "%~dp0bin\vcvars32.35.bat" goto missing
call "%~dp0bin\vcvars32.35.bat"
goto :eof


vcvarsall.35.bat is only modified to call vcvars32.35.bat NOT the original vcvars32.bat(4.0)
Now just copy the “Visual Studio Command Prompt (2010)” shortcut and replace “vcvarsall.bat”  parameter with “vcvarsall.35.bat”
now .NET 2.0 ilasm runs from this new shortcut.