Infosys Microsoft Alliance and Solutions blog

« June 2007 | Main | August 2007 »

July 26, 2007

WPF - Finding Control Location

Recently for a sample application I was building in WPF, I had to find the location of a button control on the window. Playing around with Button.Margin didn't help much since it gave the position with reference to the immediate parent. So if the button was inside a Grid and the grid itself inside say a StackPanel, the values were incorrect.

Searching on the forums, I got some ideas and following is what worked for me

            GeneralTransform transform = button1.TransformToAncestor(this);

            Point rootPoint = transform.Transform(new Point(0, 0));

button1 is the control for whom I wanted to find the location. The this represents the top level window. The value obtained in rootPoint is the Left and Top cordinates of the button with respect to the top level window. Adding button's height and width will give the complete location details.

 

July 17, 2007

Handling AppDomain's AssemblyLoad Event

In my earlier blog I had discussed about creating a new AppDomain. You may have a need to track the loading of assemblies in this newly created AppDomain. AppDomain class provides an event for this purpose called AssemblyLoad. However when I tried to work with it, I never got the event to fire up.

The catch is that the event handler needs to be static. When I had added the event handler via VS 2005 by using the tip that appears once you type "+=" after an event, it added the event handler as private void and missed the "static" qualifier. Due to this the event handler was not getting called. Once I added this, it started to work like a breeze. The handler signature will look something like the following

        static void domain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)

        {

            //add code to handle assembly load event here

        }

July 10, 2007

Windows Workflow Foundation Web Workflow Approvals Starter Kit – A Review

As you might be aware Microsoft had released the WF web workflow approvals starter kit which is a Visual Studio 2005 project that demonstrates using Windows Workflow Foundation for simple task-oriented workflow in an ASP.NET Web application.

I decided to review the starter kit so as to understand how efficiently and easily this starter kit could be used to develop simple task-oriented workflows in ASP.NET web applications. Let me list down my viewpoints on the same.

  • For starters, I personally think that the starter kit code is a little complicated to understand for even a developer who has sufficient coding experience on the .Net platform and has basic knowledge of WF. The implementation needs to be simplified, if possible, so that a developer can easily understand the logic. I saw similar concerns being raised on other forums as well.
  • To worsen things, the 8 page documentation that comes with the starter kit gives just an overview about the project structure and its contents and doesn’t really help much in understanding the sample and its architecture in detail. A better documentation would really help. But the good news is that the WF team at Microsoft is soon going to release a whitepaper on the starter kit that describes the sample in more detail.
  • Another thing which I found interesting is the usage of a custom activity called “User activity”, when I think the out of the box HandleExternalEvent activity could have been used to achieve the same functionality and it would have made the sample look a little more simpler as well. Again this is another question which I found many people raising on different forums.
  • The workflow image viewer component that is part of the application is a component that would generate the workflow image at runtime with the currently executing activity highlighted; in PNG format. This I thought was really a cool feature and was something that caught my eye. But was something as advanced as this required in a starter kit? I am really not very sure.
  • From a core workflow perspective, things looked very simple and easy to understand; apart from the custom activity which I think could have been done away with.
All in all I feel that the starter kit could have been much simpler than what it actually is. But again if you are an experienced developer you would surely know what to take from these kits and what not Laughing. But I guess when the whitepaper on this starter kit comes out, some or all of these questions could get answered and we would get a much clearer picture. But I would encourage you to try the sample out. The starter kit can be downloaded from here. Try it out and let me know your viewpoints and opinions on the same.

July 6, 2007

WPF Application Default Assembly Version

If you have worked with .NET 2.0 and VS 2005 and then move onto .NET 3.0 and WPF, be careful of the default assembly version. From .NET 1.1 to .NET 2.0, a change was made to default the assembly version to 1.0.0.0 in the AssemblyInfo.cs file, since the C# compiler would otherwise keep incrementing these values everytime you hit "build".

Seems like with for WPF in .NET 3.0, this change has been reversed, if you are working with the WPF Extensions running with VS 2005. The WCF and WF extensions still use 1.0.0.0 as the default version. So if you are working with WPF extensions, be careful, else you will end up having always changing version numbers.

However this seems to have been fixed in Orcas. I checked with Orcas Beta 1 and the version number for WPF apps has again been defaulted to 1.0.0.0.

July 5, 2007

Creating and Working with Applications in AppDomains

We all have read that AppDomains are light weight processes that .net uses to run applications, but I hardly ever played with AppDomains directly. Recently I decided to give this a try and following are my findings. If you want to get more information on AppDomains, check this excellent blog by Chris Brumme.

To create a new AppDomain, you use the static method CreateAppDomain and can give a friendly name to the AppDomain at this time. Once the domain is created, you can call ExecuteAssembly to load and run an assembly from its entry point. The code will look something like the following. For my testing, I worked with an application that can execute on its own.

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

            ad.ExecuteAssembly("DummyApp.exe");

Note that the above assumes that the DummyApp.exe is in the same path is the calling application, else complete path will need to be specified.

In .net 2.0 a new method ExecuteAssemblyByName has been added and is the preferred method to use. As documented, these methods execute the entry point, but not on a different thread. This means that while the loaded assembly is executing, the application that started it, will not execute further, since the primary thread is busy executing the assembly loaded in the new AppDomain. You will have to close the assembly (in case of applications) or unload the AppDomain to continue ahead with your main application.

In case you want to work with both the applications, you can create the AppDomain on a different thread. The code will look something like the following

        {

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

            th.Start();

        }

 

        private void func1()

        {

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

            ad.ExecuteAssembly("DummyApp.exe");

        }

To close the newly created AppDomain in this case, you can do a thread abort or upload the AppDomain. In my code, when working with .net Winform applications, i had issues in unloading the AppDomains and hence I had to work with thread abort. However thread abort it abrut and brute force method to kill that AppDomain and the executing assembly may not close gracefully.

If that assembly is an application, you will have multiple options to handle this thread abort and try and exit gracefully. The various event handlers that you can handle are as below. First place to write handlers is in the MainForm and handle deactivation and handledestroyed events.

        protected override void OnDeactivate(EventArgs e)

        {

            //do your clean up logic here

            base.OnDeactivate(e);

        }

 

        protected override void OnHandleDestroyed(EventArgs e)

        {

            //handle already destroyed. be careful of what you want to clean up here

            base.OnHandleDestroyed(e);

        }

The other option is to handle the ThreadAbortException in the Program.cs file from where the application has been started.  

        static void Main()

        {

            Application.EnableVisualStyles();

            Application.SetCompatibleTextRenderingDefault(false);

            Form1 frm = new Form1();

            try

            {

                Application.Run(frm);

            }

            catch (ThreadAbortException ex)

            {

                //form is Inaccessible by this time and is disposed

                MessageBox.Show(ex.Message);

            }

        }

I also tried the same with WPF applications. A few issues with WPF and AppDomains are listed here. I could get things working with WPF applications as well. I am not sure at this time if all will work well or not, since I haven't done any extensive testing.

A key thing to note with WPF however is setting the correct thread apartment state, else while trying to execute a WPF application in a new AppDomain, you will get an error which reads something like following

"Cannot create instance of 'Window1' defined in assembly 'DummyApp, Version=1.0.2739.17241, Culture=neutral, PublicKeyToken=null'. Exception has been thrown by the target of an invocation.  Error in markup file 'Window1.xaml'."

This may sound tricky since the WPF application on its own runs without issues. It turns out that finding the solution to this issue is also fairly simple since the inner exception states this - "{"The calling thread must be STA, because many UI components require this."}". Hence prior to starting the thread, we need to set the apartment state of the thread as shown below

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

            th.SetApartmentState(ApartmentState.STA);

            th.Start();

With this in place, I found that the WPF application loaded in the AppDomain worked fine. Surprisingly, the AppDomain.Unload method also worked, which had thrown error when working with Winform applications.

As I mentioned earlier, I haven't done extensive testing on this. If you have any other experience or comment to make, do write back.

Subscribe to this blog's feed

Follow us on

Blogger Profiles

Infosys on Twitter