Sunday, October 18, 2009

Working with the 1.5 code base : part 5

Away with the dreaded WPF, and back to good old CCNet coding ;-)
This part will cover another updated part of the code : communication. In 1.5 Craig introduced messaging, Changing to Messages. As always Craigs blog is very informative in what and why he changes. I'll try to give a more hands-on example, so others can see more details and some code.
The problem : In CCNet 1.4.3 I exposed the breakers of a build to cctray and the dashboard. Cradiator also uses this information. Now in CCNet 1.5 Craig, yes he again, exposed also the breaking tasks, which is good. But this introduced a problem : the xml feed Cradiator uses, only exposes the last message! So this means that we show the breaker, OR the breaking tasks. (also the dashboard has this problem for the moment)
The solution is simple : expose all messages :-) This is covered in Jira Issue 1718
Anyway, this kind of information is stored in the messages arraylist on the project being integrated. When a build is ok, we clear this list, when a build is bad, we look at the modifications, and extract the user names. The same goes for the tasks that failed. All this information is just a message in this arraylist. This also is a problem on its own. A message is just a text : John Wayne Broke the build. You can not see what kind of information it is, only by parsing the text :-(

Fixing the issue :
Step 1 : add a type for each message
Currently I have these 4 types in an enum
  • NotDefined
  • Breakers
  • Fixer
  • FailingTasks
This is just adding a property with the enum to the message class

/// <summary>
/// The type of message
/// </summary>
[XmlText]
public MessageKind Kind
{
get { return messageKind; }
set { messageKind = value; }
}
Step 2 : adjusting the communication
We need to update the MessageRequest to have this new property also

/// <summary>
/// The kind of message
/// </summary>
[XmlElement("kind")]
public Message.MessageKind Kind
{
get { return kind; }
set { kind = value; }
}
Everywhere where we go from a MessageRequest to a Message we need to pass this new property value. This is in 1 place : CruiseServer.cs in public virtual Response SendMessage(MessageRequest request)
The only thing left to do is specify the value of the type when we add a message to the list. For CCTray this is the option :Volunteer to fix build, in the integration itself, we must add the correct value to the breakers and the failing tasks.

Step 3 : expose the messagelist

This is merely adding a new xml element, which is a list, nothing special.
So in XmlReportAction.cs add this element :
xmlWriter.WriteStartElement("messages");

foreach (Message m in status.Messages)
{
xmlWriter.WriteStartElement("message");
xmlWriter.WriteAttributeString("text", m.Text);
xmlWriter.WriteAttributeString("kind", m.Kind.ToString());
xmlWriter.WriteEndElement();
}

xmlWriter.WriteEndElement();


Not so hard was it ?

Stay tuned for more in the future ...

Thursday, October 15, 2009

Wpf : Final encounter

I tried to enhance the display so that issues with attachments would have an icon (paperclip) next to it. To show and hide this image is easy, just use a converter. But getting the image to use an embedded resource was more difficult. For me this was the last drop.
I quit this WPF stuff for the moment, maybe I take it up again later, when the tools are more to todays standard, but from what I heard about VS2010, I doubt that it will be in the near future. Anyone who wants to take a look at the code, you can find the project at : Source Forge project
svn repository : https://jirawpf.svn.sourceforge.net/svnroot/jirawpf

maybe it needs some more comments, but you should get it to work.
If there are questions about the project itself, feel free to ask, I'll try to answer them.

Back to CCNet, that is a project I've neglected a bit, and needs some attention.

Friday, October 2, 2009

Wpf : style issues

Got the program almost like I want it. Encountered another big annoying WPF issue :
the order of the styles and datatemplates in a XAML file is of importance !
Take a look at the following example :
<DataTemplate x:Key="IssueDataTemplate" >
<ContentControl Style="{StaticResource IssueTemplate}"/>
</DataTemplate >

<Style x:Key="IssueTemplate" >
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal" >
<Label Content="{Binding Key}" Width="200"/>
<Label Content="{Binding Description}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You get a freaking runtime error : Unable to find style IssueTemplate ! ! !

To fix it, place the style first in the file, and next the datatemplate.

Questions :
1) Why does it throw a runtime error, and not a compile error, it is a static resource, meaning that it is known at design time, it is not loaded via an assembly at runtime (late binding technology).
2) Why an error in the first place? Back in 1990, it was of importance to place the procedures in correct order in the code, so the compiler could find them. But we're now in 2009! This is the way of working of 20 years ago!

When you code in any .Net language, Delphi, C, VB6, VB5, VB4 or even COBOL the place of a function in a class does not matter, hey even with partial classes, it does not even matter in which file they are! But WPF fails if the order is not ok.

I have a feeling that this issue will make the style files unorganized, and so making it errorprone :-(

sigh me wonder what is so fun and exciting about wpf, can someone enlighten me ? I do not grasp it.

Getting it to work

Found the courage again to code in this WPF world.
Reverted to use the ItemTemplate in the listbox, to get it to work. But now I have double XAML code : a datatemplate showing how a certain item is displayed, and a style showing how a certain item is displayed. The solution for this is to let the datatemplate use the style, so the layout is only defined once, like so :
<Style x:Key="IssueTemplate" >
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal" >
<Label Content="{Binding Key}" Width="200"/>
<Label Content="{Binding Description}" />
<Label Content="{Binding Votes}" />
<Label Content="{Binding Assignee}" />
<Label Content="{Binding Created}" />
<Label Content="{Binding Priority}" />
<Label Content="{Binding Reporter}" />
<Label Content="{Binding Status}" />
<Label Content="{Binding Votes}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<DataTemplate x:Key="IssueDataTemplate" >
<ContentControl Style="{StaticResource IssueTemplate}"/>
</DataTemplate >
The issue with the double click is not relevant anymore, I wanted to show a second window on double clicking an issue, but changed my mind. Now when you select an item in the listbox, the details of the selected issue are shown at the top of the screen. This is done by adding a ContentControl to the main window, binding its datacontext it to the same one as the listbox and setting the property IsSynchronizedWithCurrentItem to true. This works rather well. The code of the main window looks now like this :
<DockPanel>
<Expander VerticalContentAlignment="Top"
DockPanel.Dock="Left"
DataContext="{StaticResource CCNetProject}" >
<ContentControl Style="{StaticResource CCNetProjectTemplate}" />
</Expander>
<ContentControl DataContext="{StaticResource AllIssues}"
Style="{StaticResource IssueDetailTemplate}"
DockPanel.Dock="Top" />
<ListBox DockPanel.Dock="Bottom"
ItemsSource="{Binding Source={StaticResource AllIssues}}"
ItemTemplate="{StaticResource IssueDataTemplate}"
IsSynchronizedWithCurrentItem="True"
/>
</DockPanel>

As you can see, there is nothing in the main window about how a certain item is displayed, no coloring, borders, ... All this is done is in the styles. I'll see how far I get with this approach. I'm now busy with showing the details of a certain issue, when that is done the most important functionality of the program is done, making it possible to give the app a more polished look.
Stay tuned ...

Friday, September 11, 2009

Struggling with basic behaviour

Ran into some problems again :-(.
1) If I populate the listbox with lot's of items, let's say 200, it falls of the screen!
In winforms I would expect a scrollbar to appear, but not in my WPF app :-(. So I added a Scrollviewer around the listbox, but to no avail.
After looking around on the net, it appears that the stackpanel is at fault, my Listbox resides in a StackPanel, and this combination makes the items of the listbox to go beyond the window edge.
Solution : remove the stackpanel, this was not realy needed (yet) for my program, and when I do need a container control in the future, I'll use a grid

2) Standard a listbox does not support double-click
Surfs up : listbox double click. You have to manually add the event.

3) When I use the ItemContainerStyle in stead of the ItemTemplate, like said in the previous post, the listbox looses the ability to select items somehow. Go figure. If there is a soul out there, that can explain me the difference between ItemContainerStyle and ItemTemplate, please enlighten me.

Now these are the kind of things that drive me nuts when I am programming in WPF !
When coming from Windows Forms, I keep on stumbling on these basic stuff that is natively in windows forms, but not in WPF, I think I'll join the WPF Hate team, and stick to console and winforms. At least those work, no eye candy, but I can say it'll be finished in 2 days.

I mean it is easy to create Star Wars like stuff in WPF, but real working apps, that remains to be seen. Ok, there are a lot of showcases to be found on the Net, but I wonder how many devs and designers where put on those projects. Also read this page

laying off for a while, getting too angry ...

Thursday, September 10, 2009

Keeping the overview

The main window is getting into shape, but I do not like the Xaml code :-(. It's getting big, and I'm loosing the overview. Below is a small example of what I mean, the window just shows the CCNet project data, (name, description, ...) and a list of the issues (key, description, votes, ..)
<Window x:Class="JiraWpf.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lc="clr-namespace:JiraWpf"
Title="Window2" >
<Window.Resources>
<ObjectDataProvider x:Key="CCNetProject"
ObjectType="{x:Type lc:CCNetJiraDataProvider}"
MethodName="GetCCNetProject"
/>
<ObjectDataProvider x:Key="AllProjects"
ObjectType="{x:Type lc:CCNetJiraDataProvider}"
MethodName="GetProjects"
/>
<ObjectDataProvider x:Key="AllIssues"
ObjectType="{x:Type lc:CCNetJiraDataProvider}"
MethodName="GetIssuesFromFilter"
/>
<DataTemplate x:Key="IssueTemplate" >
<StackPanel Orientation="Horizontal" >
<Label Content="{Binding Key}" Width="200"/>
<Label Content="{Binding Description}" />
<Label Content="{Binding Votes}" />
<Label Content="{Binding Summary}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<DockPanel>
<Expander VerticalContentAlignment="Center" DockPanel.Dock="Left" DataContext="{StaticResource CCNetProject}" >
<StackPanel Orientation="Vertical" >
<Label Content="{Binding Lead}" />
<Label Content="{Binding Key}" />
<Label Content="{Binding Description}" />
<Label Content="{Binding Name}" />
</StackPanel>
</Expander>
<ListBox DockPanel.Dock="Top"
ItemsSource="{Binding Source={StaticResource AllIssues}}"
ItemTemplate="{StaticResource IssueTemplate}"
/>
</DockPanel>
</StackPanel>
</Window>
As you can see, that is getting big, and it shows almost nothing :-( Time to clean house!
First thing I do not like is the difference of showing data between the listbox and the expander. I prefer the one of the listbox, where you say that the items have the specified template, leaving the actual layout out of the overview.
Downside is that Expander does not have an ItemTemplate property, and creating that property and functionality on all kind of controls does not sound as an attractive solution. The good news is that does exist already, WPF has this functionality build in : you just have to use basic controls and styles !
Now that is what I like : basic controls. Never thought that I would ever say : I like something about WPF ;-)
So I created a style that just sets the Control.Template property, this allows to easily create a visualisation of a certain item, the way like the it is done with a DataTemplate. And this style is applied to a ContentControl residing in the expander. This is already better, but now I have a style and a datatemplate to visualise data, and personally I do not like 2 ways of doing the same stuff in 1 program. So I changed the DataTemplate also into a style. This means that I have to use the ItemContainerStyle property in stead of the ItemTemplate in the listbox.
That being done, the way of presentation is more consistent, but the file is still rather big. It would be nice if the resources where moved into a separate file, or set of files, and luckily this is also supported out of the box with the ResourceDictionary.MergedDictionaries. Resulting in the following for the window.xaml
<Window x:Class="JiraWpf.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lc="clr-namespace:JiraWpf"
Title="Window1" >
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/DataProvider.xaml" />
<ResourceDictionary Source="Resources/DataLayout.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<StackPanel>
<DockPanel>
<Expander VerticalContentAlignment="Center" DockPanel.Dock="Left" DataContext="{StaticResource CCNetProject}" >
<ContentControl Style="{StaticResource CCNetProjectTemplate}" />
</Expander>

<ListBox DockPanel.Dock="Top"
ItemsSource="{Binding Source={StaticResource AllIssues}}"
ItemContainerStyle="{StaticResource IssueTemplate}"
/>
</DockPanel>
</StackPanel>
</Window>

As you can see, a lot shorter and more easily to follow. Below is the DataLayout.xaml file, I did not post the DataProvider.xaml file, because it is just a copy and paste of all the ObjectDataProviders into an empty ResourceDictionary file.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="CCNetProjectTemplate" >
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Vertical" >
<Label Content="{Binding Lead}" />
<Label Content="{Binding Key}" />
<Label Content="{Binding Description}" />
<Label Content="{Binding Name}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Style x:Key="IssueTemplate" >
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal" >
<Label Content="{Binding Key}" Width="200"/>
<Label Content="{Binding Description}" />
<Label Content="{Binding Votes}" />
<Label Content="{Binding Summary}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>


Now this looks like it is more manageable. Stay tuned ...

Monday, September 7, 2009

Binding data with INotifyPropertyChanged

Wpf can only bind to properties, and in order to let it automatically refresh the UI when a bound data item changes value, it must implement the INotifyPropertyChanged property.
This means that the following code does not suffice :
using System;
namespace JiraWpf.DataObjects
{
public class Person
{
public Person()
{
}
public string Name { get; set; }
}
}

So let's implement the interface, this leads us to the following :
using System;
using System.ComponentModel;

namespace JiraWpf.DataObjects
{
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

public Person()
{ }

private string name;
public string Name
{
get { return name; }
set
{
if (value != this.name)
{
this.name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
}

private void OnPropertyChanged(PropertyChangedEventArgs args)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, args);
}
}
}

Not a big change, but if you have a lot of properties, this is very bad. We came from a 1 line property implementation to a 13 line !! The lines for the event and raising it are common for all properties, so they are not counted. Every property compares its value with the previous value, and when those 2 are different it raise the change-event. This is functionality that is the same for all properties so it belongs somewhere else. It would be handy if an attribute NotifyOnChange could be set on a property, but this does not exist (as far as I know). Meaning this comparison must be done in a self made base class : NotifyingObject, leading us to the following :
using System;
using System.ComponentModel;
using System.Collections.Generic;

namespace JiraWpf.DataObjects
{
public class NotifyingObject : INotifyPropertyChanged
{
private Dictionary<string, IComparable> props;

public event PropertyChangedEventHandler PropertyChanged;

public NotifyingObject()
{
props = new Dictionary<string, IComparable>();
}

public void SetValue<T>(string name, T value) where T : IComparable
{
if (!props.ContainsKey(name))
{
props.Add(name, value);
}
else
{
if (props[name].CompareTo(value) != 0)
{
props[name] = value;
OnPropertyChanged(new PropertyChangedEventArgs(name));
}
}
}

public T GetValue<T>(string name) where T : IComparable
{
return (T)props[name];
}

protected void OnPropertyChanged(PropertyChangedEventArgs args)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, args);
}
}
}
using System;
using System.ComponentModel;

namespace JiraWpf.DataObjects
{
public class Person : NotifyingObject
{
public Person()
{ }

public string Name
{
get { return base.GetValue<string>("Name"); }
set { base.SetValue("Name", value); }
}
}
}

Ok, this is already much better, property definitions are back to just 5 lines, but at least it is readable again. The NotifyingObject has a list of the property names and their values, so on a get, we look up the value in the list. On a set we compare with the value in the list, and when the 2 are different, we raise the event.
Now what still bothers me on this is that we type the property name 3 times in the Person class! Once in the property name itself, secondly in the get and another time in the set. This is asking for trouble on refactoring (renaming the property). No tool will change those names in the get and set, they are hardcoded string values ! This leads us to the following implementation of the 2 classes :
using System;
using System.ComponentModel;
using System.Collections.Generic;

namespace JiraWpf.DataObjects
{
public class NotifyingObject : INotifyPropertyChanged
{
private Dictionary<string, IComparable> props;

public event PropertyChangedEventHandler PropertyChanged;

public NotifyingObject()
{
props = new Dictionary<string, IComparable>();
}

public void SetValue<T>(T value) where T : IComparable
{
string name = GetCallerPropertyName();

if (!props.ContainsKey(name))
{
props.Add(name, value);
}
else
{
if (props[name].CompareTo(value) != 0)
{
props[name] = value;
OnPropertyChanged(new PropertyChangedEventArgs(name));
}
}
}

public T GetValue<T>() where T : IComparable
{
string name = GetCallerPropertyName();
return (T)props[name];
}

protected void OnPropertyChanged(PropertyChangedEventArgs args)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, args);
}

private string GetCallerPropertyName()
{
System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace();
System.Diagnostics.StackFrame currentFrame = stack.GetFrame(2);

//strip of the set_ and get_ prefixes of the generated property names, to get the ones you typed
string propname = currentFrame.GetMethod().Name.Substring(4);

return propname;
}
}
}
using System;
using System.ComponentModel;

namespace JiraWpf.DataObjects
{
public class Person : NotifyingObject
{
public Person()
{ }

public string Name
{
get { return base.GetValue<string>(); }
set { base.SetValue(value); }
}
}
}

We let the NotifyingObject look up the property name itself, preventing us from ever typing in a wrong name. This is now safe for refactoring.
Another benefit of the NotifyingObject is that you could foresee a way to postpone the raise event till all properties are set, a BeginUpdate and EndUpdate function could foresee that. Implementing extra logging is now also much easier. Anyway, with this baseclass ready, I can start working on the next part.
Stay tuned ....