Build a Proxy class based on Interface without implementing it

I have created the ability to implement an interface without implementing it at all, with no code!!!

Here is the general idea:

Providing an interface, Proxy class creates a class that implements this interface, and for each member, do either invoking the member on some other instance (FactoryProxy) or by invoking method (FacadeProxy).

** If you have another idea, I’d love to hear about it  (to be inherited from Proxy)**

Here is the class diagram:

Press to enlarge (open in new window)

Class diagram

  • Proxy (abstract) – base (and main) class that creates the proxy.
  • T – the type of interface to create the proxy based on.
  • FactoryProxy (abstract) – infrastructure base class to be inherited to provide the instance on which the member will be invoked on.
  • FacadeProxy (abstract) - infrastructure base class to be inherited to provide what to do when calling a member.

You can find the code here:

Continue reading

Another Enum trick

Lets say you need a flagged enum called Season,


[Flags]
public enum Season
{
    Unknown = 0x0,
    Spring = 0x1,
    Summer = 0x2,
    Autumn = 0x4,
    Winter = 0x8,
}

I found using Hexa with flagged enum easier, because of the need to multiply by two the previous value.

Best practice to include a Zero (0) value for an enum (line #4).

Always use comma(,) after a value, so the next change (source control) will be on the new values only.

Now you need to add an All seasons value.

The quick solution is trivial and easy:

[Flags]
public enum Season
{
    Unknown = 0x0,
    Spring = 0x1,
    Summer = 0x2,
    Autumn = 0x4,
    Winter = 0x8,
    All = 0xF,
}

Or even more elegant:

[Flags]
public enum Season
{
    Unknown = 0x0,
    Spring = 0x1,
    Summer = 0x2,
    Autumn = 0x4,
    Winter = 0x8,
    All = Spring | Summer | Autumn | Winter,
}

This is very nice, but what if the enum can grow and include more values?

‘All’ will be needed to changed also on every new value.

Here is my trick:
I use the default behavior of an enum, when a value is not given, the value is grater by one than the previous value.

So I add to values: Last (line #9) & All (line #10).

[Flags]
public enum Season
{
    Unknown = 0x0,
    Spring = 0x1,
    Summer = 0x2,
    Autumn = 0x4,
    Winter = 0x8,
    Last,
    All = (Last << 1) - 3,
}

‘Last’ has the value 9, because of the ‘Winter’ before it.
‘All’ is ‘Last’ multiply by 2 (<<1) than subtracted by 3, to meat the value of all values.

To ensure this trick will work, you must not skip values and always add values before ‘Last’.

Sure, for the Season enum is redundant, but think about user rights, features, and other enums that can grow.

Hope you like it,

Ofir

I Like Enum (Enum’s extension methods)

I’ve always struggled with manipulation on enums, it always look awkward and long.

So I started collecting methods to make it easier.

From MSDN:
The enum keyword is used to declare an enumeration, a distinct type consisting of a set of named constants called the enumerator list. Every enumeration type has an underlying type, which can be any integral type except char. The default underlying type of the enumeration elements is int. By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1

Here are the list of methods supported:

  • Any – Checks if the instance matches any of the values given (instead of (x & EnumType.A) != 0 || (x & EnymType.B) != 0, etc.).
  • All – Checks if the instance matches all of the values given  (instead of (x & EnumType.A) != 0 && (x & EnymType.B) != 0, etc.).
  • IsFlagged – Determines whether one or more bit fields are set in the current instance. (same as Enum.HasFlag – FrameWork 4.0).
  • BaseType - Get the base-type of the instance.
  • ToFlags - Yield all defined flags that is contained in the instance.
  • Join - Yield all defined flags that is contained both instances.
  • LeftOuterJoin - Yield all defined flags that is contained in the instance and not in the given value.
  • RightOuterJoin - Yield all defined flags that is contained in the given value  and not in the instance.
  • Parse - Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
  • TryParse – Tries to convert the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.

Here is a class for Enum Extension methods I collected and found useful:

using System;
using System.Collections.Generic;
using System.Linq;
using Ofir.Tools.Reflection;

namespace Ofir.Tools
{
    public static class EnumExtensions
    {
        /// <summary>
        /// holds specific comparers for each enum type, to make comparison faster
        /// </summary>
        private readonly static Dictionary<TypeCode, Func<Enum, Enum, bool>> Comparers =
           new Dictionary<TypeCode, Func<Enum, Enum, bool>>()
           {
              {TypeCode.Byte,   (e1, e2) => (Convert.ToByte(e1)   & Convert.ToByte(e2))   != 0},
              {TypeCode.SByte,  (e1, e2) => (Convert.ToSByte(e1)  & Convert.ToSByte(e2))  != 0},
              {TypeCode.Int16,  (e1, e2) => (Convert.ToInt16(e1)  & Convert.ToInt16(e2))  != 0},
              {TypeCode.Int32,  (e1, e2) => (Convert.ToInt32(e1)  & Convert.ToInt32(e2))  != 0},
              {TypeCode.Int64,  (e1, e2) => (Convert.ToInt64(e1)  & Convert.ToInt64(e2))  != 0},
              {TypeCode.UInt16, (e1, e2) => (Convert.ToUInt16(e1) & Convert.ToUInt16(e2)) != 0},
              {TypeCode.UInt32, (e1, e2) => (Convert.ToUInt32(e1) & Convert.ToUInt32(e2)) != 0},
              {TypeCode.UInt64, (e1, e2) => (Convert.ToUInt64(e1) & Convert.ToUInt64(e2)) != 0},
        };

        /// <summary>
        /// check if <see cref="@this"/> contains any of enums in flags
        /// <remarks>instead of using:<code>(enum1 & enum2) != 0</code></remarks>
        /// </summary>
        /// <param name="this">enum to check</param>
        /// <param name="flags">list of flags to look for</param>
        /// <returns>true if at least one of the flags contained in @this; otherwise, false</returns>
        public static bool Any(this Enum @this, params Enum[] flags)
        {
           return flags.Any(@enum => Contains(@this, @enum));
        }

        /// <summary>
        /// check if <see cref="@this"/> contains all of enums in flags
        /// </summary>
        /// <param name="this">enum to check</param>
        /// <param name="flags">list of flags to look for</param>
        /// <returns>true if all of the flags contained in @this; otherwise, false</returns>
        public static bool All(this Enum @this, params Enum[] flags)
        {
           return flags.All(@enum => Contains(@this, @enum));
        }

        /// <summary>
        /// check if <see cref="@this"/> contains the <see cref="flag"/>
        /// </summary>
        /// <param name="this">enum to check</param>
        /// <param name="flag">flag to look for</param>
        /// <returns>true if @this contains flag</returns>
        private static bool Contains(Enum @this, Enum flag)
        {
            return Comparers[@this.GetTypeCode()](@this, flag);
        }

        /// <summary>
        /// Check if the Enum declaration of <see cref="@this"/> has <see cref="FlagsAttribute"/>
        /// </summary>
        /// <param name="this">enum to check</param>
        /// <returns>true if the Enum declaration has <see cref="FlagsAttribute"/>; otherwise, false</returns>
        public static bool IsFlagged(this Enum @this)
        {
            return @this.HasAttribute<FlagsAttribute>();
        }

        /// <summary>
        /// Get the base-type of <see cref="@this"/> as in Enum declaration
        /// </summary>
        /// <param name="this">enum to check</param>
        /// <returns>the underlying type of th enum</returns>
        public static Type BaseType(this Enum @this)
        {
            Type res = null;
            switch (@this.GetTypeCode())
            {
                case TypeCode.SByte:
                    res = typeof (sbyte);
                break;
                case TypeCode.Byte:
                    res = typeof(byte);
                break;
                case TypeCode.Int16:
                    res = typeof (Int16);
                break;
                case TypeCode.UInt16:
                    res = typeof (UInt16);
                break;
                case TypeCode.Int32:
                    res = typeof (Int32);
                break;
                case TypeCode.UInt32:
                    res = typeof (UInt32);
                break;
                case TypeCode.Int64:
                    res = typeof (Int16);
                break;
                case TypeCode.UInt64:
                    res = typeof (UInt16);
                break;
                default:
                    throw new ArgumentOutOfRangeException("this", @this, "This cannot accor");
            }
            return res;
        }

        /// <summary>
        /// Checks if <see cref="@this"/> is the same as <see cref="other"/>
        /// </summary>
        /// <param name="this">enum to check</param>
        /// <param name="other">another enum to check</param>
        /// <returns>true, if @this and other are of the same type</returns>
        public static bool IsSameTypeAs(this Enum @this, Enum other)
        {
            return @this.GetType() == other.GetType();
        }

        /// <summary>
        /// Yield all defined flags that is contained in <see cref="@this"/>
        /// </summary>
        /// <param name="this">enum to itrate</param>
        /// <returns>enumerator that iterates each flag @this contains</returns>
        public static IEnumerable<Enum> ToFlags(this Enum @this)
        {
            if (!@this.IsFlagged())
            {
                yield return @this;
            }
            else
            {
                Func<Enum, Enum, bool> comparer = Comparers[@this.GetTypeCode()];

                foreach (Enum flag in Enum.GetValues(@this.GetType()))
                {
                    if (comparer(flag, @this))
                    {
                        yield return flag;
                    }
                }
            }
        }

        /// <summary>
        /// Yield all defined flags that is contained both in <see cref="@this"/> and in <see cref="other"/>
        /// </summary>
        /// <param name="this">enum to iterate </param>
        /// <param name="other">enum to join</param>
        /// <returns> enumerator that iterates each flag @this and also other contains</returns>
        public static IEnumerable<Enum> Join(this Enum @this, Enum other)
        {
            Func<Enum, Enum, bool> comparer = Comparers[@this.GetTypeCode()];
            return @this.ToFlags().Where(flag => comparer(other, flag));
        }

        /// <summary>
        /// Yield all defined flags that is contained in <see cref="@this"/> and not in <see cref="other"/>
        /// </summary>
        /// <param name="this">enum to include</param>
        /// <param name="other">enum to exclude</param>
        /// <returns>enumarator that itterates each flag @this contains, but not containd in other</returns>
        public static IEnumerable<Enum> LeftOuterJoin(this Enum @this, Enum other)
        {
            Func<Enum, Enum, bool> comparer = Comparers[@this.GetTypeCode()];
            return @this.ToFlags().Where(flag => !comparer(other, flag));
        }

        /// <summary>
        /// Yield all defined flags that is contained in <see cref="other"/> and not in <see cref="@this"/>
        /// </summary>
        /// <param name="this">enum to exclude</param>
        /// <param name="other">enum to include</param>
        /// <returns>enumarator that itterates each flag other contains, but not containd in @this</returns>
        public static IEnumerable<Enum> RightOuterJoin(this Enum @this, Enum other)
        {
            return other.LeftOuterJoin(@this);
        }

        /// <summary>
        /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
        /// A parameter specifies whether the operation is case-sensitive.
        /// </summary>
        /// <typeparam name="T">An enumeration type</typeparam>
        /// <param name="value">A string containing the name or value to convert</param>
        /// <param name="ignoreCase">true to ignore case; false to regard case</param>
        /// <returns>An object of type enumType whose value is represented by value</returns>
        public static T Parse<T>(this string value, bool ignoreCase = false) where T : struct
        {
            Type enumType = typeof (T);
            if (!enumType.IsEnum)
            {
                throw new ArgumentException(string.Format("T ({0}) is not an Enum", enumType.Name), "T");
            }
            return (T)Enum.Parse(enumType, value, ignoreCase);
        }

        /// <summary>
        /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
        /// A parameter specifies whether the operation is case-sensitive. The return value indicates whether the conversion succeeded.
        /// </summary>
        /// <typeparam name="T">The enumeration type to which to convert value</typeparam>
        /// <param name="value">The string representation of the enumeration name or underlying value to convert</param>
        /// <param name="result">When this method returns, contains an object of type T whose value is represented by value. This parameter is passed uninitialized</param>
        /// <param name="default">Default value to save in result, if parse is failed</param>
        /// <param name="ignoreCase">true to ignore case; false to consider case</param>
        /// <returns>true if the value parameter was converted successfully; otherwise, false</returns>
        public static bool TryParse<T>(this string value, out T result, T @default = default(T), bool ignoreCase = false) where T : struct
        {
            bool res;
            try
            {
                result = value.Parse<T>(ignoreCase);
                res = true;
            }
            catch (Exception)
            {
                result = @default;
                res = false;
            }
            return res;
        }

    }
}

This is an helpful and very useful class to get Attributes (used in IsFlagged method above)

using System;
using System.Collections.Generic;
using System.Linq;
namespace Ofir.Tools.Reflection
{
    public static class AttributeExtensions
    {

        public static IEnumerable<T> GetCustomAttributes<T>(this MemberInfo memberInfo, bool inherit = false) where T : Attribute
        {
            return Attribute.GetCustomAttributes(memberInfo, typeof(T), inherit).Cast<T>();
        }

        public static IEnumerable<T> GetAttributes<T>(this object @this, bool inherit = false) where T : Attribute
        {
            return @this.GetType().GetCustomAttributes(typeof(T), inherit).Cast<T>();
        }

        public static bool HasAttribute<T>(this object @this, bool inherit = false) where T : Attribute
        {
            return GetAttributes<T>(@this, inherit).Any();
        }

    }
}

Usage:

class Program
{
    [Flags]
    private enum Ofir
    {
        A = 1,
        B = 2,
        C = 4,
    }

    static void Main(string[] args)
    {
        Ofir ofir = Ofir.A | Ofir.B | Ofir.C;
        Console.WriteLine("ToFlags():");
        foreach (Ofir flag in ofir.ToFlags())
        {
            Console.WriteLine(flag);
        }
        ofir = Ofir.B | Ofir.C;
        bool b = ofir.All(Ofir.B, Ofir.A);
        Console.WriteLine("All(): {0}", b);
        Console.WriteLine("LeftOuterJoin():");
        foreach (Ofir onLeftOnly in ofir.LeftOuterJoin(Ofir.A | Ofir.B))
        {
            Console.WriteLine(onLeftOnly);
        }
        Ofir res = ofir ^ Ofir.C ^ Ofir.A;
        Console.WriteLine(ofir.All(Ofir.A, Ofir.B));
        ofir = "A,C".Parse<Ofir>();
        Console.WriteLine("Parse(): {0}", ofir);

        "A,B,c".TryParse(out ofir,ignoreCase:true);

        Console.WriteLine("TryParse(ignoreCase:true): {0}", ofir);

        Console.Read();
    }
}

Output:

ToFlags():
A
B
C
All(): False
LeftOuterJoin():
C
False
Parse(): A, C
TryParse(ignoreCase:true): A, B, C

Please use as you like.

I appreciate comments and ideas.

Enjoy,

Ofir

Unused References – VS2010 Add-in – top to bottom

I’ve always liked the idea to help developers like me, to create tool that help me develop faster and easier.

As a developer in a company with medium to large projects, I occasionally find an endless references list. The list usually there when I arrive, and probably will stay when I leave :-).

Some (Not all) developers add references while developing, and don’t remove them if they are no longer needed.

This post describes how I made a Visual Studio 2010 Add-in to remove unused references.

For the record, there are some add-ins available to install and use:

We’ll start with installing the SDK

There are different requirement for Visual Studio 2010 versions:

On Visual Studio 2010 start new Visual Studio Package named UnusedReferences:

New VS Package

New Visual Studio Package Project

A new projects are born: UnusedReferences, UnusedReferences_IntegratioTests & UnusedReferences_UnitTests.

I’ll focus on UnusedReferences project only.

Note: all referenced projects / dlls must be signed with Strong Name Key – snk.

This is how the project might look like:

  • UnusedReferences
    • Properties
    • References
    • Resources
    • GlobalSuppressions.cs
    • Guids.cs (GUIDs consts)
    • Key.snk
    • PkgCmdID.cs (Command Ids consts)
    • Resources.resx
    • source.extension.vsixmanifest
    • UnusedReferences.vsct (Declarative Xml – UI in Visual Studio)
    • UnusedReferencesPackage.cs (Initializing)
    • VSPackage.resx

UnusedReferences.vsct

[A full documentation at MSDN at How VSPackages Add User Interface Elements to the IDE]

Commands

Binding between Commands and GUID symbols, and define buttons and theirs parents.

Notice:
  • Every command must have a group as its parent (even if only child)
  • Every standard menu must also have a parent group.

Predefined Parent Ids (as highlighted) can be found at IDE-Defined Commands for Extending Project Systems

<Commands package="guidUnusedReferencesPkg">
  <Groups>
    <Group guid="guidUnusedReferencesCmdSet" id="MyMenuGroup" priority="0x0600">
      <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_PROJNODE"/>
    </Group>

    <Group guid="guidUnusedReferencesCmdReferencesSet" id="MyReferencesMenuGroup" priority="0x0601">
      <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_REFERENCEROOT"/>
    </Group>

    <Group guid="guidUnusedReferencesCmdSolutionSet" id="MySolutionMenuGroup" priority="0x0602">
      <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_SOLNNODE"/>
    </Group>
  </Groups>

  <!--Buttons section. defines the elements the user can interact with, like a menu command etc. -->
  <Buttons>
    <Button guid="guidUnusedReferencesCmdSet" id="cmdUnusedReferences" priority="0x0100" type="Button">
      <Parent guid="guidUnusedReferencesCmdSet" id="MyMenuGroup" />
      <Icon guid="guidImages" id="bmpPic1" />
      <Strings>
        <CommandName>cmdUnusedReferences</CommandName>
        <ButtonText>Unused References</ButtonText>
      </Strings>
    </Button>

    <Button guid="guidUnusedReferencesCmdReferencesSet" id="cmdReferencesUnusedReferences" priority="0x0101" type="Button">
      <Parent guid="guidUnusedReferencesCmdReferencesSet" id="MyReferencesMenuGroup" />
      <Icon guid="guidImages" id="bmpPic2" />
      <Strings>
        <CommandName>cmdReferencesUnusedReferences</CommandName>
        <ButtonText>Unused References</ButtonText>
      </Strings>
    </Button>

    <Button guid="guidUnusedReferencesCmdSolutionSet" id ="cmdSolutionUnusedReferences" priority="0x0102" type="Button">
      <Parent guid="guidUnusedReferencesCmdSolutionSet" id="MySolutionMenuGroup"/>
      <Icon guid="guidImages" id="bmpPicArrows"/>
      <Strings>
        <CommandName>cmdSolutionUnusedReferences</CommandName>
        <ButtonText>Unused References</ButtonText>
      </Strings>
    </Button>
  </Buttons>

  <!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
  <Bitmaps>
    <Bitmap guid="guidImages" href="Resources\Images_32bit.bmp" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>
  </Bitmaps>
</Commands>

Line 4: IDM_VS_CTXT_PROJNODE – context menu applies when the project node is selected.

Line 8: IDM_VS_CTXT_REFERENCEROOT- context menu applies when the references root node called “References” is selected.

Line 12: IDM_VS_CTXT_SOLNNODE - context menu applies when the solution node is selected.

Symbols

Binding between GUID symbols and GUID

<Symbols>
  <!-- This is the package guid. -->
  <GuidSymbol name="guidUnusedReferencesPkg" value="{3ba78fa2-5ca7-4e86-9442-6bd42efef0e6}" />

    <!-- This is the guid used to group the menu commands together -->
    <GuidSymbol name="guidUnusedReferencesCmdSet" value="{8b4bae75-7040-4604-857f-edf2b6044028}">

    <IDSymbol name="MyMenuGroup" value="0x1020" />
    <IDSymbol name="cmdUnusedReferences" value="0x0100" />
  </GuidSymbol>

  <GuidSymbol name="guidUnusedReferencesCmdReferencesSet" value="{3C164307-97A7-4CA0-83D4-C1271CB29679}">
    <IDSymbol name="MyReferencesMenuGroup" value="0x601"/>
    <IDSymbol name="cmdReferencesUnusedReferences" value="0x101"/>
  </GuidSymbol>

  <GuidSymbol name="guidUnusedReferencesCmdSolutionSet" value="{D09508DE-555C-45FF-84E8-FE100F540A51}">
    <IDSymbol name="MySolutionMenuGroup" value="0x602"/>
    <IDSymbol name="cmdSolutionUnusedReferences" value="0x102"/>
  </GuidSymbol>

  <GuidSymbol name="guidImages" value="{67e7b023-da0f-496d-b239-249a9f2fc3d8}" >
    <IDSymbol name="bmpPic1" value="1" />
    <IDSymbol name="bmpPic2" value="2" />
    <IDSymbol name="bmpPicSearch" value="3" />
    <IDSymbol name="bmpPicX" value="4" />
    <IDSymbol name="bmpPicArrows" value="5" />
  </GuidSymbol>
</Symbols>

UnusedReferencesPackage.cs

public sealed class UnusedReferencesPackage : Package
{
  /// <summary>
  /// Initialization of the package; this method is called right after the package is sited, so this is the place
  /// where you can put all the initilaization code that rely on services provided by VisualStudio.
  /// </summary>
  protected override void Initialize()
  {
    base.Initialize();

    // Add our command handlers for menu (commands must exist in the .vsct file)
    OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
    if ( null != mcs )
    {
      // Create the command for the menu item.
      CommandID menuCommandID = new CommandID(GuidList.guidUnusedReferencesCmdSet, (int)PkgCmdIDList.cmdUnusedReferences);
      MenuCommand menuItem = new MenuCommand(ProjectMenuItemCallback, menuCommandID);
      mcs.AddCommand(menuItem);

      menuCommandID = new CommandID(GuidList.guidUnusedReferencesCmdReferencesSet, (int)PkgCmdIDList.cmdReferencesUnusedReferences);
      menuItem = new MenuCommand(ReferencesMenuItemCallback, menuCommandID);
      mcs.AddCommand(menuItem);

      menuCommandID = new CommandID(GuidList.guidUnusedReferencesCmdSolutionSet, (int)PkgCmdIDList.cmdSolutionUnusedReferences);
      menuItem = new MenuCommand(SolutionMenuItemCallback, menuCommandID);
      mcs.AddCommand(menuItem);
    }
  }
}

Line 12: Find IMenuCommandService service

Lines 18, 22, 26: add the commands to service.

How to find services?

* Using (References)

using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;

Solution:

<span style="color: #99ccff;">IVsSolution</span> solution = GetService(typeof (SVsSolution)) as IVsSolution;

Selection:

IVsMonitorSelection monitorSelection = (IVsMonitorSelection)Package.GetGlobalService(typeof(SVsShellMonitorSelection));

Project:

From the selection get the Project with GetCurrentSelection method with IVsHierarchy.

IntPtr hierarchyPtr, selectionContainerPtr;
uint projectItemId;
IVsMultiItemSelect mis;
monitorSelection.GetCurrentSelection(out hierarchyPtr, out projectItemId, out mis, out selectionContainerPtr);
IVsHierarchy hierarchy = Marshal.GetTypedObjectForIUnknown(hierarchyPtr, typeof(IVsHierarchy)) as IVsHierarchy;
if (hierarchy != null)
{
  object prjItemObject;
  hierarchy.GetProperty(projectItemId, (int)__VSHPROPID.VSHPROPID_ExtObject, out prjItemObject);
  EnvDTE.Project prjItem = prjItemObject as EnvDTE.Project;
}

Get References:

Gets all references in project (used and unused).

EnvDTE.Project _dteProject;
.
.
foreach (VSLangProj.Reference vsReference in ((VSProject)_dteProject.Object).References)
{
  yield vsReference;
}

Now for the really tricky stuff

How to check if a reference is used or not?

Luckily, System.Reflection.Assembly has cool method: GetReferencedAssemblies()!

Need to load the assembly, first, but where is it?

We’ll build the project with BuildManager, and if success, get the assembly file path.

private bool Build(out string assemblyPath)
{
  bool res = false;
  assemblyPath = null;
  ProjectCollection projectCollection = new ProjectCollection();
  projectCollection.LoadProject(_dteProject.FullName);
  BuildRequestData buidlRequest = new BuildRequestData(_dteProject.FullName, new Dictionary<string, string>(), null, new string[] { "Build" }, null);
  BuildResult buildResult = Microsoft.Build.Execution.BuildManager.DefaultBuildManager.Build(new BuildParameters(projectCollection), buidlRequest);
  if (buildResult.OverallResult == BuildResultCode.Success)
  {
    ITaskItem[] items = buildResult.ResultsByTarget["Build"].Items;
    if (items.Length != 0)
    {
      res = true;
      assemblyPath = items[0].GetMetadata("FullPath");
    }
  }
  projectCollection.UnloadAllProjects();
  return res;
}

Now we have an assembly file, if you try to load it with Assembly.LoadFrom, you’ll be sorry.

This means you load your assembly to IDE environment and it cannot be unloaded!

This requires Sandbox to load and unload the assemblies, A.K.A, AppDomain.

Thanks to Valéry Letroye‘s post (MSDN Code for loading assembly to AppDomain, is not working in VS Add-in environment),

I’ve created a AssemblySandBoxLoader class:

public class AssemblySandBoxLoader : MarshalByRefObject
{
  private Dictionary<string, Assembly> _loadedAssemblies;

  //Create a SandBox to load Assemblies with "Full Trust"
  public static AssemblySandBoxLoader Sandbox(params string[] assembliesFilenames)
  {
    PermissionSet trustedLoadGrantSet = new PermissionSet(PermissionState.Unrestricted);
    AppDomainSetup trustedLoadSetup = new AppDomainSetup();
    trustedLoadSetup.ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

    AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
    AppDomain appDomain = AppDomain.CreateDomain("UnusedReferencesBuild", null, trustedLoadSetup, trustedLoadGrantSet);

    AssemblySandBoxLoader loader = appDomain.CreateInstanceAndUnwrap(
        typeof(AssemblySandBoxLoader).Assembly.GetName().FullName,
        typeof(AssemblySandBoxLoader).FullName,
        false,
        BindingFlags.Default,
        null,
        new object[] { assembliesFilenames },
        CultureInfo.InvariantCulture,
        null) as AssemblySandBoxLoader;

    return loader;
  }

  internal static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
  {
    return typeof(AssemblySandBoxLoader).Assembly;
  }

  public AssemblySandBoxLoader(string[] assembliesFilenames)
  {
    _loadedAssemblies = new Dictionary<string, Assembly>();
    foreach (string filename in assembliesFilenames)
    {
      Assembly asm = Assembly.LoadFrom(filename);
      _loadedAssemblies[filename] = asm;
    }
  }

  internal AppDomain GetAppDomain()
  {
    return AppDomain.CurrentDomain;
  }

  public static void Unload(AssemblySandBoxLoader sandBox)
  {
    if (sandBox == null) return;
    AppDomain appDomain = sandBox.GetAppDomain();
    AppDomain.Unload(appDomain);
  }

  public AssemblyName[] GetReferencedAssemblies()
  {
    List<AssemblyName> res = new List<AssemblyName>();
    foreach (Assembly assembly in _loadedAssemblies.Values)
    {
      res.AddRange(assembly.GetReferencedAssemblies());
    }
    return res.ToArray();
  }
}

Line 1: It must implement MarshalByRefObject because it cross domain, and from that reason, you can access to public member only.

Line 12: In IDE environment the class’ assembly can’t be found, so we help it to find it.

Line 48: use this method to unload the assembly in the AssemblySandBoxLoader c’tor – Can’t wait for GC to collect.

Line 55: here we get all the actual used references – all we need to do now is to substruct this from the VSProject.References we had earlier.

Now lets see some screen shots

Right click on References icon (can be also on project icon and on solution icon).

Context Menu on References icon

Context Menu on References icon

And When Unused References is pressed, this dialog is shown

List of projects

List of projects

Projects has no details to keep it light.
Press the ‘Build’ button to get all references (used and unused).

And voilà we can see all references

Project is built with it's references

Project is built with it’s references

Now we can press each unused reference and remove it from the project directly.

Enjoy,

Ofir

WPF – How to response to DataContextChanged?

I had a problem with Expander:

When the Expander is open, and later on, change context to something with larger content,

the Expander, does not resize and display partialy data with Scrollbars.

User need to collapse and re-expand it so the Expander will display it’s content - unacceptable.

See images below:

Image

Expander displays all content – fits.

Image

Expander displays partial content.

The first solution that comes to mind is of course to handle event in code behind.

But if you don’t want to mess with code behind, than it’s a little tricky.

DataContextChanged is a regular event and not a RoutedEvent, so we cannot useEventTrigers.

The solution is to use a Behavior with Property and Value to be set on the AssociatedObject which listen to DataContextChanged.

The Value will be set for the Property of the AssociatedObject (see line 20).

public class DataContextChangedListenerBehavior : Behavior<FrameworkElement>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.DataContextChanged += new DependencyPropertyChangedEventHandler(OnAssociatedObjectDataContextChanged);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.DataContextChanged -= new DependencyPropertyChangedEventHandler(OnAssociatedObjectDataContextChanged);
    }

    void OnAssociatedObjectDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        object oldValue = AssociatedObject.GetValue(Property);
        if (!object.Equals(oldValue, Value))
        {
            AssociatedObject.SetValue(Property, Value);
        }
    }

    public static readonly DependencyProperty PropertyProperty =
        DependencyProperty.Register("Property", typeof(DependencyProperty), typeof(DataContextChangedListenerBehavior));

    public DependencyProperty Property
    {
        get { return (DependencyProperty) GetValue(PropertyProperty); }
        set { SetValue(PropertyProperty, value); }
    }

    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof (object), typeof (DataContextChangedListenerBehavior),
                                    new PropertyMetadata(null, null, OnCoerceValueCallback));

    private static object OnCoerceValueCallback(DependencyObject d, object basevalue)
    {
        DependencyProperty prop = (DependencyProperty) d.GetValue(PropertyProperty);
        TypeConverter converter = TypeDescriptor.GetConverter(prop.PropertyType);
        object val;
        try
        {
            if (converter != null && converter.CanConvertFrom(basevalue.GetType()))
            {
                val = converter.ConvertFrom(basevalue);
            }
            else
            {
                val = Convert.ChangeType(basevalue, prop.PropertyType);
            }
        }
        catch
        {
            val = basevalue;
        }
        return val;
    }

    public object Value
    {
        get { return GetValue(ValueProperty); }
        set
        {
            SetValue(ValueProperty, value);
        }
    }
}

Usage:

<Expander DataContext="{Binding SomeData}" >
    <i:Interaction.Behaviors>
        <b:DataContextChangedListenerBehavior Property="Expander.IsExpanded" Value="False" />
    </i:Interaction.Behaviors>
</Expander>

Enjoy,

Ofir

Follow

Get every new post delivered to your Inbox.