Silverlight ObservableCollection with AddRange

Silverlight is when you have to add a lot of items.
I know, I know maybe you should choose another way to show that data,
but leaving philosophical-ui design discussions, the real problem is that
usually those components are bind  to ObservableCollections.

ObservableCollections are a bit of an exhibitionist.
Each time you add an item it will yell

Hey!! Yoo-hoo! HEY!!! YOU!!
I'm HEREEEEEEEEEEEEEEEEEE!!!!
Look at me!! Look at Me!!! Look Mom No Hands!!! Look Dad no Feet!!! HEY!!!!!!!!

So if you have some code like:

for(int i=0;i<10000;i++)
{
    comboItems.Add("item" + i);
}


A nice thing will be to be able to do something like:

var items = new String[10000] 
for(int i=0;i<10000;i++) 
{ 
    items[i]="item" + i; 
} 
comboItems.AddRange(items); 


And then provide just ONE notification of Collection Changed instead of a lot of
little cries for attention.

Well that is the reason for this new version of ObservableCollection that I call
RangeObservableCollection:

using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace Utils
{
    public class RangeObservableCollection<T> : ObservableCollection<T>
    {
        private bool _suppressNotification = false;

        public RangeObservableCollection() : base() { }

        public RangeObservableCollection(IEnumerable<T> collection) : base(collection) { }

        protected override void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (!_suppressNotification) base.OnPropertyChanged(e);
        }

        protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (!_suppressNotification)
                base.OnCollectionChanged(e);
        }

        /// <summary>
        /// Adds a collection suppressing notification per item and just raising a notification
        /// for the whole collection
        /// </summary>
        /// <param name="list"></param>
        public void AddRange(IEnumerable<T> list)
        {
            if (list == null) throw new ArgumentNullException("list");
            _suppressNotification = true;
            foreach (T item in list)
            {
                Add(item);
            }
            _suppressNotification = false;
            OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Reset));
        }

    }
    
}

Interop Structures to UnManaged Dlls

For VB6 applications it is common to rely on OS or Kernel API Calls. Some of those APIs might
need you to send data back and for the native API.

Marshalling in .NET can be complicated and bothersome. I have published several posts about
interop. But it usually depends on adding several marshalling attributes and even tricks specially for
fixed strings.

So I decided to provide a more a simpler approach for conversion. In this approach you just need to things:

1. Your VB6 types or structs will be mapped to .NET classes
2. All VB6 type or struct fields will be mapped to public fields
3. An attribute must be used on those fields to indicate the field length, for arrays or strings.
4. Extension methods .AsString() .SetFromString and .GetClassLength will handle all the complexities of setting the struct fields.

Let’s see an example:

Type EmployeeRecord
    FirstName As String * 5
    LastName As String * 5
End Type

That vb6 type will be mapped in .NET following this approach to:

    public class EmployeeRecord 
    {
        [FixedLength(5)]
        public string FirstName = "Mau";
        [FixedLength(5)]
        public string LastName = "Rojas";

    }

You can then simple use that class in .NET

var emp = new EmployeeRecord {FirstName="Mauricio",LastName="Rojas"} ;
var str = emp.AsString();
//This str value will be "MauriRojas" the helper extension methods
// .AsString and .SetFromString will handle setting the internal class fields

All that is very good but how is this used in Marshalling?? Well very simple. Let’s say you have a Dll called foo.dll
with a function foo that receives an EmployeeRecord:

        [DllImport("foo.dll")]
        public static extern int foo(IntPtr Struct);

Then if you want to call that function you will do something like:

            var emp = new EmployeeRecord { FirstName="Ann",LastName="Smith"};
            string str = emp.AsString();
            var ptr = IntPtr.Zero;
            ptr = Marshal.StringToBSTR(str);
            //or 
            ptr = Marshal.StringToHGlobalAnsi(str);
            //or
            ptr = Marshal.StringToHGlobalAuto(str);
            //or
            ptr = Marshal.StringToHGlobalUni(str);

            //And call the native function
            foo(ptr);

If the function modifies the structure and you want to reflect those changes then you will do something like:

str = Marshal.PtrToStringAnsi(ptr,typeof(EmployeeRecord).GetClassLength())
emp.SetFromString(str);

This solution can also be applied for more complex structures. For example:

    public class EmployeeRecord 
    {
        [FixedLength(5)]
        public string FirstName = "Mau";
        [FixedLength(5)]
        public string LastName = "Rojas";

    }

    public class Record1
    {
        public int field1;
        [FixedLength(10)]
        public string field2 = "";
        public EmployeeRecord rec = new EmployeeRecord();
    }

    public class GeneralInfo
    {
        public int field1;
        [ArrayLength(5)]
        [FixedLength(2)]
        public String[] countrycodes = { "cr","es","mx","pa","ni"};
        [FixedLength(2)]
        public EmployeeRecord[] employees;
    }

If you want to try it out this is the link to the CODE

Interop: BinaryCompatibilty for VB6 Migrations

In VB6 when you have an ActiveX Library it was very important to use
the BinaryCompatibility setting to make sure that your applications did not break after a change.

So let’s first introduce what is binary compatibility and how to accomplish that in .NET.

Binary Compatibility allows to make changes to your components or COM classes without recompiling
every application you've made that uses the component.
And why do you need it. Why compatibility breaks.
On lets see.

An ActiveX Control or DLL expose Public interfaces.
Those interfaces have all of the properties, methods, events, etc. that you've marked as Public.
In other words, everything you've added that shows in Intellisense while working outside of your component.

Now let's say you have create a class, with two Methods Method1 and Method2

When you compile, VB generates all the COM infraestructure you need for your component.
It defines a CoClass and an interface and an entry for each method.

For a vb class with two methods:

Sub Method1()

End Sub

Sub Method2()

End Sub

It will produce a typelib like:

// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: <could not determine filename>
[
  uuid(8ABA2C0C-7CCA-40CD-A944-56707566634A),
  version(1.0)
]
library Project1
{
    // TLib :     // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface _Class1;

    [
      odl,
      uuid(6B86684C-B3DD-4680-BF95-8DEE2C17AF5B),
      version(1.0),
      hidden,
      dual,
      nonextensible,
      oleautomation
    ]
    interface _Class1 : IDispatch {
        [id(0x60030000)]
        HRESULT Method1();
        [id(0x60030001)]
        HRESULT Method2();
    };

    [
      uuid(C71C7AB0-552A-4D5D-A9FB-AF33830A697E),
      version(1.0)
    ]
    coclass Class1 {
        [default] interface _Class1;
    };
};

As you can see in the typelib there are IDs associated to each coclass, interface and
methods. Those IDs are the ones use when you generate the .exe file for your application.
Now if you modify your Class to:

 

Sub Method3()

End Sub

Sub Method4()

End Sub

Sub Method1()

End Sub

Sub Method2()

End Sub

and you use No Compatibility the typelib after your changes will be:

// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: <could not determine filename>

[
  uuid(FE5C56C2-E03A-4DC0-994D-B68543C72A46),
  version(1.0)
]
library Project1
{
    // TLib :     // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface _Class1;

    [
      odl,
      uuid(A3032E1E-52FE-42E0-98FF-84A9DD4FD8C3),
      version(1.0),
      hidden,
      dual,
      nonextensible,
      oleautomation
    ]
    interface _Class1 : IDispatch {
        [id(0x60030000)]
        HRESULT Method3();
        [id(0x60030001)]
        HRESULT Method4();
        [id(0x60030002)]
        HRESULT Method1();
        [id(0x60030003)]
        HRESULT Method2();
    };

    [
      uuid(72721504-CC56-4BB9-9447-C7193FE8C02D),
      version(1.0)
    ]
    coclass Class1 {
        [default] interface _Class1;
    };
};

As you can see, now the ids for the methods, CoClass are different, so your applications will return errors like: Error 430 (Automation error, the component dies horribly) or Error 429 (can't create the object at all)

But if you instead used BinaryCompatibility then the typelib for your class will be:

// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: <could not determine filename>
[
  uuid(8ABA2C0C-7CCA-40CD-A944-56707566634A),
  version(1.1)
]
library Project1
{
    // TLib :     // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface _Class1;
    [
      odl,
      uuid(6E9C59C3-82D7-444C-92FB-01B49D91A2FF),
      version(1.1),
      hidden,
      dual,
      nonextensible,
      oleautomation
    ]
    interface _Class1 : IDispatch {
        [id(0x60030002)]
        HRESULT Method3();
        [id(0x60030003)]
        HRESULT Method4();
        [id(0x60030000)]
        HRESULT Method1();
        [id(0x60030001)]
        HRESULT Method2();
    };

    [
      uuid(C71C7AB0-552A-4D5D-A9FB-AF33830A697E),
      version(1.1)
    ]
    coclass Class1 {
        [default] interface _Class1;
    };

    typedef [uuid(6B86684C-B3DD-4680-BF95-8DEE2C17AF5B), version(1.0), public]
    _Class1 Class1___v0;
};

If you compare now the two typelibs you can see the Method1 and Method2 keep the same ids.

For each version a typedef is generated that will point to the last version. For example adding a Method5 will add new entry like:


    typedef [uuid(6B86684C-B3DD-4680-BF95-8DEE2C17AF5B), version(1.0), public]
    _Class1 Class1___v0;

    typedef [uuid(6E9C59C3-82D7-444C-92FB-01B49D91A2FF), version(1.1), public]
    _Class1 Class1___v1;

Well that is what binary compatibility does. Now how to achieve binary compatibility in .NET

Binary Compatibility in .NET

Achieving binary compatibility in .NET is really easy. You just need to give more information to
make explicit how your typelib information will be. I will follow an approach as the one I already explained in this post:
http://blogs.artinsoft.net/mrojas/archive/2010/06/23/exposing-c-classes-thru-interop.aspx

Lets take our previous example:

using System;
using System.Runtime.InteropServices;

namespace InteropExamples
{
    public class Class1
    {
        public void Method3()
        {
        }
        public void Method4()
        {
        }
        public void Method1()
        {
        }
        public void Method2()
        {
        }
        public void Method5()
        {
        }
    }
}

In previous posts I had recommended using partial classes and using interfaces to explicitly specify what you what to be seen in COM. This means you start up with something like:

  public partial class Class1
    {
        public void Method3()
        {
        }
        public void Method4()
        {
        }
        public void Method1()
        {
        }
        public void Method2()
        {
        }
    }

    [ComVisible(true)]
    public interface _Class1
    {
        void Method3();
        void Method4();
        void Method1();
        void Method2();

    }
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComDefaultInterface(typeof(_Class1))]
    partial class Class1 : _Class1
    {
        #region _Class1 Members

        void _Class1.Method3()
        {
            Method3();
        }

        void _Class1.Method4()
        {
            Method4();
        }

        void _Class1.Method1()
        {
            Method1();
        }

        void _Class1.Method2()
        {
            Method2();
        }

        #endregion
    }

Now to make this code binary compatible then you have to make sure that the tlb file generated for your class is almost identical to that generated before. To acomplish that we must make sure that we your methods, interfaces and classes have the same guids and ids. Lets see how:

using System;
using System.Runtime.InteropServices;

namespace InteropExamples
{
    public partial class Class1
    {

        public void Method3()
        {
            System.Windows.Forms.MessageBox.Show("3 N");
        }

        public void Method4()
        {
            System.Windows.Forms.MessageBox.Show("4 N");
        }

        public void Method5()
        {
            System.Windows.Forms.MessageBox.Show("5 N");
        }


        public void Method1()
        {
            System.Windows.Forms.MessageBox.Show("1 N");
        }

        public void Method2()
        {
            System.Windows.Forms.MessageBox.Show("2 N");
        }
    }

    [ComVisible(true)] //This to make the interface Visible for COM
    [TypeLibType((TypeLibTypeFlags)((short)TypeLibTypeFlags.FHidden |
        (short)TypeLibTypeFlags.FDual |
        (short)TypeLibTypeFlags.FNonExtensible |
        (short)TypeLibTypeFlags.FOleAutomation))] //This to use the same flags as in previous tlb
    [Guid("9BAFD76D-8E6B-439C-8B6D-37260BFA3317")] //This is to make the class have the guid
    public interface _Class1
    {
        [DispId(0x60030000)]
        void Method1();
        [DispId(0x60030001)]
        void Method2();
        [DispId(0x60030002)]
        void Method3();
        [DispId(0x60030003)]
        void Method4();
        [DispId(0x60030004)]
        void Method5();


    }

    [ComVisible(true)] //This to make the class Visible for COM
    [ClassInterface(ClassInterfaceType.None)] //This is to make sure that we have control on interface generation
    [ComDefaultInterface(typeof(_Class1))] //To set default interface
    [ProgId("Project1.Class1")] //To set ProgId 
    [Guid("C71C7AB0-552A-4D5D-A9FB-AF33830A697E")] //Maintain same Guid.
    partial class Class1 : _Class1, Class1___v0, Class1___v1
    {
        #region _Class1 Members

        void _Class1.Method3()
        {
            Method3();
        }

        void _Class1.Method4()
        {
            Method4();
        }

        void _Class1.Method1()
        {
            Method1();
        }

        void _Class1.Method2()
        {
            Method2();
        }

        #endregion


        #region Class1___v0 Members

        void Class1___v0.Method1()
        {
            Method1();
        }

        void Class1___v0.Method2()
        {
            Method2();
        }

        void Class1___v0.Method3()
        {
            Method3();
        }

        void Class1___v0.Method4()
        {
            Method4();
        }

        void Class1___v0.Method5()
        {
            Method5();
        }

        #endregion

        #region Class1___v1 Members

        void Class1___v1.Method1()
        {
            Method1();
        }

        void Class1___v1.Method2()
        {
            Method2();
        }

        void Class1___v1.Method3()
        {
            Method3();
        }

        void Class1___v1.Method4()
        {
            Method4();
        }

        void Class1___v1.Method5()
        {
            Method5();
        }

        #endregion
    }

    //This is to keep compatibility with old versions
    //we cannot generate a typedef so we will need to add all of the versions
    //for BinaryCompatibility
    [ComVisible(true)]
    [Guid("6B86684C-B3DD-4680-BF95-8DEE2C17AF5B")]
    [TypeLibType(TypeLibTypeFlags.FHidden)]
    public interface Class1___v0
    {
        [DispId(0x60030000)]
        void Method1();
        [DispId(0x60030001)]
        void Method2();
        [DispId(0x60030002)]
        void Method3();
        [DispId(0x60030003)]
        void Method4();
        [DispId(0x60030004)]
        void Method5();
    }

    //This is to keep compatibility with old versions
    //we cannot generate a typedef so we will need to add all of the versions
    //for BinaryCompatibility
    [ComVisible(true)]
    [Guid("4A7A3317-BF13-443E-9DB0-2C5EA21F00CA")]
    [TypeLibType(TypeLibTypeFlags.FHidden)]
    public interface Class1___v1
    {
        [DispId(0x60030000)]
        void Method1();
        [DispId(0x60030001)]
        void Method2();
        [DispId(0x60030002)]
        void Method3();
        [DispId(0x60030003)]
        void Method4();
        [DispId(0x60030004)]
        void Method5();
    }

}

Sadly in .NET you cannot use Interface Inheritance in COM. If there is interface inheritance YOU HAVE TO IMPLEMENT each interface. In the case of code that comes from VB6. VB6 just uses typedefs, so you really don’t know which methods belong to each version. So in the end all versions have all methods.

The other alternative to this method, is just to implement last version. And after generating the tlb, decompile it to an .IDL file add the typedefs and recompiled it. I explained something similar in this post:http://blogs.artinsoft.net/mrojas/archive/2010/05/17/interop-remove-prefix-from-c-enums-for-com.aspx

Ok. I hope this helps you to have an more clear idea of what Binary Compatibility is and how to do it in .NET. I am attaching some sample code. It show an ActiveX library that uses BinaryCompatibility and three version on an aplications that uses the different versions. And also a .NET class library that is equivalent to the VB6 one. HERE

Enjoy.

Exposing C# Classes thru Interop

23. June 2010 05:38 by Mrojas in General  //  Tags: , , , , , , , , ,   //   Comments (0)

Either if you migrate your application from VB6 to C# or if you develop a new application in C# something you end up with cases where you need to use your classes in legacy apps. Some of them could have been written in VB6 or could even be VBA macros in Excel applications.

Exposing your .NET classes can be sometimes very easy (you can think is just a matter of putting a ComVisible tag) but in other occasions is not that simple. Specially if your legacy application is using a lot of Late Bound calls like in VBA, so you must make sure that the COM information that you are exposing for your class is exactly what you really want and need.

OK. So I will provide some guidelines or some steps you should follow to provide a consistent COM interface for your .NET Code.

1. First you have to add the [ComVisible(true)]  attribute. Don’t think that’s all. Even if in some cases that is enough is better if you take an strict control of want is being generated for your class. Ok Let’s use the following class as an example:

using System;
using System.Runtime.InteropServices;

namespace InteropExamples
{
    [ComVisible(true)]
    public class MyVerySimpleClass 
    {
        public Class2 CreateANewClass()
        {  return new Class2()     }

        public int GetMyLuckyNumber() { return 15; }
    }
public class Class2 { 
  }
}

// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: <could not determine filename>
[
  uuid(370E4AD4-073B-4984-8C7D-5ED027F7B1CA),
  version(1.0)
]
library ClassLibrary1
{
    // TLib :     // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
    importlib("mscorlib.tlb");
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface _MyVerySimpleClass;

    [
      uuid(E03CCE68-2D55-3576-9DB6-019AAA667A5D),
      version(1.0),
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "InteropExamples.MyVerySimpleClass")
    ]
    coclass MyVerySimpleClass {
        [default] interface _MyVerySimpleClass;
        interface _Object;
    };

    [
      odl,
      uuid(D18BEEE1-4425-3AC7-891E-807EC2283731),
      hidden,
      dual,
      oleautomation,
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "InteropExamples.MyVerySimpleClass")   

    ]
    interface _MyVerySimpleClass : IDispatch {
    };
};

In this case your class will be expose using all defaults. That is, a progId that will be the <AssemblyName>.ClassName an interface _<ClassName> is generated and the class is exposed only for IDispatch, which would not provide class information if you add the tlb reference to a VB6 or VBA project.

And if you run this code in VB6 you will have a problem like type mismatch when you try to use the method x.CreateAClass because it is returning an object that is not exposed thru COM.

Private Sub Command1_Click()
    Dim x As Object
    Set x = CreateObject("InteropExamples.MyVerySimpleClass")
    MsgBox x.GetMyLuckyNumber
    MsgBox x.CreateAClass
End Sub

So my recommendation is to make explicit what you want to expose. Maybe you only need some of the methods to be exposed. Well that is step two.

2. Define a public, ComVisible(true) interface that will define the methods that you want to be exposed thru COM. Sometimes it is better to implement the interface explicitly. I even recommend using partial classes so you isolate the COM stuff from your normal class. If you class is very simple you can leave all COM stuff there.

    //It is better to have an interface, because
    //you are completely sure what you are exposing or not
    [ComVisible(true)]
    public interface _MyVerySimpleClass
    {
        int GetMyLuckyNumber();
    }

3. (Recommedation) This is not an obligatory step but I recommend using partial classes.

    //Using partial classes allow you to separate all the
    //COM plumbing and leave your .NET implementation simple
    public partial class MyVerySimpleClass 
    {
        public Class2 CreateAClass()
        {
            return new Class2();
        }

        public int GetMyLuckyNumber() { return 15; }
    }

3. Make sure your partial class has the following attributes:

[ComVisible(true)] <—This is obvious because you want to use your class in COM

[ClassInterface(ClassInterfaceType.None)] <—This is because your want to take charge or what will be generated in your Typelib (tlb)

[ComDefaultInterface(typeof(_MyVerySimpleClass))] <—This is to indicate the interface that holds your COM visible methods.

[ProgId("InteropExamples.MyVerySimpleClass")] <—To establish which will be the progId not have a generated one
[Guid("{029D468C-8BE6-498f-8A57-3B4B0306BA41}")] <—this is important specially if you are trying to accomplish binary compatibility

Optionally add this attribute [IDispatchImpl(IDispatchImplType.CompatibleImpl)] this is currently marked as an obsolete attribute but it still works and I have found scenarios, specially in some VBA applications where you need this attribute in order to make some late bound calls.

4. And Explicitly implement the interface methods. This is important because some of the return values or arguments might need convertions. For example what can you do if your method returns a DataSet and your Excel VBA script is expecting something like a Recordset (more on this on other posts).

So now you will have a class like:

    //Using partial classes allow you to separate all the
    //COM plumbing and leave your .NET implementation simple
    public partial class MyVerySimpleClass
    {
        public Class2 CreateAClass()
        {
            return new Class2();
        }

        public int GetMyLuckyNumber() { return 15; }
    }

    //It is better to have an interface, because
    //you are completely sure what you are exposing or not
    [ComVisible(true)]
    public interface _MyVerySimpleClass
    {
        int GetMyLuckyNumber();
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)] //This is to make sure that no automatic generation of COM methods is done
    [ComDefaultInterface(typeof(_MyVerySimpleClass))] //This to explicitly establish which is the default interface
    [ProgId("InteropExamples.MyVerySimpleClass")]
    [Guid("{029D468C-8BE6-498f-8A57-3B4B0306BA41}")]
    [IDispatchImpl(IDispatchImplType.CompatibleImpl)]
    partial class MyVerySimpleClass : _MyVerySimpleClass
    {
    
        #region _MyVerySimpleClass Members
        //Explicit implementation is better because it avoids messing your .NET
        //class specification. Sometimes when you expose thru COM you can have problem with
        //methods overloads. For example you have to have the same method name but differente 
        //return type. Or you have a collition with an existing member.
        int _MyVerySimpleClass.GetMyLuckyNumber()
        {
            return GetMyLuckyNumber();
        }

        #endregion
    }

And your TLB is now explicit and exposes ONLY what you really really want.

// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: <could not determine filename>

[
  uuid(370E4AD4-073B-4984-8C7D-5ED027F7B1CA),
  version(1.0)
]

library ClassLibrary1
{
   // TLib :     // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
    importlib("mscorlib.tlb");
   // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface _MyVerySimpleClass;

    [
      odl,
      uuid(80D00C45-EE10-3D65-A5FF-42AB7D8F8A71),
      version(1.0),
      dual,
      oleautomation,
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "InteropExamples._MyVerySimpleClass")   

    ]
    interface _MyVerySimpleClass : IDispatch {
        [id(0x60020000)]
        HRESULT GetMyLuckyNumber([out, retval] long* pRetVal);
    };

    [
      uuid(029D468C-8BE6-498F-8A57-3B4B0306BA41),
      version(1.0),
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "InteropExamples.MyVerySimpleClass")
    ]

    coclass MyVerySimpleClass {
        interface _Object;
        [default] interface _MyVerySimpleClass;
    };
};

For more info about BinaryCompatibility see my other posts on Interop.

IE Explorer and Favorites folder or Special Folders in general

29. September 2009 04:05 by Mrojas in General  //  Tags: , , , , , ,   //   Comments (0)

I found this email in my inbox today:

Hi Mauricio,I came across a reference to your blog at :http://stackoverflow.com/questions/1286746/c-open-link-in-new-tab-webbrowser-control

I have been studying your writings on extending the WebBrowser control, and verified that the extended web code you wrote for C# compiles and works fine in VS 2010 beta, against FrameWork 4.0.

Many thanks for the valuble code and writing !

I am "stuck" on how to read the contents of an IE browser page when the page is displaying a local file, like the contents of the Favorites folder.

All my attempts to get at the Document or DomDocument by casting it to the usual mshtml.dll interfaces fail.

I am NOT asking you to answer my question, or respond, but if you ever get interested in blogging about this aspect of use of IE, I think many people would be interested.

I have done a lot of research on the net, and posted my own question on StackOverFlow : so far not one real pointer, and, possibly, this is not "doable" (?) : maybe what you are seeing when IE shows a file contents is a kind of "virtual explorer" view that is not parseable.

best, Bill xxxxxx”

And I decided to take at look at it to see if I could be of any help and I found out that it is easy and doable.

So I find an useful link by Andreas M. if you want to look at it.

In general My Favorites, Desktop, etc are special folder. So they need a trick to be able to access them.

 

image

Take the code from my ExtendedWebBrowser sample published in http://blogs.artinsoft.net/mrojas/archive/2009/05/01/opening-popup-in-a-newwindow.aspx

and http://blogs.artinsoft.net/mrojas/archive/2009/08/07/newwindow3.aspx and

1. Add a reference to %windir%\system32\shell32.dll

2. Add a new property to the ExtendedWebBrowser like:

    /// <summary>
    /// Returns the shell folderview object displayed in the webbrowser control.
    /// </summary>
    public Shell32.IShellFolderViewDual2 FolderView
    {
        get
        {
            return ((SHDocVw.WebBrowser)base.ActiveXInstance).Document
                     as Shell32.IShellFolderViewDual2;
        }
    }

And now you can access the special folder from your code. As Bill mentioned, that “page” or “special page” is not real HTML and not parseable but you can examine its contents for example you can do something like:

        /// <summary>
        /// Button 1_ click
        /// </summary>
        private void button1_Click(object sender, EventArgs e)
        {
            Shell32.IShellFolderViewDual2 specialFolder = this.extendedWebBrowser1.FolderView;
            string folderName = specialFolder.Folder.Title;
            string parentFolder = specialFolder.Folder.ParentFolder.Title;
            foreach (Shell32.ShellFolderItem f in specialFolder.Folder.Items())
            {
                if (f.IsFolder)
                    System.Diagnostics.Debug.WriteLine("Folder:" + f.Name);
                else
                    System.Diagnostics.Debug.WriteLine("File:" + f.Name);
            } // foreach
        } // button1_Click(sender, e)

Continuous Migration VBUC-MS Team System 2005:Upload an initial version of your VB6 code

22. September 2009 09:34 by Mrojas in VB6 Migration  //  Tags: , , , , ,   //   Comments (0)

Now all we need to do is setup the VB6 code that we will use for migration. To do that follow these steps:

1. On the Source Code Explorer toolback click on the Add Files button:

image

 

 

2. Click the Add Folder button and select the folder with your VB6 files

image

3. After you select the folder, a list of files found will be shown. Just remove any unneccesary files. For example files like MSSCCPRJ.SCC should be removed. And press OK

image

Now you have to commit your code the Source Code Repository

4. On the Source code Explorer Right click on Source Control Folder (for this example is MergeExample) and select Check In Pending Changes..

image

5. Write an apropiate comment and press Check In

Continuous Migration VBUC-MS Team System 2005: Configure MS Visual Studio 2005 to use Team System

22. September 2009 08:57 by Mrojas in General  //  Tags: , , , , , , ,   //   Comments (0)

This post will show you the necessary steps to configure Microsoft Visual Studio 2005 to use a Team System Project. In this example we will use a Team Project created to illustrate Continuous Migration with VBUC and MS Team System 2005, called MergeExample.

1. Start Microsoft Visual Studio 2005

2.  Go to the File Menu\Open\Team Project…

image

3. Dialog is presented, where you can choose your Team Foundation Server, and the Team Project to use. For these Continuous Migration example we’ll assume a project called MergeExample.

image

4. The Team Explorer is shown, with your selected project.
Expand the elements for the Team Project you previously selected.

image

5.  We now have to map our local directories to bind them to the
source control system. On the expanded tree, double click on the
Source Control Node

image

6. The Source Code Explorer is shown:

image

7.  Go to the Workspaces dropdown box and select Workspaces…

image

8.  The Manage workspace dialog is shown.
Select the appropiate workspace and click Edit…

image 

9. On the Edit Workspace dialog type a description for your source control and select the source control Folder we had created (for this example it is $/MergeExample) and the directory for your code (for this example it is C:\MergeExample).

image 

 

 

 

 

 

NOTE: if a message box about a pending update is shown, just click ok.

 

Now you have successfully mapped your directories, and you can start setting up your VB6 code for continuous migration

CapsLock, NumLock in C# and VB.NET

10. July 2009 12:49 by Mrojas in General  //  Tags: , , , , ,   //   Comments (0)

I was looking for a “.net” way of detecting the CapsLock state, but almost all the references pointed to pinvoke code like:

<DllImport("user32.dll")> _
Public Shared Function GetKeyState(VirtKey As Integer) As Integer
End Sub

And I finally found two ways:

1) You can call methods from the System.Console class:

You can use the System.Console.CapsLock property and if you want the NumLock state use: System.Console.NumberLock

or

2) You can call make an instance of Microsoft.VisualBasic.Devices.Keyboard. (For this if you are in C# you need to add a reference to Microsoft.VisualBasic.dll)

For example:

Microsoft.VisualBasic.Devices.Keyboard key = new Microsoft.VisualBasic.Devices.Keyboard();

and use properties like:

key.CapsLock

key.NumLock

key.ScrollLock

key.ShiftKeyDown

key.CtrlKeyDown

key.AltKeyDown

DDE in .NET

10. June 2009 04:51 by Mrojas in General  //  Tags: , , , , , ,   //   Comments (0)

Someone recently made me remind an old technology called DDE.

“Dynamic Data Exchange (DDE) is a technology for communication between multiple applications under Microsoft Windows or OS/2

 

“The primary function of DDE is to allow Windows applications to share data. For example, a cell in Microsoft Excel could be linked to a value in another application and when the value changed, it would be automatically updated in the Excel spreadsheet. The data communication was established by a simple, three-segment model. Each program was known to DDE by its "application" name. Each application could further organize information by groups known as "topic" and each topic could serve up individual pieces of data as an "item". For example, if a user wanted to pull a value from Microsoft Excel which was contained in a spreadsheet called "Sheet1" in the cell in the first row and first column, the application would be "Excel", the topic "Sheet1" and the item "r1c1".

Note: In DDE, the application, topic and item are not case-sensitive.”

 

So in VB6 you can have something like:

 

Private Sub Form_Load()
Text1.LinkMode = 0
Text1.LinkTopic = "Excel|Sheet1"
Text1.LinkItem = "R1C1"
Text1.LinkMode = 1
End Sub

 

 

How can you do that in .NET. Is it possible in C#? Well I started looking around and found several forums explaining about all the API calls and I was just about to write my own solution when I found NDDE. This project hosted in CodePlex “provides a convenient and easy way to integrate .NET applications with legacy applications that use Dynamic Data Exchange (DDE)” :)

 

So this is a  nice example of how to do the previous lines in C#:

        //This class provides the infraestructure for DDE comunication
        NDde.Client.DdeClient ddeClient_TextBox1 = null;

        private void Form1_Load(object sender, EventArgs e)
        {
            //I initialize the DDEClient object. Application is Excel and Topic is Sheet1. I'm using the 
            //the TextBox as the syncronization object
            ddeClient_TextBox1 = new NDde.Client.DdeClient("Excel", "Sheet1", textBox1);
            //Connect to the DDE Server
            ddeClient_TextBox1.Connect();
            //Start the Advise Loop
            ddeClient_TextBox1.StartAdvise("R1C1", 1, true, 60000);
            //Setup the Advise Method
            ddeClient_TextBox1.Advise += new EventHandler<NDde.Client.DdeAdviseEventArgs>(ddeClient_TextBox1_Advise);
            //Setup a method to Poke the Server for TextBox cahnges
            textBox1.TextChanged += new EventHandler(textBox1_TextChanged);

        }
        
        void textBox1_TextChanged(object sender, EventArgs e)
        {
            //Syncronous Poking the server
            ddeClient_TextBox1.Poke("R1C1", textBox1.Text + "\0", 4000);
        }

        const string DDE_postFix = "\r\n\0";
        void ddeClient_TextBox1_Advise(object sender, NDde.Client.DdeAdviseEventArgs e)
        {
            //Advise only if needed
            if (e.Text.Length >=DDE_postFix.Length && textBox1.Text + DDE_postFix != e.Text)
                textBox1.Text = e.Text.Substring(0,e.Text.Length-3);
        }
NOTE: Remember that you need to download NDDE and add a reference to this library

This is very good library, you can also set up a lot of Async calls to even improve performance. I have even thought of making an extender as the ToolTip control to add LinkTopic, LinkMode and LinkItem properties for Winforms controls or provide extensions methods to make all the syntax easier, but that is for a future post. Good Luck.

VB6 Migration of Property Pages

9. June 2009 08:36 by Mrojas in General  //  Tags: , , , , , , ,   //   Comments (0)

How can I migrate property pages? Well that is a common question when migrating VB6 Activex controls.

Property Pages where commonly used in VB6 to provide a mechanism for your user controls to edit values.

.NET provides even more mechanisms for editing your control properties.  You can provide an editor for each one of your component properties or you can provide a ComponentEditor for all the component, this is very similar to the VB6 concept.

In .NET the ComponentEditor can be actived in the designer selecting Properties from the context menu when you right click over the control.

This is from the MSDN documentation:

“A component editor is used to edit a component as a whole and can be used to implement a
user interface similar to that of the property pages. You associate a component editor with a
component by using the
EditorAttribute attribute.” From: ComponentEditor Class

The VBUC does not process out of the box, your PropertyPages, but I developed a tool that can be
used so the VBUC can help you migrate those property pages. This tool will modify your VB6 project,
and VB6 PropertyPages source code to make those VB6 PropertyPages look like VB6 UserControls.
This will allow  the VBUC migration tool to recover some of the VB6 PropertyPages code and appearance
and with some manual changes you can get your property pages to work again.

Use the following link to downlaod the tool: DOWNLOAD TOOL

So these are the steps to migrate a VB6 Project that has Property Pages with the VB6.

1) Make a backup copy of your source code.

2) Run the TOOL with your project file. For example if your project file is Project1.vbp then run the tool like this:

FixPropertyPages Project1.vbp

This will generate a new VB6 Project file called ModifiedProject1.vbp

3) Open the VBUC, and migrate the new project file ModifiedProject1.vbp

4) Open the migrated solution in Visual Studio.

5) All your property pages will be migrated to .NET UserControls. You might need to go thru some changes to make them completely functional. Remeber to add the [ToolboxItem(false)] to these property pages because they do not need to be visible in your toolbox.

6) Now, to associate those property pages with your UserControl do this:

6.1) Add a new code file to your migrated solution. We are going to create a ComponentEditor, that will hold all the pages and associate that to the migrated control. Lets say the control is named Control1 and the property pages are PropertyPage1 and PropertyPage2.
We will call the ComponentEditor ComponentEditorToAssociatePagesForMyControl.
In this ComponentEditor we will add an internal class for each PropertyPage. This class will inherit from ComponentEditorPage. We will call this internal classes Page1, and Page2. And we will associate those classes with the ComponentEditorToAssociatePagesForMyControl in the GetComponentEditorPages().
 

The resulting code will be like:

C#
using System.Windows.Forms.Design;
using WindowsFormsApplication1;
using System.Drawing;
using System.ComponentModel;
[ToolboxItem(false)] 
public class ComponentEditorToAssociatePagesForMyControl : WindowsFormsComponentEditor
{
    // Methods
    public override bool EditComponent(ITypeDescriptorContext context, object component)
    {
        return false;
    }

    class Page1 : ComponentEditorPage
    {
        // Methods
        public Page1()
        {
            PropertyPage1ForControl1 page1 = new PropertyPage1ForControl1();
            Size mysize = new Size(400, 250);
            this.Size = mysize;
            this.Text = "Page 1 for Control1";
            this.Controls.Add(page1);
        }
        protected override void LoadComponent() { }
        protected override void SaveComponent() { }
    }

    class Page2 : ComponentEditorPage
    {
        // Methods
        public Page2()
        {
            PropertyPage2ForControl1 page2 = new PropertyPage2ForControl1();
            Size mysize = new Size(400, 250);
            this.Size = mysize;
            this.Text = "Page 2 for Control1";
            this.Controls.Add(page2);
        }
        protected override void LoadComponent() { }
        protected override void SaveComponent() { }
    }

    protected override System.Type[] GetComponentEditorPages()
    {
        return new System.Type[] { typeof(Page1),typeof(Page2) };
    }

    protected override int GetInitialComponentEditorPageIndex()
    {
        return 0;
    }
}

VB.NET

<ToolboxItem(False)> _
Public Class ComponentEditorToAssociatePagesForMyControl
    Inherits WindowsFormsComponentEditor
    ' Methods
    Public Overrides Function EditComponent(ByVal context As ITypeDescriptorContext, ByVal component As Object) As Boolean
        Return False
    End Function

    Protected Overrides Function GetComponentEditorPages() As Type()
        Return New Type() { GetType(Page1), GetType(Page2) }
    End Function

    Protected Overrides Function GetInitialComponentEditorPageIndex() As Integer
        Return 0
    End Function


    ' Nested Types
    Private Class Page1
        Inherits ComponentEditorPage
        ' Methods
        Public Sub New()
            Dim page1 As New PropertyPage1ForControl1
            Dim mysize As New Size(400, 250)
            MyBase.Size = mysize
            Me.Text = "Page 1 for Control1"
            MyBase.Controls.Add(page1)
        End Sub

        Protected Overrides Sub LoadComponent()
        End Sub

        Protected Overrides Sub SaveComponent()
        End Sub

    End Class

    Private Class Page2
        Inherits ComponentEditorPage
        ' Methods
        Public Sub New()
            Dim page2 As New PropertyPage2ForControl1
            Dim mysize As New Size(400, 250)
            MyBase.Size = mysize
            Me.Text = "Page 2 for Control1"
            MyBase.Controls.Add(page2)
        End Sub

        Protected Overrides Sub LoadComponent()
        End Sub

        Protected Overrides Sub SaveComponent()
        End Sub

    End Class
End Class

 

7) After creating the ComponentEditor you must associate the component Editor to your new component editors. This can be done with something like:

C# 

[Editor(typeof(ComponentEditorToAssociatePagesForMyControl), typeof(ComponentEditor))]
public class Control1 : UserControl

VB.NET

<Editor(GetType(ComponentEditorToAssociatePagesForMyControl), GetType(ComponentEditor))> _
Public Class Control1
8)  Now to use this property pages, go to the designer screen and open the context menu and select properties. And editor with your properties pages will appear :)

9) You still need to write some code for saving the property values that is something you have to add to the LoadComponent and SaveComponent methods of the internal classes in your ComponentEditor (ComponentEditorToAssociatePagesForMyControl in our previous example).

I hope this helps to get your code faster in .NET. I'm attaching a C# sample if you want to try it out.