Tag Archives: Silverlight

Fast Silverlight 4 Copy and Paste Context Menu [UPDATED]

My team recently deployed an early Beta release of a project for limited review. Of all the quirks and flaws, one stood out the most: users couldn’t right-click to copy and paste. A quick look for previous work on this topic turned up some stuff but what I found was based entirely on adding the functionality to one control at a time. This would take too long for us and since we use a variety of toolkit and 3rd party controls I decided to see if I could come up with something more generic.

For my first attempt I put the code directly in MainPage.xaml.cs which is the root control for our application. This first version worked with any control that exposed a SelectedText property but didn’t work on child windows. Still, the code was mostly useful so I pulled it out of MainPage and put it in its own control. For simplicity, I just extended the ContextMenu control available in the Silverlight 4 toolkit.

In Visual Studio I added a new Silverlight Control called PopupMenu.xaml. I then edited the XAML file to switch from UserControl to toolkit:ContextMenu and likewise in the class declaration of the associated CS file. I also ripped out the boilerplate XAML.

Then I created a method based on the original proof of concept code to handle MouseRightButtonDown and -Up methods. Using reflection I examined the controls in the visual tree under the mouse click to see if any of them offer a SelectedText property. If so, I check to see if it’s writable and if the control exposes a IsReadOnly property and if it’s false. If these conditions are met, the Copy, Cut and Paste menu options are presented to the user. (Alternatively just Copy if the SelectedText property is there but is not writeable or IsReadOnly is true.)

On loading of the MainPage control, I attach event listeners to the MouseRightButtonDown and -Up events passing some information onto an instance of the PopupMenu context menu control.

public partial class MainPage : UserControl
{
   private PopupMenu popupMenu = new PopupMenu();
   ...
   ...
   public MainPage()
   {
      InitializeComponent();

      this.MouseRightButtonDown +=
         new MouseButtonEventHandler(MainPage_MouseRightButtonDown);
      this.MouseRightButtonUp +=
         new MouseButtonEventHandler(MainPage_MouseRightButtonUp);
      ...
      ...
   }

   void MainPage_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
   {
      //Prevent generic Silverlight menu from popping.
      e.Handled = true;
   }

   void MainPage_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
   {
       popupMenu.HandleClick(e, this, false);
   }

This worked great for all our views based on Page but it still didn’t work for child windows. I decided that rather than go through all our child window controls and do the same as MainPage (wire up listeners and pass the calls on to PopupMenu) I would extend ChildWindow. I created a new control just like the PopupMenu. This time I changed UserControl to ChildWindow in the xaml and associated cs. In this class, ExtendedChildWindow, I attach the event listeners to the same mouse events and route the call just like MainPage.

public partial class ExtendedChildWindow : ChildWindow
{
   public ExtendedChildWindow()
   {
       InitializeComponent();

       if (!System.ComponentModel.DesignerProperties.IsInDesignTool)
       {
           this.MouseRightButtonDown +=
              new MouseButtonEventHandler(ExtendedChildWindow_MouseRightButtonDown);
           this.MouseRightButtonUp +=
              new MouseButtonEventHandler(ExtendedChildWindow_MouseRightButtonUp);
       }
   }

   void ExtendedChildWindow_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
   {
       new PopupMenu().HandleClick(e, this, true);
   }

   void ExtendedChildWindow_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
   {
       //Prevent generic Silverlight menu from popping.
       e.Handled = true;
   }

   ~ExtendedChildWindow()
   {
      this.MouseRightButtonDown -= ExtendedChildWindow_MouseRightButtonDown;
      this.MouseRightButtonUp -= ExtendedChildWindow_MouseRightButtonUp;
   }
}

Notice this time instead of newing up one PopupMenu control and using it over and over, I’m newing it each time. This is because for some reason I haven’t spent any time figuring out, if I don’t do it this way the offset goes crazy if I copy from a Page view before going into a child window and the menu is rendered in the lower-right corner of the Silverlight application. Doing it this way just works. Maybe in a future version I’ll try switching to a static method call so it doesn’t look as shady.

After that I went through all our controls based on ChildWindow (conveniently in their own solution folder) and changed them to inherit from my:ExtendedChildWindow. A few VS2010 crashes later I had all the source files updated. I ran the project and indeed the popup menu showed in views and child windows. Except in the child window controls the popup was shown in the upper left corner of the Silverlight application but it showed correctly for page views.

To fix this I added a boolean to the PopupMenu control. When true I would adjust the horizontal and vertical offset values based on the mouse position. With that in place I now had my final render tweaks working. I don’t know if this was the best way to go about it but this is how I took a proof of concept segment of code to working status integrated with the main source branch in less than 2 days.

Here are the important bits from the PopupMenu control code.

   ...
   ...
   [Flags]
   public enum TextOperations : ushort
   {
       Copy = 1,
       Paste = 2,
       Cut = Copy | Paste
   }

   public static TextOperations GetAllowedOperations(UIElement element)
   {
       TextOperations allowedOperations = new TextOperations();

       if (null != element.GetType().GetProperty("SelectedText"))
       {
           allowedOperations |= TextOperations.Copy;
       }
       if (null != element.GetType().GetProperty("SelectedText") &&
           element.GetType().GetProperty("SelectedText").CanWrite &&
           (null == element.GetType().GetProperty("IsReadOnly") ||
              (null != element.GetType().GetProperty("IsReadOnly") &&
               !bool.Parse(element.GetType().GetProperty("IsReadOnly")
                  .GetValue(element, null).ToString()))
               )
            )
       {
           allowedOperations |= TextOperations.Paste;                
       }

       return allowedOperations;
   }

   public void HandleClick(MouseButtonEventArgs e,
                                  UIElement displayElement,
                                  bool RenderContentOffset)
   {
       Point mousePosition = e.GetSafePosition(null);
       var elements = VisualTreeHelper
          .FindElementsInHostCoordinates(mousePosition, displayElement);

       TextOperations allowedOperations;
       foreach (var element in elements)
       {
           allowedOperations = GetAllowedOperations(element);

           if (allowedOperations > 0)
           {
               MenuItem option;
               Items.Clear();

               //Add menu items to this.Items
               ...

               //Now, if we found items, let's show and break out
               if (Items.Count > 0)
               {
                   ActiveElement = element;
                   if (RenderContentOffset)
                   {
                       HorizontalOffset = mousePosition.X;
                       VerticalOffset = mousePosition.Y;
                   }
                   IsOpen = true;
                   break;
               }
           }
       }
   }

   void SetSelectedText(UIElement element, string value)
   {
       element.GetType().GetProperty("SelectedText")
          .SetValue(element, value, null);
   }

   string GetSelectedText(UIElement element)
   {
       return element.GetType().GetProperty("SelectedText")
          .GetValue(element, null).ToString();
   }

   void ContextMenuItem_Click(object sender, RoutedEventArgs e)
   {
       if (null != ActiveElement)
       {
           MenuItem item = sender as MenuItem;

           switch (item.Header.ToString())
           {
               case "Cut":
               case "Copy":
                   Clipboard.SetText(GetSelectedText(ActiveElement));

                   if (item.Header.ToString() == "Cut")
                   {
                       SetSelectedText(ActiveElement, string.Empty);
                   }

                   break;
               case "Paste":
                   SetSelectedText(ActiveElement, Clipboard.GetText());
                   break;
           }
       }

       IsOpen = false;
       ActiveElement = null;
   }

Shiny?
-Erik

[UPDATE: 19 Oct 2010 @ 1:20PM]
One of the members on the team started getting cross-thread access errors that he was able to trace to the ExtendedChildWindow for removing the mouse event listeners. In an attempt to correct this, I’ve added a new event handler in the constructor to listen to the Unloaded event and moved the code to disconnect the event listeners into that handler. Hopefully this will resolve the issue.

Advertisements

Silverlight Polling Duplex Slowness / Queuing

In a previous post I mentioned that you should probably just give up the idea of using Polling Duplex Communication endpoints in your Silverlight application if you’re going to host your WCF services from SharePoint 2007.

The main reason I cited was that hosting the service(s) in SharePoint 2007 meant you probably wanted access to the SPContext which meant you must force your WCF services to run in ASP.NET Compatibility mode. This alone doesn’t cause the problem and I did mention the issue stems from contention trying to access session state data. I dumped the whole idea of using Polling Duplex for my needs since access to the session state was necessary. It occurs to me now that not everyone needs it.

Check out this article which provided me a good deal of information when I was debugging my issue. It provides a different approach for resolving this slowness. Simply put your Polling Duplex stuff in its own service with session state disabled. That should keep things quick and happy and prevent your session-needing service calls from getting blocked.

Hope this helps,
-Erik

Pro Tip: Return the same object for WCF service methods

In a previous post I made reference to a consistent return type from my WCF services for Silverlight to consume. Here’s what it is and why I think it’s useful.

WebServiceVoidWrapper
{
    public string ExceptionMessage { get; set; }
    public DateTime LastUpdated { get; set; }
    public bool IsGroupActive { get; set; }
}

WebServiceWrapper<T> : WebServiceVoidWrapper
{
    public T Results { get; set; }
}

Each of our calls, regardless of what they’re returning, need to let the client know about certain conditions. At first we only cared to grab the exception message. Then we added the LastUpdated to be populated when working with data pieces so we could query the server to see if any changes had occurred since. Using the structure above, we could expect all our methods to at least have those members. (The VoidWrapper version is for those times we’re not returning an data from the service but we still want those fields.)

When this was our approach, we had methods scattered in various XAML pages to check for and react to these conditions and this was fine if not anemic since we were short on time and didn’t really implement nice UI interaction to deal with errors.

At some point the business requirements changed and a user could now be part of multiple groups and could be added and removed at any time. We needed a way to react to that. We easily added the field to the base response class and use exception catching on the server side to populate the field. This also meant we were going to need to handle this case all over our Silverlight application because any call could signal the IsGroupActive as false which means the user shouldn’t be able to work with local data they already have.

Rather than peppering checks for this field, popping a child window to prompt the user for a new Organization and then react to the new data we decided to handle this a little more generically. I had constructed a singleton class to act as the interface for the Silverlight application’s communication to the server. Since this singleton is active for the life of the user’s interaction with the application we used this as our starting point.

With this layer in place, we could easily wire up listeners to all our WCF service events and attach a delegate to check for the field and react to it. A quick glance shows 42 methods we’re interested in and these methods grow and shrink and change names constantly during our development cycle. Rather than putting in 42 calls to SomethingCompleted += … I wrote a bit of code to wire up the listener automatically. See previous post for info on that: Dynamically Attach Delegates to WCF Service in Silverlight.

Now with a generic listener in place to catch the user org becoming invalid, we can deal with the UI aspect. Continuing the theme of least-amount-of-code-change we targeted the MainPage object as the best place to handle the interaction with the user. MainPage is available as our Application.Current.RootVisual and can pop child windows and control other UI aspects. It also has access to the NavigationService which we’ll be using as well.

To let the MainPage know the user org has become invalid, I decided to fire a custom event from the service where we detected the error.

[In ConnectionService.cs]
public event EventHandler OnUserOrganizationNotValid;

And subscribe to that event in MainPage.

[In MainPage.xaml.cs]
public MainPage()
{
    InitializeComponent();
    ConnectionService.GetInstance().OnUserOrganizationNotValid +=
        new EventHandler(MainPage_OnUserOrganizationNotValid);
}

void MainPage_OnUserOrganizationNotValid(object sender, EventArgs e)
{
    PopUserOrganizationPicker();
}

From the event delegate we’re calling out to another function. This is because we also have the ability to allow the user to choose a new organization whenever they wish. Rather than have a duplication of code somewhere else, we made a public method that can be accessed from other views.

private OrganizationDialogActive = false;
public void PopUserOrganizationPicker()
{
    if (!OrganizationDialogActive)
    {
        OrganizationDialogActive = true;
        Dispatcher.BeginInvoke(() =>
        {
            OrganizationPicker.InitializeForm();
            OrganizationPicker.Show();
        });
    }
}

void OrganizationDialogClosed(object sender, EventArgs e)
{
    OrganizationDialogActive = false;
    ContentFrame.Refresh();
}

I’m using a property for the org dialog window which wires up the callback event on Closed event. This is nice since the window is late loaded on first use instead of at load time and since nothing really changes, we just keep it in memory.

private SelectOrganizationDialog _OrganizationPickerBackingField;
private SelectOrganizationDialog
{
    get
    {
        if (null == _OrganizationPickerBackingField)
        {
            _OrganizationPickerBackingField =
            new SelectOrganizationDialog(OrganizationDialogClosed);
        }
        return _OrganizationPickerBackingField;
    }
}

To get the user’s screen to update, we could place code to navigate in the MainPage but we’ve stuck it in the org picker child window instead. This is because our service method returns a new URL if the newly chosen org makes the user a power user which will need to send them to a new page. From the service callback in the org picker child window we call a helper method NavigateToPage.

//DefaultPage holds a relative url string for the destination page.
public static void NavigateToPage()
{
    MainPage mp = Application.Current.RootVisual as MainPage;
    Uri defaultUri = new Uri(DefaultPage, UriKind.Relative);

    if (mp.ContentFrame.Source.OriginalString.Equals(DefaultPage))
        mp.ContentFrame.Refresh();
    else
        if (!mp.ContentFrame.Navigate(defaultUri))
            mp.ContentFrame.Refresh();
}

Faleminderit!
-Erik

Creating Silverlight + WCF Apps in SharePoint 2007

I’ve seen people asking how to create WCF services from within SharePoint 2007. It’s actually really easy – much easier than ASMX web services. I could go into a lot of detail but here’s the basic steps:

  1. Create or get a package to modify the SharePoint 2007 web.config to understand WCF URL requests.
    • WCF Service requests start with ‘~’ which SharePoint doesn’t like. You need a custom HTTPModule you can add to SharePoint’s web.config to provide a virtual path provider to strip this character from the request. This is easy to find.
  2. Create a solution that contains your projects.
    • WCF Service Project. You can only target up to .NET 3.5 because SharePoint can’t run in the .NET 4 runtime and if you want your service hosted in SharePoint (to get the nifty SPContext stuff) your service will have to run in the same runtime. See notes below about specific web.config settings.
    • Web Project. This is typically a very simple project and is likely just a simple ASP.NET page with the SharePoint master page instructions and basic layout with the Silverlight app referenced.
    • Silverlight Project. The meat of your application. Create a Web Service reference to your WCF service and watch the magic. Oh, it’s not working? Yeah, there’s a few things you need to do here.

The above is pretty straight forward but won’t work until you sprinkle in some magic.

Decorate your service method with the AspNetCompatibilityRequirementsMode.Required attribute:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]

WCF Services can run in an optimized pipeline to speed them up. Unfortunately this means they will run outside the traditional pipeline for ASP.NET applications. Decorating your service method this way forces your service to run in the ASP.NET pipeline. This is necessary in order for IIS to give the request an HTTPContext and for SharePoint to make an SPContext available.

The downside to this is that you can just forget about using polling duplex communication mode. (Well, you don’t have to forget about it but you’re on your own for solving this problem…) Why? Long story short the session state manager locks access on each request. The polling duplex communication works by initiating long-timeout requests to the server. If the server has nothing to say, the request eventually times out and a new request is initiated. This timeout isn’t super long in order to ensure you haven’t lost connection rather than the server has nothing to say but it is long enough to cause a slight problem. When the request is active, all other service calls are queued up because they can’t access the session state! As soon as the polling connection times out, the other requests can try their hand at executing while the polling connection reconnects and starts blocking again.

This is almost never what you want since the biggest advantage to the forced-async programming environment of Silverlight enables highly responsive designs. You could write your own session state handler with different locking mechanisms or tweak the polling timeout at the cost of increasingly talkative connections or you can go the route I prefer: create a heartbeat method using a Timer and firing an event if the service response contains something in which you’re interested. This gives you good granularity for how often your clients should ping the server with not too much more trouble. (It took me less time to roll this in a singleton connection class than it did to figure out how to configure the service endpoints for the polling duplex connection and then debug the queued service requests until I figured out what was happening.)

Configure your endpoints to use NTLM transport. This is to enable the user authentication to be passed over the wire and for SharePoint to treat the connection as if it were a browser user. SharePoint will even return the correct user in the SPContext if you interactively sign in as a different user in SharePoint. You don’t have to maintain separate user account management in your application.

<?xml version="1.0"?>
<configuration>
   <connectionStrings>
      <add name="WRDataConnectionString" connectionString="[...];Integrated Security=True" providerName="System.Data.SqlClient"/>
   </connectionStrings>
   <system.web>
      <compilation debug="false" />
      <customErrors mode="Off"></customErrors>
   </system.web>
   <system.serviceModel>
      <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
      <bindings>
         <basicHttpBinding>
            <binding name="customBasicHttpBinding">
               <security mode="TransportCredentialOnly">
                  <transport clientCredentialType="Ntlm" />
               </security>
            </binding>
         </basicHttpBinding>
      </bindings>
      <services>
         <service behaviorConfiguration="AppServiceBehavior" name="App.WCF.Services.AppService">
            <endpoint address="" binding="basicHttpBinding" bindingConfiguration="customBasicHttpBinding" contract="App.WCF.Services.IAppService">
               <identity>
                  <dns value="localhost" />
               </identity>
            </endpoint>
         </service>
      </services>
      <behaviors>
         <serviceBehaviors>
            <behavior name="AppServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
               <serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
               <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
         </serviceBehaviors>
      </behaviors>
   </system.serviceModel>
</configuration>

One thing to note in the above is no address in our endpoint. This is because we have to deploy to many environments, all of which use different naming. Instead of specifying a static address, we actually provide the service base address to the Silverlight application as part of its initial startup parameters in its hosting page using SPContext.Current to pass the current web URL. We just concatenate the service name to the end. This makes it much easier to deploy to various SharePoint environments.

If you happen to run SSL you will need to modify your web config to handle this.

<?xml version="1.0"?>
<configuration>
   <connectionStrings>
      <add name="WRDataConnectionString" connectionString="[...];Integrated Security=True" providerName="System.Data.SqlClient"/>
   </connectionStrings>
   <system.web>
      <compilation debug="false" />
      <customErrors mode="Off"></customErrors>
   </system.web>
   <system.serviceModel>
      <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
      <bindings>
         <basicHttpBinding>
            <binding name="customBasicHttpBinding">
               <security mode="Transport">
                  <transport clientCredentialType="Ntlm" />
               </security>
            </binding>
         </basicHttpBinding>
      </bindings>
      <services>
         <service behaviorConfiguration="AppServiceBehavior" name="App.WCF.Services.AppService">
            <endpoint address="" binding="basicHttpBinding" bindingConfiguration="customBasicHttpBinding" contract="App.WCF.Services.IAppService">
               <identity>
                  <dns value="localhost" />
               </identity>
            </endpoint>
         </service>
      </services>
      <behaviors>
         <serviceBehaviors>
            <behavior name="AppServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
               <serviceMetadata httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
               <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
         </serviceBehaviors>
      </behaviors>
   </system.serviceModel>
</configuration>

Your Silverlight clientconfig will need to match your endpoint to get a successful connection.

With these slight tweaks, creating SharePoint 2007 hostable WCF Services consumable by Silverlight is easy! Use a packager like WSPBuilder and building the deployment WSPs are easy too (just follow the necessary folder structure). For simplicity we deploy to subfolders in the LAYOUTS directory but you can get as creative as you like.

See you space cowboy.
-Erik

Optimized Parallel ForEach .NET 3.5

While talking to a coworker about optimizing some heavy-hit, relatively resource intensive code the topic of multi-threaded programming came up. We discussed the performance implications and whether there would be much benefit since the threads would all be writing to the same variable and resource contention would likely slow things down more than single-threading. While it might not fit in the situation we were discussing at the time, I was still interested since I’ve done some multi-threaded programming and I don’t have any qualms with it.

But since we had more tasks than time, I decided to look at what was out there already and see if any of it could be leveraged. Our code had to target .NET 3.5 since it was running from within SharePoint 2007 context so the new .NET 4 features were not available to us. A quick search turned up this happy class (retrieved from http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/a-simple-parallel-foreach-implementation-5):

    class Countdown : IDisposable
    {
        private readonly ManualResetEvent done;
        private readonly int total;
        private volatile int current;

        public Countdown(int total)
        {
            this.total = total;
            current = total;
            done = new ManualResetEvent(false);
        }

        public void Signal()
        {
            lock (done)
            {
                if (current > 0 && --current == 0)
                    done.Set();
            }
        }

        public void Wait()
        {
            done.WaitOne();
        }

        public void Dispose()
        {
            ((IDisposable)done).Dispose();
        }
   }

A quick review of the code revealed it did exactly what I needed: a class that will track how many tasks I want to fire off, decrement with each Signal() and provide the ability to block my main thread until all the tasks finish. Using this, I wrote a couple extension methods (based on code from the site mentioned above):

public static void ParallelForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
    IList<T> lst = enumerable as IList<T>;
    if (null != lst)
        OptimizedParallelizedForEach(lst, action);
    else
        ParallelizeForEach(enumerable, action, enumerable.Count());
}

public static void ParallelForEach<T>(this ICollection enumerable, Action<T> action)
{
    IList lst = enumerable as IList;
    if (null != lst)
        OptimizedParallelizedForEach(lst, action);
    else
        ParallelizeForEach(enumerable, action, enumerable.Count);
}

What these extensions do is provide the ability for generic and non-generic collections to execute actions on each of their items. You’ll note the code tries to figure out if the data can be cast to IList / /IList since these have a native Count member and don’t rely on iterating a full collection to get the count of elements like the extension for IEnumerator.Count() does. (If you reflect that .NET code you’ll see the Count() extension method does something similar to see if the type can just tell you how big it is without iterating and counting.) This is a nice performance boost or something.

Unoptimized iteration:

private static void ParallelizeForEach<T>(IEnumerable enumerable,
                                          Action<T> action, int count)
{
    using (var cd = new Countdown(count))
    {
        foreach (T current in enumerable)
        {
            T captured = current;
            ThreadPool.QueueUserWorkItem(x =>
            {
                action.Invoke(captured);
                cd.Signal();
            });
        }
        cd.Wait();
    }
}

Optimized iteration:

private static void OptimizedParallelizedForEach<T>(IList enumerable, Action<T> action)
{
    using (var cd = new Countdown(enumerable.Count))
    {
        int count = enumerable.Count;
        for (int i = 0; i < count; i++)
        {
            T captured = (T)enumerable[i];
            ThreadPool.QueueUserWorkItem(x =>
            {
                action.Invoke(captured);
                cd.Signal();
            });
        }
        cd.Wait();
    }
}

private static void OptimizedParallelizedForEach<T>(IList<T> enumerable,
                                                    Action<T> action)
{
    using (var cd = new Countdown(enumerable.Count))
    {
        int count = enumerable.Count;
        for (int i = 0; i < count; i++)
        {
            T captured = (T)enumerable[i];
            ThreadPool.QueueUserWorkItem(x =>
            {
                action.Invoke(captured);
                cd.Signal();
            });
        }
        cd.Wait();
    }
}

You’ll see I have two Optimized methods. That’s because IList and IList have their own indexing operations ([]) and even though the code is exactly the same, I couldn’t find a way to combine the code without getting complaints about ambiguous method calls. So I split them out.

The place I’ve put this to good use is in my previous post where I iterate all the Events inside a type and, if the event is of the right type, attach a listener to it. Since this requires no interaction between the loop iterations this was a great candidate for threading in my opinion. This is code that’s executed at the startup of a Silverlight application and it’s running over 44 events of which 42 need listeners attached. When I switched from a simple foreach to for and then ParallelForEach I saw a total reduction of 1 second from load to render with no other changes. Attaching the debugger I was able to verify all the events wired up and the listener method fired off 100% of the time it should and 0% when it shouldn’t.

So, if you were curious how I was handling my threading in the last post, this is it!

Ha det bra!
-Erik

Dynamically Attach Delegates to WCF Service in Silverlight

My team has a Silverlight 4 application sitting in an ASPX page hosted from SharePoint 2007 consuming WCF Services targeting .NET 3.5 also hosted from within SharePoint. Using this setup, our service methods can access SPContext. This allows us to leverage SharePoint Groups as a way to assign roles to users to grant or deny them access to data we’re hosting in a custom database.

Through our design, we’ve been using a generic class as the response for all our methods. This lets us have fields for data we want to track during each response (an error message for instance) along with a generic field to hold specific typed data.

As our design progressed we discovered a need to see if a user has been removed from a SharePoint group which would signify their revocation of access to specific data. Rather than wait for the user to attempt access and throw an unfriendly message, we decided to add a field to our generic response wrapper. Now we only need to check this field and pop a picker dialog to let the user pick a new organization based on SharePoint Groups to which they belong.

The problem is we have 42 methods that can return this field. Attaching to all of them with specific callbacks for the different eventargs types that were generated by adding a Service Reference to the service in our Silverlight application would be ugly and a pain to maintain as our methods are added and removed as our application evolves.

Through an earlier design decision, I implemented a singleton instance for the communication to the service: ConnectionService. This seemed like a great place to implement the side-listener. Now I just needed to hook up to each of the methods and listen for the field. I accomplished this via reflection:

ConnectionService() //private constructor in singleton class
{
   …
   EventInfo[] events = WcfClient.GetType().GetEvents();

   events.ParallelForEach((evt) =>
   {
      if (evt.EventHandlerType.IsGenericType && null != evt.EventHandlerType.GetGenericArguments()[0].GetProperty("Result"))
      {
         Delegate handler = Delegate.CreateDelegate(
            evt.EventHandlerType,
            this,
            this.GetType().GetMethod("WcfClient_CheckForInvalidUserOrg", BindingFlags.NonPublic | BindingFlags.Instance));

         evt.AddEventHandler(WcfClient, handler);
      }
   });
   …
}

void WcfClient_CheckForInvalidUserOrg(object sender, AsyncCompletedEventArgs e)
{
   try
   {
      PropertyInfo pi = e.GetType().GetProperty("Result");
      if (null != pi)
      {
         WebServiceVoidWrapper result = pi.GetValue(e, null) as WebServiceVoidWrapper;

         if (null != result)
            if (!result.IsGroupActive)
               HandleUserOrgNotValid(sender, e);
      }
   }
   catch (TargetInvocationException exc)
   {
      Debug.WriteLine(exc.Message);
   }
}

Working backwards a bit you see I created a function to be attached to the events. Since all our WCF Service methods return Silverlight-generated EventArgs inheriting from AsyncCompletedEventArgs. The upside is that we can use one method and not have to much with any of the generated code to insert a different base class. The downside is we don’t have direct access to the field in which we’re interested. The Silverlight generated class contains a field called “Result” which is of the type we’re returning. In our case we’re returning from our service method. We reflect the object, ensure the Result field is present and then try to cast it to the base class of our response wrapper which contains the boolean flag we want. If the flag is false, the user’s org is no longer valid and we pass off to another function to handle it.

Now jumping up to the constructor for the singleton class I have code that will grab all the events contained in our service connection object. This object is what exposes the Async methods to our service methods and the events that are fired when the service calls complete. Then I iterate them. You can use a simple for loop for this but I decided to use an extension method that will execute the statements in parallel and wait for them all to complete before moving forward. Since the service connection exposes a couple built-in events that we don’t care about, we reflect the type to ensure it matches the signature we’re interested in from our callback.

Once we’re sure the event is probably one we’re interested in listening to, we create an event delegate of the type required by the event which is accessed from EventHandlerType. The remainder of the delegate is just grabbing the MethodInfo for our callback function and where it exists. We call AddEventHandler and that’s it!

Now when the singleton class is constructed (and the WcfClient object is connected to our service) we cycle through all the events it exposes and attach our generic handler. If we detect the user org is invalid we fire a custom event. This lets our UI pages listen if they want to react but for our purposes we’re using our Silverlight project’s MainPage to pop up a ChildWindow for the user. We do it this way because we actually have a heartbeat method which pulses the service to see if the user’s report data has changed and if so, pop a refresh message. Since this is done in a separate thread, we can’t interact with the UI directly. Instead we fire an event which is listened to by something already in the VisualTree. Calling Dispather.BeginInvoke() lets us marshal the UI work on the proper thread and show the ChildWindow.

I was pretty sure before I started this design that I could make it work but I didn’t expect it to be so succinct.

Happy Coding!
-Erik