WebClient not sending credentials? Here’s why!

TL;DR version here.
This post applies to more than just GitHub, read the rest to see the behavior!

I was playing with the GitHub API (more specifically generating a new OAuth token).

So what you need to do, as per the documentation, is to post a certain JSON string to https://api.github.com/authorizations, with Basic Authentication. I’m going to use the WebClient class for this.

This is the JSON string that you need to post:

{
  "scopes": [
    "repo"
  ],
  "note": "API test"
}

Now this is the code I used:

var content = new 
			{
				scopes = new[] { "repo" },
				note = "API test",
			};

var webClient = new WebClient
	                {
				Credentials = new NetworkCredential("*****", "*****"),
	                };

// JsonConvert is from NewtonSoft.Json, very handy!
string serializedObject = JsonConvert.SerializeObject(content);

string reply = webClient.UploadString(new Uri("https://api.github.com/authorizations"), "POST", serializedObject);

dynamic deserializedReply = JsonConvert.DeserializeObject(reply);

Console.WriteLine(deserializedReply.token);
Console.ReadLine();

However, when using this piece of code I always get a 404 not found.

Reading through the Github API documentation yields the following:

There are three ways to authenticate through GitHub API v3. Requests that require authentication will return 404, instead of 403, in some places. This is to prevent the accidental leakage of private repositories to unauthorized users.

(emphasis mine)

So there’s a good change that we just hit Github’s security through obscurity, so we do normally get a 403.

In fact, I tested it with Github Enterprise, and that one just returns a 403, so that’s how I figured out that the URL I was calling is correct (Github Enterprise doesn’t hide information like the regular does):

403 forbidden from a Github Enterprise instance

Let’s try the same code on a Simple IIS website with basic authentication:

Screenshot (12)

I then simplified the code to just download the contents of the website, with a simple GET:

var webClient = new WebClient
	                {
		                Credentials = new NetworkCredential("*****", "*****"),
	                };

string reply = webClient.DownloadString(new Uri("http://localhost/CredentialTest"));

Console.WriteLine(reply);
Console.ReadLine();

So that gives me the response (Default.aspx contains ‘Hi, it works’).

Now what’s going on? Is it the POST that conflicts?

Using the same code, but instead of DownloadString, I upload some arbitrary piece of text with UploadString which by default uses POST.

var webClient = new WebClient
	                {
		                Credentials = new NetworkCredential("*****", "*****"),
	                };

string reply = webClient.UploadString(new Uri("http://localhost/CredentialTest/Default.aspx"), "somerandomstuff");

Console.WriteLine(reply);
Console.ReadLine();

Please note that I post directly to Default.aspx. IIS doesn’t allow postings to directories (I’m sure you can enable it).

Anyway, this also works.

Next step? I was thinking that WebClient maybe only sends the credentials over when it detects that the machines are in the same domain / workstation?

Let’s find out with Fiddler.

I first monitored the flow for the console app to IIS and I was surprised to see that there were actually two requests, and what’s even more weird is that the first request doesn’t send the credentials (notice I still use POST, to mimic our code to connect to GitHub):

First request to IIS

Instead of returning a 403 on the file, IIS nicely returns a 401 with the WWW-Authenticate header:

first response

The WebClient is then smart enough to resend the request WITH the credentials:

second request

And then the server nicely responds with a 200, and the contents are sent (notice the picture are JUST the headers).

second response

This flow is always the same, whether it is GET or POST (what’s weird is that when you want to post a 2GB file, you send it, server replies 401, and you need to send the 2GB file again…).

Now that we know that our WebClient is behaving correctly, I decided to go and look at the request and response from GitHub:

The first request (like with IIS), doesn’t contain the Authorization header:

First GitHub request

However, in contrast to IIS, GitHub doesn’t play nice. It doesn’t send a 401 with a WWW-Authenticate header, it just returns 404 (or 403 on Github Enterprise).

First and only response from GitHub

For GitHub it is perfectly valid to send a 404 if it doesn’t want to disclose information.

The only problem is just that the WebClient doesn’t know what to do and thus, we need to do stuff ourselves!

We need to manually inject the headers when calling Github (or any webserver) at the first request:

var content = new 
			{
				scopes = new[] { "repo" },
				note = "API test",
			};

var webClient = new WebClient();

// replace webClient.Credentials = new NetWorkCredentials("*****","******") by these 2 lines

// create credentials, base64 encode of username:password
string credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes("*****" + ":" + "*****"));

// Inject this string as the Authorization header
webClient.Headers[HttpRequestHeader.Authorization] = string.Format("Basic {0}", credentials);

// Continue as you are used to!
string serializedObject = JsonConvert.SerializeObject(content);

string reply = webClient.UploadString(new Uri("https://api.github.com/authorizations"), "POST", serializedObject);

dynamic deserializedReply = JsonConvert.DeserializeObject(reply);

Console.WriteLine(deserializedReply.token);
Console.ReadLine();

And we have our token!

Borat Great SuccessHave a good one!

-Kristof

Unless you have a VERY good reason, rethrow your exception.

I see it a lot, people write a function, they catch a possible exception and throw one of their own OmgSomethingHorribleHappenedException();

Check this simplefied example:

private static void Main()
{
	try
	{
		var result = (new Api()).Find("foo");
 
		Process(result);
	}
	catch (Exception e)
	{
		Console.WriteLine(e.Message + Environment.NewLine + e.StackTrace);
		Console.Read();
	}
}

And the Api.Find(string toFind) method:

namespace ExceptionRethrow
{
	using System;
	using System.Linq;
 
	public class Api
	{
		public string Find(string toFind)
		{
			try
			{
				// force a InvalidOperationException
				return (new string[] { }).Single(e => e == toFind);
			}
			catch (InvalidOperationException e)
			{
				Logger.Log(string.Format("Tried to look for element {0}, not found", toFind));
 
				throw e;
			}
		}
	}
 
	public class Logger
	{
		public static void Log(string message)
		{
			Console.WriteLine(message);
		}
	}
}

As you can see in the Api.Find method we catch the exception from the Single call, log the error (to notify the developers or something, …) and throw the exception again.

However when we look at the call stack in the Main method we see this:

e.StackTrace

Odd, what’s line 19 in Api.cs? Right, throw e. Useful! NOT!

So you need to replace the throw e by throw.

//...
catch (InvalidOperationException e)
{
	Logger.Log(string.Format("Tried to look for element {0}, not found", toFind));
 
	throw; // removed the 'e'
}
//...

Then the result will look like this:

e.StackTrace

Now we already know little more about the cause. However to improve we need to catch the error, and throw it again by passing in the current exception as inner exception:

//...
catch (InvalidOperationException e)
{
	Logger.Log(string.Format("Tried to look for element {0}, not found", toFind));
 
	throw new InvalidOperationException(e.Message, e);
}
//...

So let’s debug again:

e.StackTrace

Again we only see the lines where the exception was thrown last, however when we investigate the InnerException’s stacktrace we get way more info:

e.InnerException.StackTrace

Now that’s an exception that’s useful to debug Smile

We can leverage this knowledge in multiple ways, one of which is shielding exceptions, making sure none go through to the client. For example when your service broker detects an error, it clears the InnerException field, thus removing any business knowledge from the exception.

When in debug mode you can want to send over those exceptions to improve the finding of bugs Smile

You can find the complete example on GitHub.

Have a good one,

-Kristof

Deserializing xml: Data at the root level is invalid. Line 1, position 1

A couple of days ago a colleague of mine encountered a problem where he had some objects that define a configuration needed to be stored in the database.

He would serialize the data with the following code:

public static class XmlSerializerHelper
{
    public static string SerializeXml<TObject>(this TObject objectToSerialize)
    {
        using (var memoryStream = new MemoryStream())
        {
            var xmlSerializer = new XmlSerializer(typeof (TObject));

            var xmlnsEmpty = new XmlSerializerNamespaces(new[]
                {
                    new XmlQualifiedName(string.Empty, string.Empty),
                });

            var xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);

            xmlSerializer.Serialize(xmlTextWriter, objectToSerialize, xmlnsEmpty);

            return Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int) memoryStream.Length);
        }
    }
}

And we would read deserialize the string with these lines:

XmlSerializer xmlSerializer = new XmlSerializer(typeof(Entity));
StringReader stringReader = new StringReader(result /* result is the value from the database */);
Entity deserializedEntity = (Entity)xmlSerializer.Deserialize(stringReader);

When the string then was read from the database we got an error:

{"Data at the root level is invalid. Line 1, position 1."}
{“Data at the root level is invalid. Line 1, position 1.”}

Let’s check why.

I think the exception is in the first 2 characters of the xml:

What's the string?

Nothing weird there… However, let’s look at the characters themselves:

char at index 0

What the? What’s that thing there at [0]? Who put it there?

Well, according to some web documentation it’s the ZERO WIDTH NO-BREAK SPACE (what’s with the screaming anyway…), also known as the BOM!

So what is the BOM? Wikipedia does a better explanation than me. The only thing you need to know is that it is optional.

So where is it coming from? And why is this an issue?

Let’s investigate our serializer helper, we run the code again, but a breakpoint in the method that actually serializes the entity, and investigate the memoryStream:

investigating the stream

As we can see, after carefully reading the Wikipedia page, these first 3 chars are actually the BOM. So the issue is not our reader, it’s our writer.

Let’s get back to the question: why is this an issue? Why can’t our reader interpret this as just a UTF-8 string, and just parse it, why do we manually have to do a TrimStart()?

It seems that the StringReader used in our code just passes through the string as a stream to our deserializer, without caring whether the string starts with or without a BOM. So we can’t do anything there except for calling the TrimStart() method as mentioned above.

So since we control both sides, let’s do it less nasty, the documentation mentions that the BOM is not required, and our reader doesn’t play nice with it. We’re not going to save it at all then Smile.

Let’s check the XmlTextWriter constructor: it accepts 2 parameters, a stream and an encoding. So what’s with this encoding? Apparently if we use the Encoding.UTF8 it emits the BOM. We can avoid this by using new UTF8Encoding(false) to prevent the BOM from being emitted.

Another way, which I find cleaner is this one, using a StringWriter which automatically infers the correct encoding.:

public static class XmlSerializerHelper
{
	public static string SerializeXml<TObject>(this TObject objectToSerialize)
	{
		XmlSerializer xmlSerializer = new XmlSerializer(typeof (TObject));
 
		XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces(new[]
			{
				new XmlQualifiedName(string.Empty, string.Empty),
			});
 
		StringWriter stringWriter = new StringWriter();
 
		xmlSerializer.Serialize(stringWriter, objectToSerialize, xmlnsEmpty);
 
		return stringWriter.ToString();
	}
}

This doesn’t involve any encoding being manipulated / used on our side, so it uses a bit more magic, but it’s way more straightforward. Please add your experience to the comments Smile

You can find the full code on GitHub here!

Have a good one,

-Kristof

Check if your LinkedIn password appears in the leaked file.

  1. Get the file from the internet.
  2. Change the line that points to the file.
  3. Run the app, type your password and be happy or sad.
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Security.Cryptography;
using System.Text;
 
namespace LinkedInFileSearcher
{
	internal static class Program
	{
		private const string PathToLinkedInFile = @"c:\combo_not.txt";
 
		private static void Main()
		{
			ConsoleKeyInfo consoleKeyInfo;
			Console.Write("Please enter password, keypresses won't be visible: ");
 
 
			var passwordStringBuilder = new StringBuilder();
 
			do
			{
				// hide keypresses
				consoleKeyInfo = Console.ReadKey(true);
 
				if (consoleKeyInfo.Key == ConsoleKey.Backspace)
				{
					if (passwordStringBuilder.Length <= 0)
					{
						continue;
					}
 
					passwordStringBuilder.Remove(passwordStringBuilder.Length - 1, 1);
				}
 
				passwordStringBuilder.Append(consoleKeyInfo.KeyChar);
			}
			while (consoleKeyInfo.Key != ConsoleKey.Enter);
 
			Console.WriteLine();
			Console.WriteLine();
			Console.WriteLine();
 
			string pass = passwordStringBuilder.ToString().Trim();
 
			SHA1 sha1 = SHA1.Create();
			byte[] passwordBytes = Encoding.UTF8.GetBytes(pass);
			byte[] computedHash = sha1.ComputeHash(passwordBytes);
 
			var stringBuilder = new StringBuilder();
 
			// Loop through each byte of the hashed data 
			// and format each one as a hexadecimal string.
			for (int i = 0; i < computedHash.Length; i++)
			{
				stringBuilder.Append(computedHash[i].ToString("x2"));
			}
 
			string hashedPasswordAsString = stringBuilder.ToString();
 
			string substringOfPassword = hashedPasswordAsString.Substring(5);
 
			Console.WriteLine("Searching for" + Environment.NewLine + "{0} or " + Environment.NewLine + "{1} in the database", hashedPasswordAsString, substringOfPassword);
 
			// using a memory mapped file to not load everything in memory all at once. 
			using (MemoryMappedFile memoryMappedFile = MemoryMappedFile.CreateFromFile(PathToLinkedInFile))
			{
				Stream memoryMappedViewStream = memoryMappedFile.CreateViewStream();
 
				using (var sr = new StreamReader(memoryMappedViewStream, Encoding.UTF8))
				{
					while (!sr.EndOfStream)
					{
						String line = sr.ReadLine();
						if (null == line || !line.EndsWith(substringOfPassword))
						{
							continue;
						}
 
						if (line == hashedPasswordAsString)
						{
							Console.WriteLine("Complete SHA-1 match found: " + line);
						}
						else
						{
							Console.WriteLine("Partial SHA-1 match found: " + line);
						}
					}
				}
			}
			Console.WriteLine();
			Console.WriteLine();
			Console.WriteLine("Done... if no matches are found you should be happy.");
			Console.ReadLine();
		}
	}
}

You can find the full code here. Please fork and update!

Get your facts right.

I just read this article: http://www.simple-talk.com/dotnet/.net-framework/10-reasons-why-visual-basic-is-better-than-c/ . And I’m pissed. Why? Allow me to elaborate

First of all, because on the top right there is “a service from redgate”. You’d expect to get their facts right.

And that’s the main issue with this article. There will always be fights among developers about which language is better, VB.NET or C#. I don’t care. Use what works best for you. And I’m always interested in a well argumented comparison between the two. (and I’m not going to show my preference here, I worked in both languages, and I like to think I know what I’m talking about).

Alright, let’s take a look at Andy Brown’s points:

Point number 1

Not a good point to start with, very personal point, so I’m like: ‘whatever dude’

Point number 2

Again, bad point, it’s a language feature, I have no problem with typing out an if else statement. However the second part of your argument is utterly wrong. C# doesn’t allow fall through in a case statement, meaning the compiler will complain if you omit the break statement:

case fallthrough C#

Point number 3

If I change the name of my control in the designer it just updates everything. I guess your Visual Studio 2010 is broken.

Point number 4

The one I hate the most. Not only are you PUSHING your personal preference to other people, you’re doing it with the wrong arguments. Allow me to explain:

&& in C# is NOT And in VB.NET. The && is a Contitional And, the And operator is logical, meaning it will execute both sides. We can read this on the MSDN page on this topic.

Let me prove it to you with an example:

C# &&

As you can see it only executes the Foo one, it completely omits the Bar!

If we follow the ‘comparison’ Andy wrote this would translate to the following VB.NET code:

VB.NET And

Well, it seems that we need the AndAlso operator in VB.NET to get the same behavior. You can get the And behavior in C# by using a single & instead of a double.

The same is for the || and the Or, you need the OrElse to achieve the || result, or, to use the Or in C#, you need the single | instead of a double.

Point number 5

First of all this is a IDE feature, not a language feature. But fine, I’ll bite: you have that too in C#. Type prop+<tab>+<tab> and you’ve got your property.

Point number 6

You are relying on the Microsoft.VisualBasic namespace. If you want to you can even add it to your C# project. The issue with this function is the following: reading the documentation doesn’t give me a single clue on what the restrictions are, what culture does it use?

You can always do int.TryParse or double.TryParse. Hec, you can even do a Regular Expression like ^[0-9]$ and then something else for the commas. I’m not a Regex Expert, and probably never will be Smile with tongue out.

And for your PMT function (click, decompiled from the dll):

PMT

Point number 7

Language feature, personal preference. No argument there.

Point number 8

First you start off by writing how to declare and define variables and then you come up with the out keyword? And if you have problems with the out keyword, you do realize that VB.NET doesn’t offer a way to the programmer to restrict him to give either an initialized variable or a non initialized variable. VB.NET only supports ByVal and ByRef, where the first is always a pointer (like in C#) and the second one is ref. No out available Sad smile. And then there is this behavior: Force an Argument to Be Passed by Value.

Point number 9

An enum in C# is an enum, even though it inherits from int. It’s just the compiler that complains. Of all the things you mentioned you have a fair point here. I’ll give you that.

Point number 10

You say it yourself, the whole point of an array is that it is fixed size. Use a list. They’re there for a reason.

I do not want to rant. I just want to get the facts right. And I feel bad. From your bio I read that you are a trainer. While they don’t have to go too deep for an introduction, you should get your facts right.

Signing off,

-Kristof

Your project dependencies matter (when using NuGet)

At work we had the following issue, we had a Silverlight project consuming some NuGet package, this project was being exposed in a Web application.

In order to keep our solution clean – we didn’t want an ‘assemblies’ folder – we used NuGet package restore.

We did it like the book said, don’t check in the packages folder, enable NuGet package restore, add the required repositories to the nuget.targets, you know the drill.

When building locally we didn’t have any issues. Everybody was able to run the project perfectly. The packages were downloaded from the repository when unavailable locally.

The issue rose when we committed our code to our source control system and ran builds of it with MSBuild.

In order to explain my issue I managed to create a very simple test case.

I created a solution with 2 projects, a web project, and a Silverlight project.

Solution window

The NuGetTest.Client had a NuGet reference to the package ‘MvvmLight’.

When you commit code to source control and run builds of it with MSBuild, it is supposed to download the NuGet package, and then build of that.

However, if we invoke the build we are presented with the following error:

Ugh, error.

What? Why?

So I went to check the packages folder on the Build location:

It's there!

It’s there! How could it not find the dll?

Time to dig deeper.

The command line executed for the project was simple:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe /nologo /noconsolelogger "C:\Builds\1\NuGetTest\New Build Definition 1\Sources\Dev\NuGetTest.sln" /m:1 /fl /flp:"logfile=C:\Builds\1\NuGetTest\New Build Definition 1\Sources\Dev\NuGetTest.log;encoding=Unicode;verbosity=diagnostic" /p:SkipInvalidConfigurations=true  /p:OutDir="C:\Builds\1\NuGetTest\New Build Definition 1\Binaries\\" /p:VCBuildOverride="C:\Builds\1\NuGetTest\New Build Definition 1\Sources\Dev\NuGetTest.sln.vsprops"  /dl:WorkflowCentralLogger,"C:\Program Files\Microsoft Team Foundation Server 2010\Tools\Microsoft.TeamFoundation.Build.Server.Logger.dll";"Verbosity=Diagnostic;BuildUri=vstfs:///Build/Build/8;InformationNodeId=622;TargetsNotLogged=GetNativeManifest,GetCopyToOutputDirectoryItems,GetTargetPath;TFSUrl=http://localhost:8080/tfs/DefaultCollection;"*WorkflowForwardingLogger,"C:\Program Files\Microsoft Team Foundation Server 2010\Tools\Microsoft.TeamFoundation.Build.Server.Logger.dll";"Verbosity=Diagnostic;"

I slimmed it down to the real deal (removed logging), and ended up with this:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe "C:\Builds\1\NuGetTest\New Build Definition 1\Sources\Dev\NuGetTest.sln" /m:1 /p:SkipInvalidConfigurations=true

Now I download the latest version of the project locally, and execute that command line:

"d:\Personal\Documents\Visual Studio 2010\Projects\NuGetTest\NuGetTest.sln" (default target) (1) ->"d:\Personal\Documents\Visual Studio 2010\Projects\NuGetTest\NuGetTest.Host\NuGetTest.Host.csproj" (default target) (2) ->"d:\Personal\Documents\Visual Studio 2010\Projects\NuGetTest\NuGetTest.Client\NuGetTest.Client.csproj" (default target) (3:2) ->(CoreCompile target) ->  ViewModel\MainViewModel.cs(1,7): error CS0246: The type or namespace name 'GalaSoft' could not be found (are you missing a using directive or an assembly reference?) [d:\Personal\Documents\Visual Studio 2010\Projects\NuGetTest\NuGetTest.Client\NuGetTest.Client.csproj]  ViewModel\MainViewModel.cs(17,34):errorCS0246: The type or namespace name 'ViewModelBase' could not be found (are you missing a using directive or an assembly reference?) [d:\Personal\Documents\Visual Studio 2010\Projects\NuGetTest\NuGetTest.Client\NuGetTest.Client.csproj]  ViewModel\ViewModelLocator.cs(15,7): error CS0246: The type or namespace name 'GalaSoft' could not be found (are you missing a using directive or an assemblyreference?)[d:\Personal\Documents\Visual Studio 2010\Projects\NuGetTest\NuGetTest.Client\NuGetTest.Client.csproj]

 

And we’re greeted with a lot of text, and at the end the same error. So it’s not the user that executes the build (he has sufficient rights).

What seems to happen here?

If we check the log we can see that the NuGetTest.Client is built because NuGetTest.Host depends on it:

Project "d:\Personal\Documents\Visual Studio 2010\Projects\NuGetTest\NuGetTest.Host\NuGetTest.Host.csproj" (2) is building "d:\Personal\Documents\Visual Studio 2010\Projects\NuGetTest\NuGetTest.Client\NuGetTest.Client.csproj" (3) on node 1 (GetXapOutputFile target(s)).

If we then continue reading the log file we can see that NuGet’s RestorePackages task is only executed after this task, which results in MSBuild trying to build the Silverlight project before all the required assemblies are downloaded.

How can we solve this? We can specify that the Silverlight project should be built before the web project, and thus forcing the RestorePackages to execute in order (before the build of the Silverlight project).

We can do this by going to project dependencies in the Solution Explorer, just do a right mouse on your solution and go to “Properties”.

In that window go to “Project Dependencies”:

Properties for solution, build order

In the “Project” dropdown you select your host project (here “NuGetTest.Host”), and check the checkbox of every Silverlight Project it depends on. This will make MSBuild explicitly build that project before continuing to the web project.

Good luck & have a good one!

Thoughts on a Resharper 6 refactoring

Consider the following code:

var foo = new List() { /* ... */ };

If we want to know if there are no items in the list we have 3 possibilities (probably more, but I’ll limit myself to 3 possibilities in this scope).

bool listEmpty = foo.Count == 0;

Which just uses List’s implementation of ICollection.Count.

Second option is:

bool listEmpty1 = foo.Count() == 0;

Last option is:

bool listEmpty2 = foo.Any();

As you might know, since ReSharper 6, the second option is suggested to be refactored to the last option.

image

However it is not the same to do an Enumerable.Count() and a Enumerable.Any() on an ICollection.

If we take a closer look using a decompile tool (Reflector, Resharper Decompile, JustDecompile, …) at the Count() and Any() extension methods we see the following:

Count:
image

Any:
image

We can clearly see that when using the Count() extension method the system actually first tries to see if the the IEnumerable<TSource> is an ICollection<TSource> or an ICollection, and using the Count property if possible, before enumerating over the entire list.

So I say: not everything Resharper says is correct, always use it carefully, think before you do an automatic refactoring. And more importantly: think about the design of your code. Is it needed that you pass in an IEnumerable? Is an ICollection<TSource> or an IList<TSource> more useful?

Have a good one!

Team Foundation 2010: Associate work item with changeset AFTER check in.

Today I was struggling associating a work item with a changeset after a check in. There is however no options for this in the UI. Or is there? Let’s check:

Cannot edit work items from changeset window

Ok, so not through the changeset UI.

Let’s check the command line.

c:Program Files (x86)Microsoft Visual Studio 10.0VC>tf changeset /?
TF - Team Foundation Version Control Tool, Version 10.0.30319.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Displays information about a changeset and lets you change the associated
attributes, such as comments and check-in notes.

tf changeset [/comment:("comment"|@commentfile)]
             [/notes:("NoteFieldName"="NoteFieldValue"|@notefile)] [/noprompt]
             [/collection:TeamProjectCollectionUrl]
             [changesetnumber | /latest] [/login:username,[password]]

(emphasis mine).

So not through the command line. Now what? Let’s think: we want to associate a changeset with a work item. Why not look the other way around?

After the successful commit remember the changeset # (in our example 25464), go to the work item you want to associate the changeset to.

Right bottom you’ll see a tab called ‘Links’ (1), click it, and then click the ‘Link to’ (2) button.

Associate work item with changeset

In the next window, select ‘Changeset’ (1) and enter the number in the box (2). Hit  ‘OK’ (3).

Associate part II

After that hit ‘CTR+S’ to save your work item.

After that go back to your changeset:

It's there!

It works!

Have a good one!

-Kristof

Silverlight 4: Bug in TabControl.TabStripPlacement = Dock.Left and OnApplyTemplate

At work I ran into a bug with the Silverlight TabControl, more specifically when setting the TabStripPlacement to Dock.Left and hiding one of the TabItems in the parent’s OnApplyTemplate.

The way to get to this bug is quite specific. For example, you can’t do it when you’re using a UserControl, since in that kind of class OnApplyTemplate is never called.

You need to build a class which inherits from Control.

public class TabControlTest : Control

Then we have the style of this particular class:

<Style TargetType="TabControlTest:TabControlTest"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="TabControlTest:TabControlTest"> <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="38" /> </Grid.RowDefinitions> <sdk:TabControl TabStripPlacement="{TemplateBinding TabStripPlacement}" Grid.Row="0"> <sdk:TabItem Header="First"> <sdk:TabItem.Content> <TextBlock Text="A" /> </sdk:TabItem.Content> </sdk:TabItem> <sdk:TabItem Header="Second" x:Name="middleTabItem"> <sdk:TabItem.Content> <TextBlock Text="B" /> </sdk:TabItem.Content> </sdk:TabItem> <sdk:TabItem Header="Last"> <sdk:TabItem.Content> <TextBlock Text="C" /> </sdk:TabItem.Content> </sdk:TabItem> </sdk:TabControl> <Button HorizontalAlignment="Center" Grid.Row="1" Content="{TemplateBinding ShowOrHide}" Command="{TemplateBinding ShowOrHideCommand}" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>

As you can see I’ve named the middle TabItem (“middleTabItem”) so I can retrieve it in the OnApplyTemplate:

public override void OnApplyTemplate()
{
    this._middleTabItem = (TabItem) this.GetTemplateChild("middleTabItem");
    this._middleTabItem.Visibility = Visibility.Collapsed;

    base.OnApplyTemplate();
}

Now if the following 2 conditions are matched:

  1. TabStripPlacement is set to Dock.Left (it doesn’t happen on ‘Top’)
  2. We set the Visibility of the said TabItem to Collapsed in the OnApplyTemplate of the surrounding Control

Then we cannot make the TabItem Visible anymore.

To show or hide I use the following code:

this.ShowOrHideCommand = new Command(() =>
                                         {
                                            this.ShowOrHideText = string.Format(SwitchTo, this._middleTabItem.Visibility);
                                            this._middleTabItem.Visibility = this._middleTabItem.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
                                         });

The solution to this is to call the OnApplyTemplate of the TabControl itself so that it recalculates it’s children’s location (or something, I’m not sure):

Give the TabControl a name, fetch it in OnApplyTemplate of your Control:

<sdk:TabControl TabStripPlacement="{TemplateBinding TabStripPlacement}" x:Name="TabControl" Grid.Row="0">
public override void OnApplyTemplate()
{
    this._middleTabItem = (TabItem) this.GetTemplateChild("middleTabItem");
    this._middleTabItem.Visibility = Visibility.Collapsed;
    this._tabControl = (TabControl) this.GetTemplateChild("TabControl");
    base.OnApplyTemplate();
}

And call the _tabControl’s OnApplyTemplate after switching the Visibility to Visible:

this.ShowOrHideCommand = new Command(() =>
                                         {
                                            this.ShowOrHideText = string.Format(SwitchTo, this._middleTabItem.Visibility);
                                            this._middleTabItem.Visibility = this._middleTabItem.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
                                            this._tabControl.OnApplyTemplate();
                                         });

This will make the UI respond correctly. For your convenience I’ve added the SL project, and you can download it here.

Visual Studio 2010 SP1: DebuggerTypeProxy works again! *yay*

A while ago I bumped into a bug in Visual Studio 2010 + Silverlight 4.

While working with a List / Dictionary / … it didn’t show the items in the list, instead it showed the normal ‘raw view’:

No debug view

As you can see, there was no view to see the items.

What you would expect to see is this:

DebugView!

I reported the bug (many others did too!). The problem was that it was fixed in SL3, but not ported to SL4.

Now it’s fixed! This makes my work a lot easier. When we get SP1 @ work too that is!

Cheers!