Freitag, 27. April 2012

Silverlight DataBinding und NULL

I recently noticed that, if you have an ObservableCollection<Something> in Silverlight bound to an ItemsControl with an explicit DataTemplate attached to it, that if you insert a NULL reference in to the collection you get the following error message:


Zeile: 1587
Fehler: Unhandled Error in Silverlight Application
Code: 4004   
Category: ManagedRuntimeError      
Message: System.Collections.Generic.KeyNotFoundException: Der angegebene Schlüssel war nicht im Wörterbuch angegeben.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at System.Windows.ResourceManagerWrapper.GetResourceForUri(Uri xamlUri, Type componentType)
    



While this seems cryptic in the first place, it still makes perfectly sense: for the type of object you add there is no something in the resource dictionaries to display it - hence the error.

The ugly thing is, that the error causes Silverlight to crash completely. A registered ApplicationUnhandledException handler just setting e.Handled = true does not seem to help either. So if you add something to a bound collection always check for null.

Maybe this saves some of you some time...

Cheers,
Tobias.

Samstag, 31. März 2012

Team Build Anpassungen

Besser spät als nie - hier die Demos aus meinem Vortrag zum Thema TFS Team Build Anpassungen, für alle die selber Anpassungen planen.

Bei Fragen fragen - ansonsten happy builiding :-)

Build Workflow Demos

Montag, 30. Januar 2012

Closures in C#

Closures kommen eigentlich auch den funktionalen Programmiersprachen. Einige Konzepte solcher Sprachen haben in C# einzug gehalten, und durch Lambda-Expressions zum Beispiel auch die Closures.

Hier ein kleines Beispiel:

public void DoSomething(int i)
{
  var x = 2 * i;
}

private Action GetAction(int initial)
{
  var i = 2 * initial;
  return () => DoSomething(i);
}

public void CallAction()
{
  Action a = GetAction(5);
  a();
}

Die Variable i in GetAction ist lokal und wird im Lambda-Ausdruck () => DoSomething(i) festgehalten. Beim Aufruf der Action in CallAction ist i eigentlich schon gar nicht mehr im Scope. Wegen des Closures kann der Delegat trotzdem darauf zugreifen.

Soweit so gut. Jetzt ein kleines Beispiel wo mich dieses Feature ein kleine Debugging-Session gekostet hat. Dazu ein vereinfachtes Beispiel:

private List<Action> actions = new List<Action>();

private void CreateActions()
{
  for (int i = 0; i < 10; i++)
  {
    var param = i * 2;
    AddAction(() => DoSomething(param));
  }
}

private void AddAction(Action a)
{
  if (!actions.Contains(a))
    actions.Add(a);
}

public void DoSomething(object i)
{
  var x = 2 * (int)i;
}

Jetzt werden die Actions zunächst in einer Liste gespeichert, aber nur falls die gleiche Action nicht schon enthalten ist.

Die Preisfrage lautet: wie viele Einträge enthält die Liste actions nach der Ausführung von CreateActions?

Die richtige Antwort ist: 10. Denn obwohl der Ausdruck () => DoSomething(param) immer gleich aussieht, ist es durch den Closure jedesmal eine andere Action, weshalb die Bedingung !actions.Contains(a) niemals greift.

Bei folgender Varianten ist die Menge der Varianten in der Liste gleich 1:

private void CreateActions2()
{
  for (int i = 0; i < 10; i++)
  {
    AddAction(() => DoSomething(1));
  }
}

Soweit klar, hier wird ja kein Element aus dem externen Scope eingeschlossen.
Aber auch diese Variante liefert einen Eintrag in der Liste actions:

private object context = 1;
private void CreateActions3()
{
  for (int i = 0; i < 10; i++)
  {
    context = 2 * i;
    AddAction(() => DoSomething(context));
  }
}

Hier ist die Variable context ein Referenztyp und das Closure schlíeßt nur die Addresse der Variablen mit ein.
Wenn man es mal so einfach hinschreibt, eigentlich logisch :-)

Donnerstag, 5. Januar 2012

A BranchCreatedEvent and TFS Extensibility

There are many extension and integration points in TFS. A very fine thing is the event system. We can hook into this either by defining a web service that gets called by the TFS - this works fine with WCF. Or we define a server side plugin for the event we are interested in, by implementing a class that implements the ISubscriber interface and deploying this into TFS.

Most documentation found on the web is written for TFS 2010 but as of now everything is running well with my TFS vNetxt installation.

The BranchCreatedEvent
There are a lot of useful events, with TFS vNext the list still got longer compared to version 2010. I wanted to do something, everytime a branch is created, so i was looking for a BranchCreatedEvent. But surprise: there is no such event, neither in TFS 2010 nor in TFS vNext. A suggested solution was to create a service that polls for new branches. I am not happy with this approach, but there seems to be no other way. So I started thinking about where to put my polling service.
One opportunity was to create a long running WCF service. I discarded that, because I was not sure wehter the service would restart once the App Pool was recycled. Another option was to write a custom Windows Service. Regarding this solution I was concerned that I would end up with a new services for each new requirement. So I thougth to implement a plugin based service to have on spot to add new features.

A plugin  based Task Scheduler for TFS  
So I wanted to have a task scheduler that could be extented thorugh plugins. And surprise again, there is already such a thing in TFS, called the TFS Job Agent - the thing that is also responsible for initiating the event processing. So all I had to was to find out how to implement a plugin for this agent.

Implementing a custom TFS Job Agent Job
Information about how to accomplish this was a little harder to find. This one put me on track, and other nice information can be found here.

Here is how the story goes:

Create a new class library solution and add the follwoing references:
  • Microsoft.TeamFoundation.Client [GAC]
  • Microsoft.TeamFoundation.Common [GAC]
  • Microsoft.TeamFoundation.Framework.Server [C:\Program Files\Microsoft Team Foundation Server Dev11\Application Tier\TFSJobAgent\]
Add a class implementing the ITeamFoundationJobExtension interface:

    public class MyFirstJob : ITeamFoundationJobExtension 
    {
        public TeamFoundationJobExecutionResult Run(TeamFoundationRequestContext requestContext, TeamFoundationJobDefinition jobDefinition, DateTime queueTime, out string resultMessage)
        {
            resultMessage = "Successfuly created my first job";
            return TeamFoundationJobExecutionResult.Succeeded;
        }
    }

The following code is needed to register the job and get it executed every 30 seconds:

var tfsConfigServerUri = new Uri(String.Format("http://localhost:8080/tfs"));
var tfsConfigServer = TfsConfigurationServerFactory.GetConfigurationServer(tfsConfigServerUri);
var service = tfsConfigServer.GetService<ITeamFoundationJobService>();

var definition = new TeamFoundationJobDefinition(
                    new Guid("E5B15F37-1B19-4014-B354-B6CA3DA908E7"),
                    "My First Job",
                    "Lab.TFSJob.FirstTry.MyFirstJob",
                    null,
                    TeamFoundationJobEnabledState.Enabled);

var schedule = new TeamFoundationJobSchedule(new DateTime(2012, 1, 5, 9, 0, 0), 30);
definition.Schedule.Add(schedule);
                
service.UpdateJob(definition);

To queue the job initially you can add the following line:

var Result = service.QueueJobNow(definition, false);

The dll must be deployed to the %ProgramFiles%\Microsoft Team Foundation Server Dev11\Application Tier\TFSJobAgent\plugins\ folder. After this the job agent service must be restarted once, otherwise the assembly will not be loaded. You can debug your job by attaching to the TFSJobAgent.exe process on the TFS machine.

Now only some logic to check wether there are new branches between two polls and you are done!

Enjoy!

Donnerstag, 27. Oktober 2011

The Silverlight ListBox and the space bar

I have, as I think, a quite common scenario: I have a ListBox control containing items that have a checkbox. I want to enable the user to remove items using the delete key and to toggle the checkbox using the spacebar.

So I set up a little MVVM magic to bind the keydown event of the listbox to my view model. Everything is working fine for the delete key. But when it comes to space, I never reached my command handler. It could not be an issue with the command binding, because it worked for delete.

So I searched around and found out that I was not the only one having recognized the issue. The post I found was from 2009 and written about Silverlight 2. I am wondering why this behaviour has not been changed because it seems hardly reasonable to me.

The technical reason for this behavior is, that within the ListBox control in the OnKeyDown method, if the pressed key is space, the Handled flag of the event parameters is set to true, so the space bar press will not propagate upwards the visual tree.

The solution is easy: inherit your custom ListBox control, overwrite the OnKeyDown method, handle the space press and set Handled to false:


protected override void OnKeyDown(KeyEventArgs e)
{
     switch (e.Key)
     { 
           case Key.Space:
                 e.Handled = false;
                 break;

           default:
                 base.OnKeyDown(e);
                 break;
     }
}

Lucky you, if you already have your custom control project in place...