« September 2008 | Main | November 2008 »

October 20, 2008

And it happens in a 'snap'

From the complex setups/data entry forms for end users in the past to the modern day simplistic UI, the ERP applications have come a long way. Today, every organization wants a robust ERP with an easy-to-use user interface that encourage greater end-user adoption and acceptance. One of the major reasons for failure of an ERP implementation has been lack of user adoption. And there is nothing wrong with it. Everyone wants a simple approach to most complex of problems, especially when it comes to conducting day to day business transactions. Its all about getting the right data at the right place at the right time in the right format to meet the right needs.

Microsoft has brought the ERP closer to end users with the release of Microsoft Dynamics Snap sample applications for Microsoft Dynamics AX 2009. Although the concept of Snap was started with Microsoft Dynamics AX 3.0 (Business Data Lookup, Time sheet and Vacation Management), followed by Snaps for Microsoft Dynamics AX 4.0 (Business Data Lookup, Time sheet, Vacation Management, Custom Report Generator), this release is meant to bring the additional features of Microsoft Dynamics AX 2009 close to users who do not interact directly with an ERP.

The current release contains the following snap-in applications for use with Microsoft Dynamics AX 2009 and Microsoft Office 2007.

  • Expense Entry - This snap-in enables expenses to be entered in Excel while disconnected from the corporate network, and then posted to Dynamics AX 2009 at a later time. Mobile professionals can use this Microsoft Dynamics Snap application to record expenses as they are incurred while they have downtime on airplanes or other places where they may not have access to the corporate network. Then, when connected again, they can submit the expense for approval and reimbursement. The user who wishes to submit expense reports must be a valid employee in the Dynamics AX configuration. This snap-in uses WCF-based services and LINQ to retrieve data from Dynamics AX, and uses the Application Integration Framework, or AIF to send business documents to Dynamics AX.
  • Business Data Lookup (BDL) - The Business Data Lookup Snap-in enables an Information Worker to look for information in Microsoft Dynamics AX 2009 from within 2007 Microsoft Office system applications. Using a task pane launched within Word, within Microsoft Excel or within an email in Microsoft Outlook, the Information Worker can
    • Search for records like customers, contacts, sales orders, inventory items etc.
    • View details of these records
    • Browse data related to a particular record, e.g. all sales orders for a particular customer
    • Select and copy data into the document
    • Attach the document to a selected record in Microsoft Dynamics AX for reference.

This snap-in is an upgrade of the BDL snap-in that was distributed with Microsoft Dynamics AX 4.0.

For the snap to work effectively, you need .Net 3.5, Microsoft Windows Vista, Microsoft Office 2007, Microsoft Visual Studio 2008 and needless to say the latest version of Microsoft Dynamics AX 2009. This release has been done considering the greater focus on Microsoft Office 2007 and the new features that have come in with this release. This snap version is not supported for Microsoft Office 2003 and has not been tested with Windows XP.

What this means to SI is that they need to understand and customize these components based on client needs. The possibilities are endless as any “out of ERP processing” can now be tied down with correct data (in real time) that can be pulled out and used for say drafting letters, sending invites, sending reminders, correspondence, follow up and response to queries from trading partners. Let the whole supply chain collaborate together!!!

The source code for these snaps is available at codeplex, Microsoft’s open source community site at http://www.codeplex.com/axsnap

October 17, 2008

Using Styles in WPF User Experience Design

One of the many reasons I love doing UX Design work for WPF is because it gives the designer an opportunity to significantly change the presentation aspects without making changes to core functionality. The UI revamping capabilities in WPF are built around four pillars. Styles, Templates, Skins and Themes.  Here is a brief introduction to using Styles in WPF.

A style ( from System.Windows.Style class)  groups together properties that can be set individually at on different controls in different places. Extracting them and setting them as a group allows simple reuse of that set of properties across multiple user interface elements. In that sense, this is very similar to CSS approach.

Here is how it is different. In WPF, Style uses a collection of setters to set the target properties. Creating a Setter basically involves setting the name of dependency property and it’s desired values. It is best to set Styles in Resources repository (in a resource.xaml file) as you can use them across the application or set more specific window level styles to override the application level styles. This approach works great and there is plenty of reuse of code without losing creative control over the output.

Consider following simple code snippet to see how styles work. Ideally, this style description should be covered in a resource.xaml file. Here we are defining it in the stackpanel’s Resources collection to keep things simple.

<StackPanel Orientation= “Horizontal”>
<StackPanel.Resources>
<Style x:key= “myButtonStl”>
                <Setter Property= “Button.Foreground” Value= “Silver” />
                <Setter Property= “Button.Height” Value= “35” />
                <Setter Property= “Button.Width” Value= “65” />
                <Setter.Value>
                                <RotateTransform Angle = “10” />
                </Setter.Value>
</Style>
</StackPanel.Resources>
                <Button Style= “{StaticResource myButtonStl}”> Save</Button>
                <Button Style= “{StaticResource myButtonStl}”> Close </Button>
<StackPanel>

In the Setters section, we defined three properties that will make this Style, gave them a name, ‘myButtonStl’, and applied this style to two buttons we have in the application. Simple!

There is one more approach that can get this done. That involves use of Triggers. Whereas Style applies the properties unconditionally, Triggers get into action when one or more condition is satisfied. More on Triggers in next posting.

Role of Claims based Authentication in Federated Security -- 5 (Zermatt)

Zermatt is the code name of the Single Sign On /Federated Security Solution which Microsoft is planning to offer. This is currently in its Beta. It is a set of classes which helps end users build Security Token Service (STS) of their own and generate the tokens as per their requirement. As long as it fits into the representation standard schema of SAML (Security Assertion Markup Language), the RP (relying party) is capable of understanding the same, the single sign on solution works.

It leverages a host of WS protocols for achieving this like WS Security, WS Policy, WS Trust, WS Federation, etc. Based on the type of client trying to access the service, there are 2 broad categories.

Active Clients: Clients which are WCF Service aware and which can understand all of the WS protocols mentioned above (since WCF is built with all of these WS protocols in it).

Passive Clients: Clients which are not WCF Service aware and which cannot understand all of the WS protocols mentioned above. Good example for this is the browser like IE. Zermatt offers a slightly different solution for this through Http Module and Http Redirect mechanisms for achieving the same.

The Beta SDK , has a decent number of samples to play around and you can always ping me back in case you have any queires around the same.

To summarize, Claims based authentication with Zermatt, will form the foundation for Federated Security in S+S scenario which is also referred to as Cloud Computing in different context.

October 16, 2008

Role of Claims based Authentication in Federated Security -- 4

I guess with part 3, I hope I have covered all the basics required for understanding Zermatt. Just to recap, Claims represent an identity as a set of attributes which can be anything like Email Id, name, Age, certificate, etc and can come from any source like AD/LDAP, WCS, custom DB, etc. These attributes are very similar to the attributes that exists in AD for any user/machine entry that exists in AD.

In a normal Application based Authentication, Application goes to these repositories like AD/DB to get additional attributes information for Authorization needs. In Claims based Authentication, the user does the Authentication and goes to the Application with Claims (attributes) to the Application (Application never goes to repositories for getting the attributes).

The next logical question that comes to ones mind is that when a user goes with Claims to access the Application, how will the Application trust all the claims and that they are issued by a valid Authority? Digital Signatures help in the same. All the claims details which in the serialized form is called a token alias Security Token, is signed by the issuing authority and the Application post verification of the signature, will trust that the claims made has come from the right, trusted source. The Authority which issues tokens is often referred to as STS (Security Token Service). Just like the way certificate chaining can be built to validate the certificates issued by intermediate authorities, Chaining can be built here as well through the same concept of Digital Signatures and ensure that trust relationship between STS's of partner organizations can be built. This ensures that an organization can trust the partner organization STS to do the authentication and the based on the token it received, the local STS can further issue additional claims (token) which will be valid in the local domain. The same logic holds true across departments/ applications on different platforms having their own STSs.

With this background, we will see what Zermatt offers for achieving this Single Signon /Federated Security in the next blog probably the last in this series to cover the basics of Zermatt.

October 15, 2008

Performance Point Server - Reports Migration

Recently I have a weired problem which made me to recreate all the forms that we have created in Development environment to be created in UAT environment.

We have created a set of forms in development environment of PPS and assigned it to Users. Since it's a development environment, the dimensions were deleted from the backend and they were reloaded using ETL routines. Once the development environment is stabilised, time has come to move to UAT. We migrated DB to UAT and created the dimensions with ETL routines.  When we started migrating reports that's when we figured out a weird problem.

We moved one Report from Dev to UAT and opened it in UAT. To our surprise, we could find any data in UAT. We checked the DB and cube and they were all perfect and they all show proper data. We even browsed the cube to find the data proper. Then we had a doubt and we opened the MDX query in the UAT Report. We immediately noticed the MDX queries are referring to MemberIds instead of labels of the dimensions. It was interesting to note that. What this means that the same query which works in Dev will not work in UAT or Prod if you have truncated the dim tables for testing prupose on Dev. If you have clean Dev environment (which in many cases is rare), then your reports will work like charm.

 Now we are stuck with a problem which is typical for a v1 product and we are about to look for the workaround to enable our UAT users to being their test the next day.

We had the following options to resolve this problem:

1. Create the reports all new in UAT

2. Migrate the environment as is

 

October 14, 2008

Where is Windows Aero?

I had been struggling for the past few days in trying to get Aero to work again. It was working fine all along, but a few days back it stopped working and since then I could not get it work again. I searched online for answers but without much luck. Most online articles talk about situation where you are trying to get Aero to work for the first time, but not really if you had it working and it stopped to work in between.

Typically you start with the Personalization screen 

personalization.jpg

and go to Windows Color and Appearance screen

color.jpg

And you click on "Open classic appearance properties for more color options" link to get to the following

aero.jpg

For my case, when I would select "Windows Color and Appearance" (first figure above), I would directly come to "Appearance Settings" (this previous figure) and "Windows Aero" wasn't listed there. The Aero related files were present in C:\Windows\Resources folder and the Windows Experience Index base score was 3.1 as shown in Performance Information and Tools dialog.

So what was really wrong? Ronnie from the Vista forum finally pointed out to this excellent troubleshooting site - CleverCretin's Ultimate Step-by-Step Guide to Troubleshooting Windows Aero. It takes you through various steps in a very user friendly manner. What finally helped in my case was the section that talked about when using remote desktop connection, Vista may switch to basic and after closing the session, Aero may not re-engage. I recalled that the last time Aero worked for me, I was getting onto a desktop sharing session with a colleague using office communicator. The desktop sharing, causes Vista to switch to Basic and after that session had closed, I seemed to have lost Aero.

So I did a new session with another colleage, did desktop sharing and then closed the session. When I then checked the Color and Appearance dialog, Windows Aero was there!!! Pretty freaky, isn't it, but my problem did get solved. 

Language "M"

Microsoft recently announced that it is renaming its textual modelling language from "D" to "M". Check out http://blogs.zdnet.com/microsoft/?p=1631
and http://www.pluralsight.com/community/blogs/dbox/archive/2008/09/06/oslo.aspx

This would be released along with visual modelling tool called Quadrant.

I have always felt the need for textual modelling language given the limitations of visual modelling languages such as DSL tool kit. I feel these are long due in MS platform given that eclipse platform has taken lead check out http://www.eclipse.org/modeling/

October 13, 2008

Working with Application Domains in WPF

In a few of my earlier blogs, I have written about how one can work with Applications domains (or AppDomain) in a Windows Presentation Foundation (WPF) applications. You can find these

  1. Creating and working with Applications in AppDomains
  2. WPF and AppDomains
  3. WPF – AppDomain – Cannot find type

The information however is scattered across these blogs. I hence decided to consolidate the learning from all of these in a single place so that it is easy for the reader to follow through. You can still go ahead and read the above blogs, or read this one, where I will capture all that I have captured in the earlier blogs with more explanation where possible. Special thanks to Hua Wang from Microsoft for clearing doubts around these concepts.

AppDomains is a concept that was introduced along with .NET. Earlier when one talked about applications and executions, one used to deal with processes, which offer isolation boundaries between two executing applications. On a Win32 box, each process gets a 2 GB address to work with and manages all the resources like threads, addresses, executing code, stack, heap etc. Each process however is heavy weight. So in .NET the concept of light weight process was introduced in the form of AppDomains. A Win32 process can host multiple AppDomains, each of which can execute managed code. AppDomains continued to provide same benefits as that of a process, but being light weight, they incur less cost in their creation and destruction. If you want to understand the details around this, you should definitely read Chris Brumme’s blog.

To understand the various concepts, I created a sample application. The same can be downloaded from here. There are four projects in the code

  1. WPFApp: WPF application that will be hosted inside of an appdomain by another application
  2. WPFControlLib: WPF user control library used by WPFApp
  3. HostApp: WPF application that hosts WPFApp inside of an appdomain
  4. Unloader: Class library project to help unloading of appdomain. I will talk about this towards the end of this post

I will cover key aspects of these applications and explain relevant sections of the code here. You can build something fresh or use the code shared earlier to walk along.

Let’s start with WPFControlLib and WPFApp. The intention of these projects is to have some application to host inside of appdomain, so I will keep the contents simple. The WPFControlLib project will control a TextBlock to show that this is user control and also a button that shows a message box, just to show interaction as well. Similarly the WPFApp is also trivial application and it has a TextBlock to show that it is the main window and it also creates an instance of the user control from the WPFControlLib project.

With this in place, let’s now get on with the appdomain work. For this we will now modify the HostApp to add two buttons to the UI, one which is used to create the appdomain and load the WPFApp and another to unload this appdomain. An appdomain can be created by using any of the various overloads the static CreateDomain method. For our case right now, we will use the simplest of these and pass a string that specifies the friendly name

            AppDomain ad = AppDomain.CreateDomain("testDomain");

Our requirement is to run the WPFApp application inside of this newly created appdomain. The easiest way to do this is to use the ExecuteAssembly method and provide the path of this application. This method loads the assembly and executes the entry point. The complete method will look something like below. Note that I am working from D:\Temp\WPFApDTest folder. 

        private void btnLoadAppD_Click(object sender, RoutedEventArgs e)

        {

            AppDomain ad = AppDomain.CreateDomain("testDomain");

            ad.ExecuteAssembly(@"D:\Temp\WPFAppDTest\WPFApp\bin\Debug\WPFApp.exe");

        }

The documentation says that ExecuteAssembly method uses LoadFile context. If you want to understand what the various load contexts are and what they mean, you can read this blog by Suzanne Cook. What load file context essentially means that I can specify the name of the assembly with full path and it will be loaded from that path and not from the current executing assembly’s path or GAC. The end result is the same, though the documentation here isn’t correct. If you use Reflector you will see that ExecuteAssembly actually uses LoadFrom and not LoadFile. See here if you want to know the differences between the two. Alternatively, if we have the WPFApp in the probing path used by Load method (or in GAC), we can then use ExecuteAssemblyByName method as well.

If we build and run now, we expect WPFApp to come up when we hit the Load AppDomain button, but instead we see this error – “Cannot find type 'WPFControlLib.UserControl1'. The assembly used when compiling might be different than that used when loading and the type is missing.  Error at object 'System.Windows.Controls.Grid' in markup file 'WPFApp;component/mainwindow.xaml' Line 8 Position 10.”

You can make out easily from this exception that while loading the WPFApp’s main window, the type UserControl1 failed to load and this is actually inside of the WPFControlLib assembly. This assembly is present in the path from where we loaded WPFApp, but still it fails to load when WPFApp is loaded inside of this other AppDomain.

This happens since the new appdomain inherits the probing path for loading types from the base appdomain. In our case, it means that same as that used by HostApp and hence the WPFControlLib isn’t found and loaded. When creating a new domain, hence we need to also set the ApplicationBase property of AppDomainSetup and pass it as parameter. See this documented here. Let’s make appropriate code change (as below) and run the application again.

        private void btnLoadAppD_Click(object sender, RoutedEventArgs e)

        {

            //need to set the applicationbase appropriately, else any additional assemblies required

            //by WPFApp won't load correctly

            AppDomainSetup setup = new AppDomainSetup();

            setup.ApplicationBase = @"D:\Temp\WPFAppDTest\WPFApp\bin\Debug";

 

            AppDomain ad = AppDomain.CreateDomain("testDomain", null, setup);

            ad.ExecuteAssembly(@"D:\Temp\WPFAppDTest\WPFApp\bin\Debug\WPFApp.exe");

        }

This time it runs fine. Note however that if I had put the WPFApp and WPFControlLib in the same path as HostApp and then used ExecuteAssembly with only WPFApp.exe (instead of full path), this would have  worked without the need to set the ApplicationBase. So far, so good! However if you tried to write any code after the ExecuteAssembly call, like say a MessageBox to say that WPFApp is loaded, it won’t display till the time you actually exit from WPFApp. And in that case the message really has no meaning, does it? What this means is that ExecuteAssembly is pretty much a blocking call since it is actually executing the entry point of WPFApp and that is the main method. The main method will exit only when the application terminates and hence this behavior. How do we handle it? Threading comes to our rescue.

However just threading isn’t sufficient for you may end in an error like this – “Cannot create instance of 'MainWindow' defined in assembly 'WPFApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Exception has been thrown by the target of an invocation.  Error in markup file 'MainWindow.xaml' Line 1 Position 9”. The inner exception gives more details and states – “{"The calling thread must be STA, because many UI components require this."}”. The final working code will look as below.

        private void btnLoadAppD_Click(object sender, RoutedEventArgs e)

        {

            Thread th = new Thread(new ThreadStart(CreateDomainAndLoadApp));

            //The calling thread must be STA, because many UI components require this

            th.SetApartmentState(ApartmentState.STA);

            th.Start();

            MessageBox.Show("WPFApp loading");

        }

 

        private void CreateDomainAndLoadApp()

        {

            //need to set the applicationbase appropriately, else any additional assemblies required

            //by WPFApp won't load correctly

            AppDomainSetup setup = new AppDomainSetup();

            setup.ApplicationBase = @"D:\Temp\WPFAppDTest\WPFApp\bin\Debug";

 

            AppDomain ad = AppDomain.CreateDomain("testDomain", null, setup);

            ad.ExecuteAssembly(@"D:\Temp\WPFAppDTest\WPFApp\bin\Debug\WPFApp.exe");

        }

Build and execute and it will work fine and you will also see the message box also. Needless to say that the message box will appear much before you see WPFApp load up since appdomain creation and launching WPFApp in it takes much longer. However with threading it does, now allow us to keep our HostApp responsive while WPFApp runs in this other appdomain. However this does brings up an interesting aspect. Is there something we can do to speed up the loading of the other appdomain and WPFApp inside of it? It turns out that yes, we can. The primary issue is that when the loader loads assemblies in an appdomain, it is by default not sharable. So even .NET framework files like System.dll, PresentationCore.dll, PresentationFramework.dll, etc will also get loaded again in the new appdomain that we create and this slows down the loading. To control this behavior, we can tag our HostApp with a LoaderOptimization attribute.

This attribute can however be set only on the entry point function. The issue we face now is that WPF applications typically don’t have their entry points exposed and are auto generated during build and this is controlled by configuring the App.xaml file as ApplicationManifest. See more details on how to address this here. In the HostApp, for the App.xaml file, change the Build Action in Properties explorer to Page. Then add the main method to App.xaml.cs and set the appropriate loader optimization attribute.

    public partial class App : Application

    {

        [STAThread]

        [LoaderOptimization(LoaderOptimization.MultiDomainHost)]

        static void Main()

        {

            App app = new App();

            app.InitializeComponent();

            app.Run();

        }

    }

I did a simple time check with this in place. In the HostApp, in the click of Load AppDomain button, I recorded the time and then in the WPFApp, in the window loaded event I again record the time and compared the difference. With the optimization in place the time lag to the WPFApp to start up in the new domain was about 600 milliseconds, while without optimization is was more than 4 seconds. With this, the creation of a new appdomain and loading a WPF application inside of it is pretty much done. Let’s now focus on how to close the application and unload the domain.

The simplest case in this is when the user directly closes WPFApp. As explained earlier, the call to ExecuteAssembly will return. But does that close the appdomain also? How do we find that out? Let’s write some logic to count the total appdomains. That will help us know how many are there at any given time. We will add a Utility class with two static methods to help achieve this. Iterating over the appdomains requires us to use interop services and use methods found in mscoree library. When adding reference, we need to add reference to mscoree.tlb found in C:\Windows\Microsoft.NET\Framework\v2.0.50727.

    class Utility

    {

        public static List<AppDomain> GetProcessAppDomains()

        {

            List<AppDomain> result = new List<AppDomain>();

            IntPtr enumHandle = IntPtr.Zero;

            mscoree.CorRuntimeHostClass host = null;

            try

            {

                host = new mscoree.CorRuntimeHostClass();

                host.EnumDomains(out enumHandle);

                object domain = null;

                host.NextDomain(enumHandle, out domain);

                while (domain != null)

                {

                    result.Add((AppDomain)domain);

                    host.NextDomain(enumHandle, out domain);

                }

            }

            finally

            {

                if (enumHandle != IntPtr.Zero)

                {

                    host.CloseEnum(enumHandle);

                }

                if (host != null)

                {

                    Marshal.ReleaseComObject(host);

                }

            }

 

            return result;

        }

 

        public static AppDomain GetAppDomain(string friendlyName)

        {

            IntPtr enumHandle = IntPtr.Zero;

            mscoree.CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();

            try

            {

                host.EnumDomains(out enumHandle);

 

                object domain = null;

                while (true)

                {

                    host.NextDomain(enumHandle, out domain);

                    if (domain == null)

                    {

                        break;

                    }

                    AppDomain appDomain = (AppDomain)domain;

                    if (appDomain.FriendlyName.Equals(friendlyName))

                    {

                        return appDomain;

                    }

                }

            }

            finally

            {

                host.CloseEnum(enumHandle);

                Marshal.ReleaseComObject(host);

                host = null;

            }

            return null;

        }

    }


We can use the method that returns the list of appdomains just after the call to ExecuteAssembly to see the total appdomains. If you debug, you will realize that there are still 2 appdomains (as seen in the figure below)

countdomain.jpg

The appdomain listed as transparent proxy is the one that we created in our application by invoking CreateAppDomain earlier. Hence we need to unload this appdomain explicitly. The modified method is as shown below

        private void CreateDomainAndLoadApp()

        {

            //need to set the applicationbase appropriately, else any additional assemblies required

            //by WPFApp won't load correctly. If however I copy WPFApp and WPFControlLib to the same

            //folder as HostApp, I won't need this

            AppDomainSetup setup = new AppDomainSetup();

            setup.ApplicationBase = @"D:\Temp\WPFAppDTest\WPFApp\bin\Debug";

 

            AppDomain ad = AppDomain.CreateDomain("testDomain", null, setup);

            ad.ExecuteAssembly(@"D:\Temp\WPFAppDTest\WPFApp\bin\Debug\WPFApp.exe");

 

            AppDomain.Unload(ad);

            //only for debugging. once verified, can comment/remove the following line

            List<AppDomain> list = Utility.GetProcessAppDomains();

        }

The call to Utility.GetProcessAppDomains is for debugging purposes only and helps us quickly verify that now the appdomain count is 1, pointing to the primary appdomain inside of which HostApp is running. This is fine, but what if we want to explicitly unload the appdomain from our HostApp? We had already added a button to HostApp earlier for this purpose so let’s now add some code to it. As we saw earlier, we need to pass the handle to the appdomain to the Unload method. This hence calls for a slight change in the overall code to make the variable ad global.

        private void btnUnloadAppD_Click(object sender, RoutedEventArgs e)

        {

            if (ad != null)

            {

                AppDomain.Unload(ad);

            }

        }

In the button click event handler, we will then write simple logic to unload the appdomain. If you build and run this and try and unload, you will get an exception – “Error while unloading appdomain. (Exception from HRESULT: 0x80131015)”.

When a WPF application is created, a dispatcher thread is also created. If you have written applications in Visual C++/MFC you can possibly relate this to message loop, dispatch message, wndproc etc. The AppDomain.Unload call causes a thread abort exception in the threads currently running in the appdomain. When all the threads are aborted, the appdomain unloads. However in our current case, the dispatcher is in native mode, i.e. executing unmanaged code. This results in it not being aborted and results in CannotUnloadAppDomainException and this is what we saw above. So we want to successfully unload the appdomain, we will need to first kill the dispatcher. Unfortunately, this can’t be done from outside the appdomain, but has to done from within the same appdomain. We will need to make a cross appdomain call and for that we can use CrossAppDomainDelegate. The modified code looks as below.

        private void btnUnloadAppD_Click(object sender, RoutedEventArgs e)

        {

            if (ad != null)

            {

                ad.DoCallBack(new CrossAppDomainDelegate(Shutdown));

                AppDomain.Unload(ad);

                //only for debugging. once verified, can comment/remove the following line

                List<AppDomain> list = Utility.GetProcessAppDomains();

            }

        }

 

        static void Shutdown()

        {

            Application.Current.Dispatcher.InvokeShutdown();

        }

If only life was easier! The DoCallBack code added above fails at runtime with exception – “Could not load file or assembly 'HostApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.”

At first this looks intriguing. The code that is running is HostApp itself, so why can’t it be found. If you however go back a bit in this article, you will notice that when creating the appdomain, we had set the ApplicationBase to point to the directory where the WPFApp is. The cross appdomain delegate is a call that executes inside the appdomain we created, so it will try to load assemblies using the ApplicationBase path and needless to say, HostApp assembly is not present in that path. There are various ways in which we can address this

  1. Ensure that HostApp, WPFApp, WPFControlLib are all in same folder
  2. Make a copy of HostApp in the folder where WPFApp is
  3. Externalize the unloading code in another assembly and copy it in WPFApp folder
  4. Write unloading code in WPFApp itself and invoke it

The third approach will be pretty much same as what we did just now, apart from writing this in another assembly. Also second and third approaches are pretty much the same. Let’s see how we can handle the forth approach. For this we will add another class to WPFApp project.

    class Terminator : MarshalByRefObject

    {

        public Terminator()

        {

            System.Windows.Application.Current.Dispatcher.InvokeShutdown();

        }

    }

This class will then be invoked from the HostApp and will cause the dispatcher to shutdown and eventually allow the appdomain to unload.  This class is derived from MarshalByRefObject since it has to be invoked from cross domain. The code changes are as below

        private void btnUnloadAppD_Click(object sender, RoutedEventArgs e)

        {

            if (ad != null)

            {

                ad.CreateInstanceAndUnwrap("WPFApp", "WPFApp.Terminator");

 

                AppDomain.Unload(ad);

                //only for debugging. once verified, can comment/remove the following line

                List<AppDomain> list = Utility.GetProcessAppDomains();

            }

        }

If only unloading of appdomain was as easy as loading of it was? You may now end up getting a MDA Message – “RaceOnRCWCleanup () was detected. Message: An attempt has been made to free an RCW that is in use.  The RCW is in use on the active thread or another thread.  Attempting to free an in-use RCW can cause corruption or data loss.” I got this message on my dual core Vista machine. Another colleague didn’t get it on her single core XP SP2 machine. It turns out that when you invoke dispatcher shut down, you actually don’t need to call AppDomain.Unload anymore. The appdomain get’s unloaded, but there is some delay. So if you check the count immediate after invoking the shut down, you will still see the count as 2, but if you check this in say few seconds, it will be down to 1. To summarize, the method that finally worked is

        private void btnUnloadAppD_Click(object sender, RoutedEventArgs e)

        {

            //only this call is sufficient

            ad.CreateInstanceAndUnwrap("WPFApp", "WPFApp.Terminator");

        }

This approach hence works, but requires change in WPFApp. If we don’t have access to the code, then this isn’t going to work. The approach 1 I stated above can work, but requires additional housekeeping of copying all assemblies to same folder. If I miss on any 1 assembly, it will cause runtime exceptions. Hence personally I think approach 3 is the best. The terminator class that we added to WPFApp will actually be added to another assembly. At runtime we will copy this assembly to the path from where we loaded WPFApp. Copying 1 assembly is better than copying a whole bunch of assemblies.

In the demo code that I have uploaded, I have added an Unloader assembly that includes the Terminator class. For simplicity, I have set a post build event to copy the Unloader.dll to the output folder or WPFApp. In actual application scenario, this will have to be a file copy operation that HostApp needs to trigger. I have also modified the paths set in HostApp to relative path so that the code will run from where you copy it locally.

This brings me to end of this post. Comments welcome!

Silverlight 2 RTM

Some days back Microsoft had released the RC0 version of Silverlight. Considering that PDC 2008 is just about 2 weeks away, there was much speculation that that's when Silverlight 2 will RTM. But according to Mary-Jo, the release is very much expected later today. Keep an eye on Scott's blog for the announcement.

[Update: 14 Oct 4.20 am] The download should be available in the next few hours. Check the press release.

[Update: 14 Oct 9.00 am] Silverlight 2 is now available. To install the runtime, visit here. For related developer tools, visit here.

October 3, 2008

Midori, RedHawk, MinSafe?

Midori, RedHawk, MinSafe? What does all this mean? Check out the interesting cover story on Redmond Developer News by Mary Jo Foley. Essentially these are projects towards next generation of operating systems. Will they see the light of the day? How will it impact the developers and consumers? That is a question that has no clear answers today and is probably very early in the game to even think of answers. But this sure is an interesting read for now.

October 1, 2008

Part 2: Manage Page level states with VisualStateManager in Silverlight

In part 1 we saw how using basic storyboarding/animation capabilities in Silverlight, we created a simple application where buttons move to new location when clicked and move back when another button is clicked. In this part we will see how using VSM, we can build the same capabilities with lesser animations and lesser code. The complete code (including code for part 1) can be downloaded from here - Download file. Note that I have built this using SL 2.0 RC0, but the concepts explained here will work with Beta 2 bits as well.

Let's add a new page to the application and call it VSMPage.xaml. We will build the same UI but with VSM to manage the animations on the page. Copy the page content (inside of the LayoutRoot grid) from Page.xaml to this page to get the 3 buttons.  To add the VSM related states, we will use Blend. In Blend, first create a new StateGroup and then add states to it to identify the 3 states related to the 3 button. See the figure below. The intention is that each state relates to that particular button being animated and moved down.

states.jpg

Like in part 1, we we want the transitions between each state to always take 0.3 sec and hence we add that as global value at StateGroup level.

                <vsm:VisualStateGroup.Transitions>

                    <vsm:VisualTransition GeneratedDuration="00:00:00.3"/>

                </vsm:VisualStateGroup.Transitions>

 To add the actual state transition, we can now click each state and add the transition. Note that unlike regular animations, the state transitions are typically recorded for time 0 itself. The actual time taken for the transition is managed separately by setting the state transition. Here, we have taken 0.3 sec for all transitions. I can however set this at individual state transition as well. The Button1State animation looks like below

                <vsm:VisualState x:Name="Button1State">

                    <Storyboard>

                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="btn1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">

                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="40"/>

                        </DoubleAnimationUsingKeyFrames>

                    </Storyboard>

                </vsm:VisualState>

Checkout the breaking changes between Beta 2 and RC0 to know the differences introduced for VSM and state transitions. Back to the article and we can now add similar transitions for the rest of the states and this will finally look like the following

<UserControl x:Class="SLTestApp.VSMPage"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"

   Width="400" Height="300" xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">

    <Grid x:Name="LayoutRoot" Background="White">

        <vsm:VisualStateManager.VisualStateGroups>

            <vsm:VisualStateGroup x:Name="MenuStates">

                <vsm:VisualStateGroup.Transitions>

                    <vsm:VisualTransition GeneratedDuration="00:00:00.3"/>

                </vsm:VisualStateGroup.Transitions>

                <vsm:VisualState x:Name="Button1State">

                    <Storyboard>

                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="btn1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">

                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="40"/>

                        </DoubleAnimationUsingKeyFrames>

                    </Storyboard>

                </vsm:VisualState>

                <vsm:VisualState x:Name="Button2State">

                    <Storyboard>

                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="btn2" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">

                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="-72"/>

                        </DoubleAnimationUsingKeyFrames>

                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="btn2" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">

                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="40"/>

                        </DoubleAnimationUsingKeyFrames>

                    </Storyboard>

                </vsm:VisualState>

                <vsm:VisualState x:Name="Button3State">

                    <Storyboard>

                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="btn3" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">

                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="-142"/>

                        </DoubleAnimationUsingKeyFrames>

                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="btn3" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">

                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="40"/>

                        </DoubleAnimationUsingKeyFrames>

                    </Storyboard>

                </vsm:VisualState>

            </vsm:VisualStateGroup>

        </vsm:VisualStateManager.VisualStateGroups>

        <Button HorizontalAlignment="Left" Margin="8,8,0,0" x:Name="btn1" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5" Content="Button 1" Click="btn1_Click">

            <Button.RenderTransform>

                <TransformGroup>

                    <ScaleTransform/>

                    <SkewTransform/>

                    <RotateTransform/>

                    <TranslateTransform/>

                </TransformGroup>

            </Button.RenderTransform>

        </Button>

        <Button HorizontalAlignment="Left" Margin="80,8,0,0" x:Name="btn2" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5" Content="Button 2" Click="btn2_Click">

            <Button.RenderTransform>

                <TransformGroup>

                    <ScaleTransform/>

                    <SkewTransform/>

                    <RotateTransform/>

                    <TranslateTransform/>

                </TransformGroup>

            </Button.RenderTransform>

        </Button>

        <Button HorizontalAlignment="Left" Margin="150,8,0,0" x:Name="btn3" VerticalAlignment="Top" Content="Button 3" d:LayoutOverrides="Width" Click="btn3_Click">

            <Button.RenderTransform>

                <TransformGroup>

                    <ScaleTransform/>

                    <SkewTransform/>

                    <RotateTransform/>

                    <TranslateTransform/>

                </TransformGroup>

            </Button.RenderTransform>

        </Button>

 

    </Grid>

</UserControl>

The only piece left now is to run these transitions at appropriate time and for that we will handle the button click events and write a single line to invoke the VSM's GoTo method. The following is how the code behind file looks like.

    public partial class VSMPage : UserControl

    {

        public VSMPage()

        {

            InitializeComponent();

        }

 

        private void btn1_Click(object sender, RoutedEventArgs e)

        {

            VisualStateManager.GoToState(this, "Button1State", true);

        }

 

        private void btn2_Click(object sender, RoutedEventArgs e)

        {

            VisualStateManager.GoToState(this, "Button2State", true);

        }

 

        private void btn3_Click(object sender, RoutedEventArgs e)

        {

            VisualStateManager.GoToState(this, "Button3State", true);

        }

    }

Build and run the application (do make sure that you have modified the App.xaml.cs to point to VSMPage in the Application_Startup event handler) and you will see the animations working just like before. Unlike earlier case however, we have not written any animation to move the button back to its original place. That is automatically handled by VSM when managing the state transitions. Additionally, this also means the code behind logic is greatly simplified and we no longer need to track which is the currently selected button. All we do is invoke the VSM's GoTo method. The third parameter signifies that we want to play the storyboard associated with this state change. 

So next time you are building a Silverlight application think how you can define various states for your application and use VSM to effectively transition between them.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter