James's profileJames McCaffreyBlogLists Tools Help

Blog


    September 26

    Formatting the Background Color of a Selected WPF ListBox Item

    Yesterday I was looking at WPF ListBox controls and ran into an interesting low-level problem. When a user clicks on an item in a ListBox control to select the item, the background color of the selected item is set to a system-defined color -- a medium blue by default. This medium blue color looks great on machines running Windows Vista but on earlier versions of Windows (Server 2003 in particular) the blue is darker and makes the text of the selected item very difficult to read. So, how hard can it be to modify the WPF XAML description to change the background color of a selected ListBoxItem? See the image below. Well, it was a lot harder than I thought it would be to track down the documentation on exactly how to do this. I discovered fairly quickly that it isn't too hard to modify the background color of selected items in a ListBox by directly modifying the ListBox XAML definition to include a Resources tag:
     
    <ListBox Name="employeeListBox" Grid.Column="0" Grid.Row="1"
      ItemsSource="{Binding Source={StaticResource SkillDataSource},
      XPath=Employee}"
      ItemTemplate="{StaticResource nameItemTemplate}">
      <ListBox.Resources>
       <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
        Color="LightYellow" />
      </ListBox.Resources>
    </ListBox>
     
    However, the example WPF application I was looking at had more than one ListBox, so I wanted to create a Style tag in the main App.xaml file and apply the style to the ListBox controls defined elsewhere. It took me much longer to discover how to do this. The obvious approach simply does not work:
     
    <!-- wrong -->
    <Style x:Key="listBoxItemStyle" TargetType="{x:Type ListBoxItem}">
      <Setter Property="Background" Value="LightYellow" />
    </Style>
     
    Here's the correct way to define a style:
     
    <!-- Correct way -->
    <Style x:Key="listBoxItemStyle" TargetType="{x:Type ListBoxItem}">
      <Style.Resources>
       <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
        Color="LightYellow"/>
      </Style.Resources>
    </Style>
     
    And then background color of selected items in any ListBox control can be modified using the ItemContainerStyle attribute to point to the Style template defined in the App.xaml file:
     
    <ListBox Name="employeeListBox" Grid.Column="0" Grid.Row="1"
      ItemsSource="{Binding Source={StaticResource SkillDataSource},
      XPath=Employee}"
      ItemTemplate="{StaticResource nameItemTemplate}"
      ItemContainerStyle="{StaticResource listBoxItemStyle}"> 
    </ListBox>
     
    WPF is still a new technology. Common tasks like this -- modifying the background color of a selected ListBoxItem -- take a bit longer to figure out than you might expect.
     
    ChangeColorSelectedWPFListBoxItem
     
    September 21

    WPF Application UI Test Automation - Getting the Main Window

    I've been looking at testing WPF (Windows Presentation Foundation) applications lately. It took me a while to warm up to WPF apps but now I'm a big fan. UI automation for WPF apps is a new ball game. By far the best approach is to use the Microsoft UI Automation (MUIA) library which, like WPF, is part of the .NET 3.0 Framework. The very first step to automate a WPF application is to get a reference to the main app window (we don't call it a WinForm any more). There are several approaches possible. One technique is to use the AutmationElement.FindFirst() mehod of the MUIA library. FindFirst accepts parameters which identify the target element to get. Interestingly, when I used Visual Studio 2008 to create a WPF application, by default the application main window does not get a Name attribute in the XAML file which dfines the main window. So, in a default scenario, when the WPF app is built using Visual Studio, no AutomationId propertty is generated for the main window of the app -- and so you can use FindFirst() in a natural way. Well the obvious solution is to just manually add a Name property to the main window. Then you can get a reference to the app (named StatCalc in my example in the screenshot below) like:
     
    // launch app here
    // get reference to Desktop element
    Console.WriteLine("\nLooking for StatCalc main window. . . ");
    AutomationElement aeStatCalc =
     aeDesktop.FindFirst(TreeScope.Children,
       new PropertyCondition(AutomationElement.AutomationIdProperty,
         "StatCalc"));
    if (aeStatCalc == null)
      throw new Exception("Failed to find StatCalc main window");
     
    However, this approach has three significant problems. First, it relies on you having control of the app under test so you can manually add a Name attribute so that an AutomationId property is generated. You may not have such control. Second, this approach assumes there is only a single instance of the app under test running. If there is more than one instance you might get a reference to the wrong instance. Third, this approach doesn't take any timing issues into account. A complex app may take time to launch and your automation will miss it. For these reasons I conclude that the approach above is not feasible. I have a neat solution and I'm writing up an article for MSDN Magazine.
     
    UIAutomationForWPF-GetAppWindow
    September 15

    The Simplest ASP.NET Web Application

    Visual Studio is a great IDE but it can be a bit psycho at times. Suppose you want to create the simplest possible ASP.NET Web application. With most .NET project types -- a Console Application, a WinForm application, and a Class Library in particular -- you can launch an instance of notepad, type in some C# code, and compile from a command line using the csc.exe tool. However, even the simplest ASP.NET Web application is relatively complicated. The crux of the issue is that with Web apps you've got to place your C# file(s) on a Web server of some sort. The bottom line is that even though you can create and call a simple ASP.NET Web app without using Visual Studio, in practice you really have to use VS. Here's how to do the simplest possible Web app. First launch Visual Studio 2008. Now click on File | New | Web Site. This is somewhat misleading terminology because we are about to create a Web application but not a Web site to hold the app. On the New Web Site dialog, select .NET Framework 3.5 in the upper right-hand corner. Next select the Empty Web Site (again the term is somewhat misleading) template. Now in the Location field, you have your choice of File System or HTTP. If you choose File System, after you write your Web application Visual Studio will use a special lightweight Development Web Server. If you choose HTTP, Visual Studio will actually create a fully functional, heavyweight, Internet Information Services (IIS) based Web site on your development machine. Because our goal is simplicity, choose File System. Select C# as the language (VB.NET is a declining language), and then type something like C:\SomeWhere\DescriptiveName in the second part of the Location field. Click OK.
    Now in Solution Explorer, right-click on your "DescriptiveName" project name, and select Add | New Item. From the template list, select Web Form (with name Default.aspx). Be sure to uncheck the "Place code in separate file" entry -- Visual Studio by default uses a horrible technique of placing code into two separate files, which is not what we want for a simple approach. Click the Add button. In the Visual Studio editor delete the template code in Default.aspx and replace with this:
     
    <%@ Page Language="C#" %>
    <script runat="server">
      static int ct = 0;
      protected void Button1_Click(object sender, EventArgs e)
      {
        ++ct;
        TextBox1.Text = "Hi!  page load # " + ct;
      }
    </script>
    <html>
    <head runat="server">
        <title>Simple Page</title>
    </head>
    <body>
      <h3>Simple ASP.NET Demo</h3>
        <form id="Form1" runat="server">
         <asp:Button id="Button1" runat="server" Text="Click Me" onclick="Button1_Click"/>
         <p></p>
         <asp:TextBox ID="TextBox1" runat="server" ></asp:TextBox>
        </form>
    </body>
    </html>
     
    Now you can exercise the Web application by hitting the F5 key. You will be asked if you want to create a Web.config file to allow debugging -- it doesn't matter what you answer here. Now when the Web app loads into Internet Explorer, you'll see the URL in the address bar as http://localhost:12345/DescriptiveName/Default.aspx where the port number 12345 has been randomly selected. This is because you are using the special built-in Development Server instead of IIS, and IIS because uses the default port 80, the Development server has to use a different port number. It is possible to specify a hard-coded port number to use. In Solution Explorer, select the DescriptiveName project then do a View | Properties Window. Click into the True value next to the "Use dynamic ports" field, which will enable the Port number field where you can type in some port number. Confusingly, this information gets saved into the Visual Studio .sln file. The location of the .sln file can vary depending upon several factors. A likely location is at C:\Documents and Settings\userXXX\My Documents\Visual Studio 2008\Projects\DescriptiveName. The .sln file has configuration settings including VWDDynamicPort = "false" and VWDPort = 12345. Interestingly, if you simply edit the .sln file by hand, Visual Studio will not read the update until after you close VS, re-launch, and reload the Web application.
     
    September 05

    A Simple WinForm Application using csc.exe and Notepad

    The thing I like least about Visual Studio is that the tool creates unnecessarily complex templates for simple programs. For example, suppose you want a very simple WinForm "Hello" application such as the one in the screenshot below. The WinForm just has a Button control and a TextBox control. When the user clicks on the button, a message is displayed in the text box. If you launch Visual Studio then do a File | New | Project, and select a C# Windows Form Application, you get eight files (AssemblyInfo.cs, Resource.resx, Resource.Designer.cs, Settings.Designer.cs, Form1.cs, Form1.Designer.cs, Form1.resx, and Program.cs). Furthermore, the internal structure of even a simple program is overly complex, with partial classes, and semi-hidden Windows Form Designer generated code. In many testing-related situations I just want a simple, single Hello.cs file but Visual Studio does not directly allow this. So, the alternative I usually take is to type the program into notepad:
     
    using System;
    using System.Windows.Forms;
    public class Form1: System.Windows.Forms.Form
    {
      private System.Windows.Forms.Button button1;
      private System.Windows.Forms.TextBox textBox1;
      public Form1() // constructor
      {
        InitializeComponent();
      }
      private void button1_Click(object sender, EventArgs e)
      {
        this.textBox1.Text = "Hello there!";
      }
      static void Main()
      {
        Application.Run(new Form1()); // Run our form
      }
      private void InitializeComponent() // helper method to keep constructor clean
      {
        this.SuspendLayout(); // for better performance
        // button1
        this.button1 = new System.Windows.Forms.Button();
        this.button1.Location = new System.Drawing.Point(28, 26);
        this.button1.Name = "button1";
        this.button1.Size = new System.Drawing.Size(75, 23);
        this.button1.TabIndex = 0;
        this.button1.Text = "button1";
        this.button1.UseVisualStyleBackColor = true;
        this.button1.Click += new System.EventHandler(this.button1_Click);
        // textBox1
        this.textBox1 = new System.Windows.Forms.TextBox();
        this.textBox1.Location = new System.Drawing.Point(28, 55);
        this.textBox1.Name = "textBox1";
        this.textBox1.Size = new System.Drawing.Size(100, 20);
        this.textBox1.TabIndex = 1;
        // Form1
        this.AutoScaleDimensions = new System.Drawing.SizeF(6.0F, 13.0F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(284, 264);
        this.Name = "Form1";
        this.Text = "Form1";
        this.Controls.Add(this.button1);
        this.Controls.Add(this.textBox1);
        this.ResumeLayout(false);
      }
    } // class Form1
     
    After saving this single file as Hello.cs in any convenient directory, I compile the application with:
     
    C:\SomeWhere>csc.exe /target:winexe Hello.cs
     
    which generates a Hello.exe file. It is possible to create custom Project templates in Visual Studio, but I argue that VS should ship with a "Simple Windows Form Application" template.
     
    SimpleWinFormWithcscAndNotepad