Wednesday, August 24, 2011

The curious world of WPF Popups

Popups in WPF sometimes appear to behave not as expected. Mostly it’s just because they are not used correctly or there’s just not enough understanding of the popups functionality and the relations to other controls. I encountered such a situation lately in a project and thought it would be worth to share some thoughts on this topic.

First of all it needs to be clear that a popup is not a window in terms of WPF. This aspect is important because a popup cannot receive focus inside an application. This again is relevant in order to understand how the StaysOpen property of the Popup class works and why a popup sometimes is not closed or opened as expected. Let’s have a look at a first example. Suppose we have the following XAML code:

<ToggleButton x:Name="toggleButton"
              HorizontalAlignment="Center"
              VerticalAlignment="Center" 
              Content="Popup"/>
<Popup IsOpen="{Binding ElementName=toggleButton, Path=IsChecked}"
        Placement="MousePoint"
        StaysOpen="False">
</Popup>

What would you expect to happen here? Will the popup be closed when the toggle button gets unchecked? No, it will not. When the user clicks the toggle button it changes its state to “checked”. This opens the popup, because the popups IsOpen property is bound to the toggle buttons IsChecked property. When the toggle button is clicked again the following happens:
1. The toggle button receives focus. Therefore the popup is closed, because StaysOpen is set to false.
2. The IsOpen property is set to false.
3. Because of the implicit two-way binding between IsOpen and IsChekched, the “checked” status of the toggle button is set to false.
4. The toggle button receives the click event and gets checked, resulting in an open popup.

The simple solution to that problem is to define the binding above as one-way, so the closing of the popup would not change the “checked” state of the toggle button. Another possibility would be to set StaysOpen to true. This results in not closing the popup automatically when the toggle button receives focus. It is closed not until IsChecked changes.

Another problem may be that you have a popup within a popup. Imagine a popup where you have several other controls and maybe some of the controls open another popup which is then displayed over the existing popup. In this situation you would expect to close the “child” popup by clicking on the “parent” popup. But that’s just not possible with the build in functionality. Remember the statement from the beginning: Popups can’t receive focus. Therefore the StaysOpen property won’t work, because the parent popup doesn’t get the focus. This in turn means that there is no way to automatically close the child popup by clicking somewhere on the parent popup. One solution to that problem is to listen to mouse events on the application window and to route these information to “child” popups. We will see this in the following simple example:

<Window x:Class="WpfPopupTestApp.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel Height="29">
    <ToggleButton x:Name="toggleButton"
                  HorizontalAlignment="Center"
                  VerticalAlignment="Center"
                  Content="Popup"/>
    <Popup IsOpen="{Binding ElementName=toggleButton, Path=IsChecked}"
           Placement="MousePoint"
           StaysOpen="True">
      <StackPanel Width="100" Height="100">
        <ToggleButton x:Name="togglePopupButton"
                      HorizontalAlignment="Center"
                      Content="Popup"/>
        <Popup x:Name="popup2"
               IsOpen="{Binding ElementName=togglePopupButton, Path=IsChecked}"
  Placement="MousePoint"
               StaysOpen="False"
               Opened="popup2_Opened">
          <StackPanel Width="50" Height="50">
            <Label Content="Test"/>
          </StackPanel>
        </Popup>
      </StackPanel>
    </Popup>
  </StackPanel>
</Window> 

This small XAML snippet defines a toggle button that can be used to open a popup. Inside the popup is another toggle button that opens another popup inside the parent popup. The StaysOpen property of both popups is set to false so that they are automatically closed when a control receives focus. With the current setup, the child popup lacks this functionality. That’s why it needs a manual trigger. As mentioned above we will start listening on mouse events – concretely the PreviewMouseUp event – of the application window. In the event handler we will invoke previously registered callback methods to inform child popups of the event. The following additions have to be made to the application main window:

private IList<MainWindowMouseDownCallback> callbacks = new List<MainWindowMouseDownCallback>(); 

public delegate void MainWindowMouseDownCallback(object sender, MouseButtonEventArgs args); 

public MainWindow()
{
    InitializeComponent();
    this.PreviewMouseUp += MainWindow_MouseUp;
} 

public void RegisterMouseDownCallback(MainWindowMouseDownCallback callback)
{
    callbacks.Add(callback);
} 

public void UnRegisterMouseDownCallback(MainWindowMouseDownCallback callback)
{
    if (callbacks.Contains(callback))
    {
        callbacks.Remove(callback);
    }
} 

void MainWindow_MouseUp(object sender, MouseButtonEventArgs e)
{
    foreach (var callback in callbacks)
    {
        callback.Invoke(sender, e);
    }
}

We define our own special callback delegate for this situation. Child popups may register such a callback to get informed of mouse up events. Pay attention to use PreviewMouseUp and not just MouseUp. Otherwise you may end up with someone already handling the mouse event so you don’t get notified. In order to clean up properly we also added a method to unregister callbacks. Let’s have a look at what needs to be done in the popup:

private void popup2_Opened(object sender, EventArgs e)
{
    var parent = GetParent(popup2, typeof(Popup));
    if (parent != null)
    {
        this.RegisterMouseDownCallback(PopupCallback);
        popup2.Closed += popup2_Closed;
    }
} 

private void popup2_Closed(object sender, EventArgs e)
{
    popup2.Closed -= popup2_Closed;
    this.UnRegisterMouseDownCallback(PopupCallback);
}

private void PopupCallback(object sender, MouseButtonEventArgs args)
{
    var obj = args.Source;
    if (obj != null)
    {
        var parent = GetParent(obj as DependencyObject, typeof(Popup));
        if (parent != null && !parent.Equals(popup2))
        {
            this.popup2.IsOpen = false;
        }
    }
}

When the popup opens, it adds its callback to the application main window. It does this only if it has a parent control in its logical tree which is a popup too. Additionally it subscribes to its own close event in order to properly unregister the callback. In the callback we’re looking at the source of the mouse event. If the source or one of its parents in the logical tree is a popup and this popup is not the child popup itself, then we close the child popup. There is no magic at all. To find a parent control of a given type in the logical tree the GetParent method is used. This method is implemented as follows:

private static DependencyObject GetParent(DependencyObject current, Type type)
{
    var parent = LogicalTreeHelper.GetParent(current);
    if (parent == null)
        return null; 

    if (parent.GetType().Equals(type))
    {
        return parent;
    }
    return GetParent(parent, type);
}

This method uses the LogicalTreeHelper to walk up the logical tree until it finds an element of the given type. This element is then returned. With these additions to the popup and the main window we’re now able to close a child popup when we click anywhere on the parent popup.
This was a very small and simple example and I’m sure that everyone is conflicted with more complex problems and applications but I hope it gives you a starting point when you’re entering the curious world of WPF popups.

Friday, August 12, 2011

Aspect oriented programming with PostSharp and SharePoint 2010

The main purpose of aspect oriented programming in today’s software development processes is to separate the so called cross cutting concerns from the applications business logic. Things like logging or exception handling are used across all layers of an application. They should always be done in the same way and should not distract developers with their complexity. Aspect oriented programming can be used to achieve these goals. The following blog post shows an example of a caching aspect implemented with PostSharp and used inside a SharePoint 2010 WebPart. For an overview of implementing aspects with PostSharp have a look at http://www.sharpcrafters.com/postsharp/documentation. If you need more information on aspect orientation read http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.37.4987&rep=rep1&type=pdf or http://en.wikipedia.org/wiki/Aspect-oriented_programming.

Let’s start by creating a new Visual Studio solution. For the example I used the Visual Webpart template. After creation of the solution is finished we can directly start by creating the caching aspect. First of all a reference to the PostSharp library is needed. PostSharp comes with a library for different development environments. There is one library for Silverlight, one for developing applications with the compact framework and one for other .NET applications. I used the signed assembly for .NET 2.0. In order to make the assembly available to SharePoint we have to add it not only to the references of the project but also to the deployment package. With the new package windows in Visual Studio this can be achieved with a few clicks. Just open the package in the solution explorer. In the package window choose the ‘Advanced’ tab at the bottom:

On the following page you can add any existing assembly:

After adding the library and making the settings for deployment, we can now start to implement our aspect. PostSharp provides several base aspects that can be used to realize custom aspects. For the example I used the OnMethodBoundaryAspect. This aspect can be used to decorate a single method and contains the following base methods:

-          OnEntry
-          OnExit
-          OnSuccess
-          OnException
When you decorate a method with this aspect the preceding methods are respectively called on entering the method, on leaving the method or when an exception occurs. This makes it really comfortable to provide additional functionality for any given method. You can just write your attribute over a method and PostSharp will take care of linking the functionality of the aspect to the method. In order to achieve this, PostSharp integrates itself in the build process as a kind of post build event. That means that after building your assemblies, PostSharp takes them, searches for aspects and integrates the appropriate attribute implementation.

There are already some examples for caching aspects realized with PostSharp on the web. I used one from the sharpcrafters tutorial site. This aspect is designed for ASP.NET web applications so it also fits in the SharePoint context. Let’s have a look at the implementation and start with the OnEntry method:
public override void OnEntry(MethodExecutionArgs args)
{
    // Compute the cache key.
    string cacheKey = GetCacheKey(args.Instance, args.Arguments); 

    // Fetch the value from the cache.
    object value = HttpRuntime.Cache[cacheKey]; 

    if (value != null)
    {
        // The value was found in cache. Don't execute the method. Return
    immediately.
        args.ReturnValue = value;
        args.FlowBehavior = FlowBehavior.Return;
    }
    else
    {
        // The value was NOT found in cache. Continue with method execution, but
     store
        // the cache key so that we don't have to compute it in OnSuccess.
        args.MethodExecutionTag = cacheKey;
    }
}

The cache key for every decorated method is computed by the method name, its parameters and the concrete instance the method is executed in. This does not cover all cases, but it is sufficient for this example. With the fetched cache key, the runtime cache is accessed to provide the according value. If a value is found, this value will be returned from the method. Otherwise the computed cache key is temporarily stored in a Tags object which will be used in the OnSuccess method:
public override void OnSuccess(MethodExecutionArgs args)
{
    var cacheKey = (string)args.MethodExecutionTag;
    HttpRuntime.Cache[cacheKey] = args.ReturnValue;
}

The OnSuccess method takes the cache key that was built in the OnEntry method and stores the result of the executing method in the runtime cache. The next time the method is called with the same arguments, the aspect will provide the value directly from the cache instead of executing the whole method.
Using the caching aspect
With our caching aspect implemented we are now ready to use it. As with any attribute in .NET this is fairly easy. Just write the attribute over any method you want. In the example I had a method that returned an employee object from a SharePoint list for a given ID:
[Caching]
private static Employee GetEmployee(string id)
{
    using (var web = SPContext.Current.Web)
    {
        var list = web.GetList("/Lists/Employees");
        var item = list.GetItemById(Int32.Parse(id));

        if(item != null)
        {
            var result = new Employee
                                {
                                    Name = item["Title"].ToString(),
                                    Team = item["Team"].ToString(),
                                    Contribution = (double) item["Contribution"]
                                    //... properties left out for brevity
                                };
            return result;
        }
    }
    return null;
}

The retrieval of an employee happens only on the first call of the method. On the second call with the same ID the employee object is directly from the cache.