Sunday, April 26, 2009

ListView.GetItemAt() in WPF Application

At present (.NET 3.5 SP 1) a GetItemAt() method doesn't exist for the ListView class that is part of WPF. At least I couldn't find it. I always found it handy in Forms based development for instance when handling mouse events. Please note that the WPF class ListView is in System.Windows.Controls while the forms based class of the same name is in System.Windows.Forms. Don't confuse the two! In this post I'm referring to the WPF class ListView. I wanted a similar behavior to the method of the same name in the forms based library. As a result I want the client code to look like this:
      private void _replacementsListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
         var mousePosition = e.GetPosition(_replacementsListView);
         var item = _replacementsListView.GetItemAt(mousePosition);
         if (item != null
            && item.Content != null) {
            EditTokenReplacement((TokenReplacement) item.Content);
         }
      }
Since the WPF class ListView doesn't come with a built-in method I decided to implement an extension method as follows:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Foo.Gui {
   internal static class Extensions {
      public static ListViewItem GetItemAt(this ListView listView, Point clientRelativePosition) {
         var hitTestResult = VisualTreeHelper.HitTest(listView, clientRelativePosition);
         var selectedItem = hitTestResult.VisualHit;
         while (selectedItem != null) {
            if (selectedItem is ListViewItem) {
               break;
            }
            selectedItem = VisualTreeHelper.GetParent(selectedItem);
         }
         return selectedItem != null ? ((ListViewItem) selectedItem) : null;
      }
   }
}
Please note that the position is passed into the method as an object of class Point. This means that the coordinates need to be relative to the list view object. In an event handler for a mouse button event, e.g. a double click, these can be calculated by using a method on MouseButtonEventArgs object that is passed into the mouse event handler. It's already shown in the first code snippet above but just to be on the safe side here are the relevant lines again:
      private void _replacementsListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
         var mousePosition = e.GetPosition(_replacementsListView); // gets position relative to ListView
         var item = _replacementsListView.GetItemAt(mousePosition);
         ...
      }

Saturday, April 25, 2009

Getting Parent Window for UserControl in WPF

Again a small item that may take you some time to search and find on the internet. Suppose you are working on a WPF based application and you have a UserControl that you want to host inside of a window. In addition you need the window within which the UserControl is hosted. To find out that parent window you might wonder whether you could use the property 'Parent'. Well that might work in some cases but in other cases it might not. For example the parent might be a Canvas and then it starts to become tricky. So here is a solution that should make it a bit easier. It uses the static method Window.GetWindow() and passes a reference to the UserControl instance as a parameter:
   public partial class FooControl : UserControl {
      public FooControl() {
         InitializeComponent();
      }

      private void _barButton_Click(object sender, RoutedEventArgs e) {
         var window = new MyWindow {
            WindowStartupLocation = WindowStartupLocation.CenterOwner,
            Owner = Window.GetWindow(this)
         };
         if( window.ShowDialog() == true) {
            // Ok button clicked, do something useful here...
         }
      }
   }

Sunday, April 19, 2009

OpenFileDialog in .NET on Vista

I'm working on a WPF based application and as quite some applications do, I need to display an OpenFileDialog as well. Trying to be compliant with what .NET 3.5 SP 1 is offering I tried Microsoft.Win32.OpenFileDialog first. I ran into two issues. The first issue I noted was that the dialog box positioned itself just about anywhere but not centered (or at least on the same screen) as the owner window, even if I set the owner window. I did some research and found that despite .NET having been around for years (I started using it in 2001) there is still no OpenFileDialog box available for .NET that inherits from Window (or Forms previously) and can be easily customized. And therefore - as an example - there is no WindowStartupLocation property. Oh, well ... Even with the latest WPF version running on Vista the old XP style dialog box is displayed: In addition even when I passed the owner as a parameter to ShowDialog() it wouldn't center relative to that owner window. Then I tried the previous System.Windows.Forms.OpenFileDialog(). Although it is not recommended to use that namespace within WPF I thought it would still be worth a try. And voila! Here is the result under Vista: Not only did it display the correct dialogbox under Vista but in addition it's location centered on it's owner is correct as well. To achieve this you have to fiddle a bit with the namespaces as there are a few classes that have the same name in both Microsoft.Win32 and in System.Windows.Forms. Since I wanted the client code to look as simple as possible I created two very small wrapper classes. The first wrapper class is basically a replication of the Microsoft.Win32.OpenFileDialog interface (I left out most of the code since it is simply forwarding):
using SWF = System.Windows.Forms;

namespace FooApp.Presentation {
   internal class OpenFileDialog {
      public OpenFileDialog() {
         _dialog = new SWF.OpenFileDialog();
      }

      public bool? ShowDialog(System.Windows.Window window) {
         return _dialog.ShowDialog(new WindowWrapper(window)) == SWF.DialogResult.OK;
      }

      public string DefaultExt {
         get {
            return _dialog.DefaultExt;
         }
         set {
            _dialog.DefaultExt = value;
         }
      }
      public string FileName {
         get {
            return _dialog.FileName;
         }
         set { _dialog.FileName = value; 
         }
      }
      public string Filter {
         get {
            return _dialog.Filter;
         }
         set {
            _dialog.Filter = value;
         }
      }
      public string Title {
         get {
            return _dialog.Title;
         }
         set {
            _dialog.Title = value;
         }
      }

      private readonly SWF.OpenFileDialog _dialog;
   }
}
The only really interesting piece is the ShowDialog() method which takes a WPF window. System.Windows.Forms.DialogBox.ShowDialog() requires a IWin32Window instead (for easy access to the window handle), so we need a second wrapper class that looks as follows:
using System;
using System.Windows;
using System.Windows.Interop;

namespace FooApp.Presentation {
   /// <summary>WindowWrapper is an IWin32Window wrapper around a WPF window.
   /// </summary>
   /// <remarks>Add System.Windows.Forms to the references of your project to
   /// use this class in a WPF application. You don't need this class in a
   /// Forms based application.</remarks>
   internal class WindowWrapper : System.Windows.Forms.IWin32Window {
      /// <summary>
      /// Construct a new wrapper taking a WPF window.
      /// </summary>
      /// <param name="window">The WPF window to wrap.</param>
      public WindowWrapper(Window window) {
         _hWnd = new WindowInteropHelper(window).Handle;
      }

      /// <summary>Gets the handle to the window represented by the implementer.
      /// </summary>
      /// <returns>A handle to the window represented by the implementer.
      /// </returns>
      public IntPtr Handle {
         get { return _hWnd; }
      }

      private readonly IntPtr _hWnd;
   }
}
With this machinery in place the client code in a WPF based application looks very familiar and simple:
var dialog = new OpenFileDialog {
   DefaultExt = ".dll",
   Filter = "Assemblies (*.dll; *.exe)|*.dll;*.exe",
   Title = "Select Assembly"
};
if( dialog.ShowDialog(this) == true ) {
   // File is selected, do something useful...
}
As a result you have it all, simple client code, the correct OpenFileDialog on Vista and the dialog centered properly on its owner. The only thing that I still can't get my head around: After 8 years of .NET we still don't have a class available that is based on Window (or Form) and can be customized. It seems as if this is a puzzle that the guys in Redmond haven't figured out yet...

Wednesday, April 15, 2009

Separator for Menu Item in XAML (WPF, Silverlight)

Trivial task and yet worth mentioning: You want a separator in your XAML based application (WPF or Silverlight)? Use System.Windows.Controls.Separator. In XAML write as follows:
<Menu ...
  <MenuItem ...
     <MenuItem ...
     <Separator />
     <MenuItem>
  </MenuItem>
</Menu>
In C# use:
using System.Windows.Controls;

Menu menu = new Menu();
...
menu.Items.Add(new Separator());

Tuesday, April 14, 2009

LinearGradientBrush with more than two GradientStop's in XAML

Using Expression Blend 2 you may believe that you can have only two GradientStop's for your LinearGradientBrush in particular if you are UI addicted as I am. The UI of Expression Blend 2 doesn't offer adding another GradientStop. But funny enough it supports them! So just go to the XAML code for, e.g. select "View XAML" from the context menu and just add another GradientStop tag to the XAML code for the GradientBrush. Once you do that you will notice that the Expression Blend 2 user interface will happily display 3 (or more) sliders as demonstrated in the screenshot. Here is an example:
<lineargradientbrush key="WindowBackgroundBrush"
  spreadmethod="Pad" endpoint="0.5,1" startpoint="0.5,0">
<gradientstop color="#FFFFDCDC" offset="0.56" />
<gradientstop color="#FFFF6F6F" offset="0.77" />
<gradientstop color="#FFFFDCDC" offset="1" />
</lineargradientbrush>

Monday, April 13, 2009

TextBox and other Controls with Transparent Background in XAML

Again a small item but something you may have searched for quite a while. In a XAML based user interface (WPF, Silverlight) if you want your control (e.g. TextBox) to NOT have a background, that is you want the background to be transparent you can do one of the following:
  1. Set its Background property to 'Transparent'
  2. In Expression Blend select the Background brush and set the alpha channel to zero
Sounds simple but it might take you longer than expected to find this information on the internet (unless I'm completely hopeless to enter the appropriate keywords). Most sources explain for the 100th time how to set the background color.

Sunday, April 12, 2009

"The file 'x' is not part of the project or its 'Build Action' property is not set to 'Resource'"

When working on a WPF based application and using the Image control you may encounter the following error message when trying to enter the source file name for the image:
The file 'x' is not part of the project or its 'Build Action' property is not set to 'Resource'
It's not quite clear to me what causes this and it's not quite clear why Microsoft didn't fix it in Service Pack 1 for Visual Studio 2008 but here is a solution that may work for you:
  1. Add the file to your solution.
  2. Set its 'Build Action' to 'Resource' (in my case the drop-down also offers 'Embedded Resource' but that's not what you want)
  3. Select the image control.
  4. Set its Source property to the image file name. It should show up in the drop-down list. Next it may display the error message mentioned above. Then rename the image file to a short filename. Try setting the Source property of the Image control again. It should be fine now.
In case you want to try editing the XAML code then here is an example of what you may want set the source property to:
<Image ... source="Foo.Gui;component/pictures/myLogo.jpg" ... />
The assumptions for this example are
  • Your project is named 'Foo.Gui'
  • That project contains a folder named pictures
  • Your picture is in that folder and also included in the project. The name of the picture is 'myLogo.jpg'

Saturday, April 04, 2009

"Configuration system failed to initialize"

If you see a ConfigurationErrorsException along with the information "Configuration system failed to initialize" in your QuickWatch window then in all likelihood your app.config (or web.config) file is not correct. In my case I simply forgot to surround the membership provider section with <system.web></system.web>. Once I added those it worked like a breeze. Also, in case your custom provider cannot be found, make sure you have added the proper assembly name to the 'type' attribute for the <add> element of your provider in the <providers> section. And yes, you can test custom providers without having to deploy to or run in a web server. Just ensure your app.config file contains the bare minimum by copying some content from web.config and you should be fine. For my scenario it was sufficient to copy the config section for NHibernate, the hibernate configuration, and the declaration for my custom membership provider.

Friday, April 03, 2009

Membership Provider Implementation using NHibernate

ASP.NET allows selectively replacing provider implementations with your own custom implementation, e.g. when you want to store membership information in a database other than aspnetdb.mdf. There are more details provided on MSDN. Microsoft provides one such example implementation for ODBC in C# here. For VB.NET a sample membership provider implementation can be found here. While the provider concept is essentially nothing more than Microsoft's flavor of service trays, it is not a surprise that by default Microsoft has a preference for their own products, in particular Microsoft SQL Server. And as long as you don't have a good reason to get into the details of a custom implementation it probably is a good choice to go with what comes out of the box. However, I (and my customers) would like a little more flexibility. Since I'm also experimenting with Fluent NHibernate I am attempting to implement a custom membership provider for ASP.NET based on NHibernate. The idea is that I could use in-memory databases for testing and SQL Server or PostgreSQL for production without having to change a single line of code. All I would need is changing four lines in web.config. So goes the theory. Let's see whether practice proves it right. So far the code looks much simpler than the ODBC sample implementation and yet easier to read and understand. I'll keep you posted.