Jaime Rodriguez
On Windows Store apps, Windows Phone, HTML and XAML

  • Jaime Rodriguez

    writing a .net activex control for your sidebar gadget..

    • 3 Comments

    HTML is quite easy for people to get started with, pretty broadly known, light weight, rich enough (if you work hard at it or have nice graphics), etc.. so there is a good set of reasons why the sidebar folks chose HTML as the presentation technology  [ I am guessign here]

    However, there are some things that are a little harder to do with HTML [at least they are for me] ... for example, charting (pie charts) ...I tried using VML for this, and had issues.. (it was a little unstable on how it rendered, some times it would stop rendering, I could not figure why)...  so for these very extreme scenarios, I am thinking ActiveX might be OK...  

    Note that by no means I am advocating activex as the best choice for gadgets, imho the activex registration (regsvr32) breaks the easy, per-user, non-impactful deployment model  for gadgets...  but I am thinking that there are enterprises (say internal, lob gadgets) that can get away with an impactful deployment model (via MSI or SMS) and benefit from the rich visualization of an activex -say for some BI  KPIs... 

    Here is what I created code to do:

    1. An Interface so I can 'raise' some events from my activex to my gadget  (outward )
    2. An interface so I can call from my gadget into the Active X...  (inwar)
    3. An ActiveX user control, written in C# that shows UI 
    4. some lame code to draw a piechart... just to make me feel good ....  it does not update, etc.. very much a token ..  I will skip on steps below
    5. Wrote the code to call the activex from my gadget and sink to the events..

    For reference, here are some good articles in creating .NET Activex controls for IE...   I used a little bit from all of these..

    http://msdn.microsoft.com/msdnmag/issues/02/01/UserCtrl/ 

    http://www.codeproject.com/cs/miscctrl/exposingdotnetcontrols.asp  

    http://msdn.microsoft.com/library/default.asp?url=/workshop/author/om/event_model.asp

    http://www.cisco-cert.com/dotnet_general/574541-How-to-fire-Javascript-events-from-a--NET-winforms-user-control-back-to-Javascript-in-IE

     

    Come on dude, get to it... show us the code!.. 
    OK .. here you go ...   All of the code is commented, please go through it in the project... here I am showing only the 4 tasks above.. |f you are to reuse this, you will have to replace all the lines that have a TODO with your own guids or your own names code

    // Outgoing interface, wired via ComSourceInterfaces on my main class  -- see step 3
    //TODO: Replace the Guid with our own
    [Guid("56726927-833B-4cfb-94D3-D1501DFD30D2")]
    [
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IGadgetControlEvents
    {//TODO: The DispIds must be unique and must be here... I first skipped them and was getting a MethodNotImplementedException [DispId(1)]
    void RightClickRelay(int x, int y);
    [
    DispId(2)]
    void MouseEnterRelay();
    [
    DispId(3)]
    void MouseLeaveRelay();
    }

    In my class, I had to expose the events ( showing only RightClick here...  the rest you can see in the code)

    // Right Mouse Click will be passed to the gadget... I was hoping I could popup the context menu..
    protected override void OnMouseClick(MouseEventArgs e)
    {
    base.OnMouseClick(e);
    if (e.Button == MouseButtons.Right)
    {
    // Did any one subscribe ???
    if (RightClickRelay != null)
    {
    new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
    // fire the event ..
    RightClickRelay(e.X, e.Y);
    CodeAccessPermission.RevertAssert();

    }
    }

     2 -- Exposing an interface for gadget to call my Activex ..

    // incoming interface -- so I can call from HTML Gadget ....
    // wired simply by my control class implementing it..
    // note: I had to implement extra interface because I had the eventing interface... if I did not have eventing, I would not have
    // needed to explicitly implement this extra interface..

    //TODO: Replace guid...
    [Guid("341F46AA-0EA0-4b97-A294-E131D8CFF7E6")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IGadgetIncoming
    {
    void SetVariableName(string name, string value);
    }

    to wire the interface, simply implement it in my class ...  (see step3 )

    3.-  Creating the activex .. it is simply a .NET Class Library with a UserControl and some attributes:

    // TODO: Replace GUID
    [Guid("A4B9C3C2-8DAC-451a-AEC1-DE3B042FB311")]
    // TODO: Replace Prog Id
    [ProgId("WindowsFormsUserControl.UserControl")]
    //Wire up my eventing interface ..(ComSource)...
    [ClassInterface(ClassInterfaceType.None), ComSourceInterfaces(typeof(IGadgetControlEvents))]
    [
    ComVisible(true)]
    public partial class UserControl1 : UserControl, IGadgetIncoming

     

    5.-  Using the object from my gadget

    <!-- using the object tag to call my ActiveX -->

    <object id="sample"
    classid="clsid:A4B9C3C2-8DAC-451a-AEC1-DE3B042FB311"
    height="126" width="126" style="background-color:Transparent;" onclick="alert('clicked');" >
    </object>
    <
    script>

    <!--  one of several ways to sync event; another way is using the FOR statement -->  
    function
    sample::RightClickRelay ( x, y )
    {
    // ShowContextMenu ();
    var o = document.getElementById('sample');
    o.SetVariableName (
    'test1' , 'value1') ; }
    </script>

    --- 

    So, the control is pretty trivial... download the sample code, replace the TODOs ( use GuidGen to generate your guids) and you will reuse it ...  There were however a couple of note worthy issues I have not figured out .. [will get to these as soon as I have time ]

    • I implemetned the right click so that I could fire the context menu of the gadget .. but the event is raised and I handled it in Javascript, but still could not fire the context menu ..  
    • I implemented MouseEnter/Mouse Leave because gadgets show the little drag & drop handle when MouseEnter or it gets focus...   One way to get around it, was that I put a Table around it... this way, the table detects when MousEnter/leave happens and lets the sidebar know ...  This works for the most part, but if you run you mouse very fast into the gadget, some times table does not fire it ..
    • When you put the activex, you lose the easy drag & drop functionality gadget has.. you an only drag & drop from outside the activex..

    None of these are blockers, in fact some of the MS gadgets themselves (notes) have these issues but I would like to solve them for a different post ...  

    You can download the code for this project here...  
    There are two directories in the download, the sample.gadget and the ActiveX code itself..

    For dev/test deployment, the project in Visual Studio is using the "Register for COM Interop" checkbox in the Build section of the Project properties to do the registration ... if you are not rebuilding it, or installing it in a box where you are not building it,  you will have to register it manually from a .NET Command prompt  (or a command prompt with regasm in the path)..  

    regasm /register /codebase WindowsFormsuserControl.dll 

     

    This leaves the whole topic of how to deploy at run-time...  are you going to need admin, etc..  I will have to get back to these questions in a future (but will try to not make it to far into the future) post.. .

     

     

    Cheers,

  • Jaime Rodriguez

    Using Atlas to make webservices calls from sidebar gadget..

    • 2 Comments

    Last post we did a REST call using XmlHttp from a sidebar gadget... but let's admit it, we were all doing xmlhttp calls in 2000 and we were 'geeks'... it was not until 2005? that this exact same code became "cool" because it was called AJAX ...  So here is the ticket to get you to the cool group -- every one knows I will never make that :(

    Pre-requisites (or stuff you get with the sample code located here)   

    1. Create an Atlas website ( might want to check http://atlas.asp.net, if you don't know Atlas, you will need an hour there)
    2. Create a Web Service...    mine has the very creative name of "WebService.asmx"  and it has a function called GetVolume that returns an object .... [arguably a 'complex type']

    The client code: 

    1. Copy the atlasruntime.js  from a machine that has Atlas installed to your gadget's js directory (or any where that you can get to from your gadget).. 
       By default, my atlas install put the files under \Program files\Microsoft asp.net\atlas\v2.0.50727\atlas\scriptlibrary\debug
       
    2. The trick: Atlas by default uses frames to get around cross-site scripting...  That gets messy if you are trying to generate a proxy with out using the Atlas' server side script manager..   so instead I forced Atlas to use xmlhttp with out frames.. and when it goes directly it is a breeze..  The caveat is that the parameter to forceXmlHttp is hidden some where that I could not [though did not look too hard] get to easily...  if you download the sample code for this project, search atlasruntime.js for 'jaimer' to see the change..  for those going at it alone,  it is line 2317 ... in atlasruntime.js  ( Sept CTP ) ...   the code around it looks like this:


      Sys.Net.WebRequest =
      function() {
      Sys.Net.WebRequest.initializeBase(
      this, [true]);
      var _url = null;
      ...... //lots of stuff here
      var _delegateRequest = null;
       //jaimer Changed this line... from
      // var _forceXmlHttp = false; /* to */
      var _forceXmlHttp= true;

    3. Create a proxy to your web service from your gadget code..
      [So I added a new Javascript file, called AtlasServiceProxy.js and I put the same stuff that atlas gets by making the call to WebService.asmx/js [this is what they use to get the proxy] ...  If you have an aspx website, with a webservice, you can just navigate to it using IE, and add the /js after the webservice URL ... (e.g. http://jaimersvr/AtlasWs/Webservice.asmx/js ]  and it should look like the one below..  Notice I did have to add one line, the this.path = <url to my webservice> .. 

      Type.registerNamespace('AtlasSample');
      AtlasSample.WebService=
      new function()
      {
      //  These parameters can of course be set at runtime... In my case,  I hardcoded them here for brevity
      this.path = "http://jaimersvr/AtlasWS/WebService.asmx" ;
      this.appPath = "http://jaimersvr/AtlasWS/" ;
      var cm=Sys.Net.ServiceMethod.createProxyMethod; cm(this,"EchoString","s"); } 

    4. OK,  I think that is all we needed...  we are now ready to party w/ ajax.. :)  To run the sample, simply click in the button in the gadget...

    WHY BOTHER using Atlas to call webservices instead of just plain old XmlHttp like we did on my last post??...
    Well, there is one nice reason to do this: Atlas does some nice JSON serialization on the back-end and front-end, so  the results come back as objects instead of as an XML file you need to parse...   for example, my webservice returns an object...  that looks like this in C#:

    public class VolumeObject {
    public string At10m;
    public string At50m;
    public string At100m ;
    }
    using Atlas, I do not need to write the XML de-serialization code ...

    /*digress: why this weird object I started with a simple Echo function ... that returned what you sent.. but then I decided to do a more complex object .. so I thought an echo would do... here you get the echo at 10m ( LOUD ) at 50m ( LOud) and at 100m when it fades (loud) ....  you can stop gagging now :) */

    The code for this sample is located here,  there is a client side component ( sample.gadget)  and a server side component  ( AtlasWS ) ... You will as usual have to change the URLs in AtlasServiceProxy.js  from pointing to my servers to pointing to yours..

    Cheers. ..

Page 1 of 1 (2 items)