Wednesday, March 24, 2010

Attached Behaviors

It is kind of late in the game, but I thought I would cover how to bring WPF DataGrid selected item into view using Attached Behaviors. There is a nice article by Josh Smith which covers Attached Behaviors in more details. I will just cover a specific case with DataGrid.

When you are using MVVM sometimes there is a business need to change selected item from ViewModel and when it happens occasionally users will need to manually scroll to that item. This could be very confusing to the user. The desired behavior would be to scroll selected item into the view automatically. 

There are multiple ways to solve this problem:

- Create an event handler for DataGrid.SelectionChanged event. If you however have multiple DataGrids in your project your code behind file will be polluted with these handlers. This is exactly the case why we are using MVVM to avoid code in code behind files and have a clear separation of concerns.

- Second approach will require extending DataGrid class and adding desired behavior. This is an overkill, since now everybody on the project will need to remember to use custom DataGrid. And if there are many of them it requires changes.

- Third approach using Attached Behaviors is very lightweight, “XAML friendly” and preserves MVVM separation of concerns.  All you need is to create a separate class which can be used sparingly as developers see fit, it can sit in your project and be handy when need arises.

Below is an example of such class I used for DataGrid.

namespace MyProject.AttachedBehaviors
{
public class DataGridBehavior
{
#region AutoScrollIntoView

public static bool GetAutoScrollIntoView(DataGrid dataGrid)
{
return (bool)dataGrid.GetValue(AutoScrollIntoViewProperty);
}

public static void SetAutoScrollIntoView(
DataGrid dataGrid, bool value)
{
dataGrid.SetValue(AutoScrollIntoViewProperty, value);
}

public static readonly DependencyProperty AutoScrollIntoViewProperty =
DependencyProperty.RegisterAttached(
"AutoScrollIntoView",
typeof(bool),
typeof(DataGridBehavior),
new UIPropertyMetadata(false, OnAutoScrollIntoViewWhenSelectionChanged));

static void OnAutoScrollIntoViewWhenSelectionChanged(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = depObj as DataGrid;
if (dataGrid == null)
return;

if (!(e.NewValue is bool))
return;

if ((bool)e.NewValue)
dataGrid.SelectionChanged += OnDataGridSelectionChanged;
else
dataGrid.SelectionChanged -= OnDataGridSelectionChanged;
}

static void OnDataGridSelectionChanged(object sender, RoutedEventArgs e)
{
// Only react to the SelectionChanged event raised by the DataGrid
// Ignore all ancestors.
if (!Object.ReferenceEquals(sender, e.OriginalSource))
return;

DataGrid dataGrid = e.OriginalSource as DataGrid;
if (dataGrid != null && dataGrid.SelectedItem != null)
dataGrid.ScrollIntoView(dataGrid.SelectedItem);
}

#endregion // AutoScrollIntoView

}
}

Now is XAML you will need to reference the above namespace:

xmlns:localBehaviors="clr-namespace:MyProject.AttachedBehaviors"

And use it in your DataGrid in the following manner whenever you like to see such behavior.

<wpfToolkit:DataGrid 
EnableColumnVirtualization="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
Grid.Row="0"
DataContext="{Binding}"
ItemsSource="{Binding Path=Entities}"
SelectedItem="{Binding
Path=EntityNavigation.CurrentEntity,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay,
Converter={StaticResource ChildToParentEntityViewModelConverter}}"

localBehaviors:DataGridBehavior.AutoScrollIntoView="True"

...
/>

This (localBehaviors:DataGridBehavior.AutoScrollIntoView="True") last line in the XAML markup above will do it.

This was another ISolvable<TProblem>.

Happy Coding!

Tuesday, March 23, 2010

Avoid memory leaks in .NET

There is enough said about memory leaks in .NET when using events. BING or Google for "weak event". The problem is that when you have an observer for some object's event myObj.MyEvent when observer is no longer needed myObj.MyEvent still keeps reference to observer's event handling delegate. Thus preventing observer from being garbage collected. The solution is to always unsubscribe observer from any events it might listen to before disposing it. Well, this is easier said than done.

One way to deal with this issue is to create weak event references. There are many patterns and frameworks which support weak events such as EventAggregator in Prism or in WPF MVVM app template there is WeakEventReference.

The other way is to keep reference to event handler and always unsubscribe before disposing. This could be tricky.

The third way is very simple but covers only a subset of all possible event scenarios. This is what I will show in here. Suppose you have a handler which is no longer needed after event is fired. Then you can use lambda expression or anonymous delegate and keep reference to it only until it is in the scope. Here is an example. In this example I actually have two handlers which are not needed after either one of them is executed.  

   1: // declare a reference to event handler
   2: EventHandler myEvent1Result = null;
   3: EventHandler myEvent2Result = null;
   4:  
   5: // when MyEvent1 fires we handle it like so 
   6: myEvent1Result = (s, ea) =>
   7:     {
   8:         // unsubscribe from events immedeately
   9:         (s as MyObject).MyEvent1Event -= myEvent1Result;
  10:         (s as MyObject).MyEvent2Event -= myEvent2Result;
  11:  
  12:         // do some other processing
  13:         //....
  14:     };
  15:  
  16: // when MyEvent2 fires we handle it like so 
  17: myEvent2Result = (s, ea) =>
  18:    {
  19:        // unsubscribe from events immedeately
  20:        (s as MyObject).MyEvent1Event -= myEvent1Result;
  21:        (s as MyObject).MyEvent2Event -= myEvent2Result;
  22:  
  23:        // do some other processing
  24:        //....
  25:    };
  26:  
  27: // once we created event handlers now we can subscribe them.
  28: myObject.MyEvent1Event += myEvent1Result;
  29: myObject.MyEvent2Event += myEvent2Result; 

Happy coding!

Wednesday, January 6, 2010

Uninstalling ClickOnce

ClickOnce deployment model has many benefits, but it also has some deficiencies. One of these deficiencies is inability to uninstall ClickOnce applications for a different user.
Let’s take a look at the following scenario.
You are an departmental computer admin in a large enterprise where regular users do not have the ability to open “Add/Remove Programs”, but they can however install ClickOnce applications. When ClickOnce application is installed a folder is created in %USERPROFILE%\Local Settings\Apps for that application. There is also a registry entry added under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall. This entry will have an UninstallString for a specific application. Something like this:
rundll32.exe dfshim.dll,ShArpMaintain MyClickOnceApplicationName.application, Culture=neutral, PublicKeyToken=9999999999999999, processorArchitecture=msil

Let’s say there are some obsolete ClickOnce applications which need to be removed from a regular user profile.

If you try logging in under regular user account and SHIFT+RightClick on “Add/Remove Programs” you would be able to invoke “Run as…”, in this case you would attempt to use your admin credentials. But the list of installed applications will not have the one you are looking for, because “Add/Remove Programs” will list apps from CURRENT_MACHINE and admin user profile. Now there is a problem. You could of course give necessary privileges to that regular user and let him do it. But what if you can’t give such privileges to a user, because of a security concern you have or a company policy, or you lack required permissions? Then there is a better way.

You may create an uninstall.cmd script and place it into original application installation folder, like a shared drive or your IIS. Below is script content. Replace MyClickOnceApplication with the name of your application. Use uninstall command line from the registry (see above on how to get that command).

   1: echo off

   2:  
   3: cls
   4:  
   5: Echo MyClickOnceApplication...
   6:  
   7: cd c:\
   8:  
   9: taskkill /F /IM "MyClickOnceApplication.exe"
  10:  
  11: cls
  12:  
  13: Echo Uninstalling MyClickOnceApplication...
  14:  
  15: cd %USERPROFILE%\Start Menu\Programs\Startup 
  16:  
  17: if exist "MyClickOnceApplication.appref-ms" del "MyClickOnceApplication.appref-ms"
  18:  
  19: cd c:\windows
  20: rundll32.exe dfshim.dll,ShArpMaintain MyClickOnceApplicationName.application, Culture=neutral, PublicKeyToken=0000000000000000, processorArchitecture=msil
This is using VB script and SendKey to auto press OK button during uninstall.
   1: On Error Resume Next 
   2: Set objShell = WScript.CreateObject("WScript.Shell")
   3: objShell.Run "taskkill /f /im MyClickOnceApplication*"
   4: Dim returnCode
   5: returnCode = objShell.Run("cmd /K CD " + Chr(34) + "%USERPROFILE%\Start Menu\Programs\Startup" + Chr(34)+" & del " + Chr(34)+"MyClickOnceApplication.appref-ms" + Chr(34),0, false)
   6: objShell.Run "rundll32.exe dfshim.dll,ShArpMaintain MyClickOnceApplication.application, Culture=neutral, PublicKeyToken=0000000000000000, processorArchitecture=msil"
   7: Do Until Success = True
   8:     Success = objShell.AppActivate("MyClickOnceApplication")
   9:     Wscript.Sleep 200
  10: Loop
  11: objShell.SendKeys "OK"

Then you may either send a link to the user, or issue required update which in turn calls one of the above scripts. If you are using script without SendKey command then user will see the following window and you would need to instruct user to click OK button.

image 

Conclusion

Now you know how to remove ClickOnce apps for a different user. Although many of these commands are exposed via APIs you would still need to run ProcMon to monitor registry changes. Simply removing HKEY_USERS\<Retrieved User SID>\Software\Microsoft\Windows\CurrentVersion\Uninstall  and related entries in “USERPROFILE\Local Settings\Apps\..” folder corrupts ClickOnce cache. Removing the whole Apps store fixes it, but user looses all ClickOnce apps in that case and related settings.
This problem was somewhat ISolvable problem :).

Friday, September 25, 2009

Problem rendering AJAX PopupExtender over ActiveX in IE 7, IE 8 and FireFox.

If you happen to have some sort of ActiveX control on your page in an IFrame, such as PDF viewer, then some of the AJAX controls which use PopupExtender will be obscured by the ActiveX or any other browser plug-in which rendering happens out of the main page context.

For example menus, and CalendarExtender suffer this problem, since they inherit PopupBehavior.

What happens is Ajax PopupExtender behavior explicitly checks for browser version, in particular IE 6. In IE 6 there was a known problem with z-index so you had to use IFrames to render some popup controls, so PopupBehavior checks whether browser is IE 6 and creates an IFrame around control, otherwise no IFrame created, hence the problem in newer IEs and FireFox with browser plug-ins.

To fix this problem, first download source code for AJAX Control Toolkit from CodePlex. And find PopupExtender folder in VS project. Then find this function:

addBackgroundIFrame : function()

Remember to duplicate your effort for debug and release versions of code, since there are two separate files.

Now lets take a look at the original code:

addBackgroundIFrame : function() {
/// <summary>
/// Add an empty IFRAME behind the popup (for IE6 only) so that SELECT, etc., won't
/// show through the popup.
/// </summary>

// Get the child frame
var element = this.get_element();
if ((Sys.Browser.agent === Sys.Browser.InternetExplorer) && (Sys.Browser.version < 7)) {
var childFrame = element._hideWindowedElementsIFrame;

// Create the child frame if it wasn't found
if (!childFrame) {
childFrame = document.createElement("iframe");
childFrame.src = "javascript:'<html></html>';";
childFrame.style.position = "absolute";
childFrame.style.display = "none";
childFrame.scrolling = "no";
childFrame.frameBorder = "0";
childFrame.tabIndex = "-1";
childFrame.style.filter = "progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)";
element.parentNode.insertBefore(childFrame, element);
element._hideWindowedElementsIFrame = childFrame;
this._moveHandler = Function.createDelegate(this, this._onMove);
Sys.UI.DomEvent.addHandler(element, "move", this._moveHandler);
}

// Position the frame exactly behind the element
$common.setBounds(childFrame, $common.getBounds(element));

childFrame.style.left = element.style.left;
childFrame.style.top = element.style.top;

childFrame.style.display = element.style.display;

if (element.currentStyle && element.currentStyle.zIndex) {
childFrame.style.zIndex = element.currentStyle.zIndex;
} else if (element.style.zIndex) {
childFrame.style.zIndex = element.style.zIndex;
}
}
},

Notice the section above, which has IF statement.

Remove that IF statement completely. Recompile your AjaxControlToolKit project and reference this new dll in your ASP.NET project. Drum roll… it works! ActiveX does not render over your popup control anymore.

Keep in mind, if you have many IFrames on your page it would slower rendering, so try to keep number of IFrames to a minimum.

Another ISolvable problem :).

Wednesday, September 16, 2009

Restoring WPF window of another process

Recently I was working on a WPF single instance application, which hides it’s main window and shows system tray icon. My task was to restore main window whenever user tries to open another instance of the application. The good thing I have control over source code, which means I can do anything to the target app. However there are 3 bad things related to WPF

WPF shortfalls
  • no out of the box support for single instance applications (in WinForms it is just a flag in project properties tab)
  • it is hard to get a handle of main window of another process, if that window is hidden. Process.MainWindowHandle would return 0.
  • when restoring window of a different process WPF does not listen for window events and thus WPF thread does not start rendering, as a result you get black window with XP blue frame around it.
1st problem

First problem is easily ISolvable either using Process.GetProcessByName(yourAppName) when returns true and process id is different from current process, then most likely there is another instance of the app is running, unless name of your app for some reason, is the same as some other app running on the box. In that case you can use mutex to solve the problem. In fact using Mutex is a more robust approach. You can find implementations in here.

2nd Problem

Second problem turned out to be lengthy and requires use of SharedMemoryFile and a bunch of other APIs. You can see how it is solved in the same article. This article however does not address WPF issue, since it was written prior WPF release.

3d Problem

While easily solvable, it took me some time to figure it out.
When you have a code like this:

'' if mutex was not created that means other instance is running, 
'' so we need to restore window of other application.
If Not IsMutexCreated Then
Try
Dim mainWindowHanle As IntPtr = System.IntPtr.Zero

SyncLock GetType(FilesView)
mainWindowHanle = MemoryMappedFile.ReadHandle("Local\sharedMemoryFilesView")
End SyncLock

If mainWindowHanle <> IntPtr.Zero Then
Dim result As Boolean
result = ShowWindowAsync(mainWindowHanle, SW_SHOWDEFAULT)
result = SetForgroundWindow(mainWindowHanle)
result = UpdateWindow(mainWindowHanle)
SetFocus(mainWindowHanle)
End If
Catch ex As Exception

End Try
Application.Current.Shutdown()

Try
_mutex.ReleaseMutex()
Catch ex As Exception

End Try
End If

the ShowWindow or ShowWindowAsync function will indeed restore the window, only with a little problem. It is going to be black. As WPF rendering runs on a separate thread and apparently not listening for main window events :(. Notice that I am not using GC.KeepAlive and GC.Collect like in the original article, but I declared mutex as a class member of Application class, which is a main class in WPF applications. In my case reference to mutex object is kept until Application class is disposed, which is when application shuts down. So GC (Garbage Collector) will not reclaim memory occupied by object mutex because it would have active reference.

blackWindow

So WPF thread is silent when some other process calls window restore or window show. Well, here is a good thing I mentioned in the beginning. I have complete control of the source code. And it means I can include event listener in the application main message loop and from there restore WPF window.

Since I know I am calling ShowWindow and SetFocus I can concentrate on the events which are fired in those two cases. It is most likely would be GotFocus, Activated or IsVisibleChanged events for WPF window.
In there I call Show method, and … it works!



Private Sub FilesView_Activated(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Activated
Application.Current.MainWindow.Show()
End Sub

Private Sub FileView_GotFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.GotFocus
Application.Current.MainWindow.Show()
' or simply
' Me.Show() 'if you are already inside main window class
End Sub

' this should also work
Private Sub FilesView_IsVisibleChanged(ByVal sender As System.Object, ByVal e As System.Windows.DependencyPropertyChangedEventArgs) Handles MyBase.IsVisibleChanged
Me.Show()
End Sub

That’s it. A lot of sweat for a simple problem, but hey it is ISolvable :).

Monday, September 14, 2009

VB.NET short circuit IF statement and Nullable(of T)

Now this was an interesting bug to find. May be it is not a bug, but I can’t explain this behavior otherwise.

When working with databases and trying to cover impedance mismatch cases it is common to use the following logic:

...
Dim primaryUserId As Integer?
...
Using reader As IDataReader = dataAccess.ExecuteReader("usp_CostCenter_Get", params)
With reader
While .Read()
returnResult = True
costCenter = If(.IsDBNull(0), "", .GetString(0))
description = If(.IsDBNull(1), "", .GetString(1))
primaryUserId = If(.IsDBNull(2), Nothing, .GetInt32(2)) '' <<<<-- incorrect behavior,
'' if condition is true primaryUserId will get 0 instead of Nothing.
'' 0 - is default value for type Integer, but not for type Integer? or Nullable(of Integer)
'' Nothing - should be the correct value in this case.
End While
End With
End Using
...


In case when condition is true you would expect true part of IF statement to execute, while something else happens and primaryUserId receives default value for type Integer not for type Nullable(of Integer) or Integer? .

This is how to correct such behavior:

...
Using reader As IDataReader = dataAccess.ExecuteReader("usp_CostCenter_Get", params)
With reader
While .Read()
returnResult = True
costCenter = If(.IsDBNull(0), "", .GetString(0))
description = If(.IsDBNull(1), "", .GetString(1))

'' expanding IF statement
If .IsDBNull(2) Then
primaryUserId = Nothing
Else
primaryUserId = .GetInt32(2)
End If

End While
End With
End Using
...


Please let me know if you had experienced this before and agree or disagree with me.

Thank you!

Visual Studio 2008 XAML designer crashes

There are several reasons why Visual Studio can crash.
- http://code.msdn.microsoft.com/KB963035
- http://code.msdn.microsoft.com/KB963676

and some other which I don’t remember now. But I am not going to talk about the above issues, rather about some other case when Visual Studio could crash.

Here is what happens.
Visual Studio Designer could create instances of some of the controls if for example you placed a child control on a form the base class for that child would be instantiated, similar if you have referenced external assemblies which have controls that are on a form, those controls could be instantiated, it depends on a control logic.

If you are creating a control you have to check for Design time compilation versus run-time. See my previous blog on how to handle it in Win Forms and there is plenty of topics on the web how to handle a similar issue for WPF.

Now if control resides in an assembly which is located on a shared or networking folder, then logic of that control would be executed in a different security context, and AccessDenied exception could be thrown. In my case Visual Studio was not handling this exception properly and was crashing. DWatson was executing and collecting crash data but that did help.

What should happen is VS IDE should handle such exception and cancel rendering with a proper message and type of the exception.

I solved it by copying all referenced assemblies into local project folder, then recompiling project and restarting Visual Studio IDE.


I usually copy assemblies for real projects. I ran into this problem by trying to create real quick prototype and was lazy to transfer dlls locally.

Try and let me know.
This was another ISolvable problem. I am sorry for being out of touch, but I will try to get back and post since I have a lot of things to share.