25 August 2009

Yet another way to determine root paths in ASP.NET

How many times I have tried to find the root path of an ASP.NET page or a handler (ASHX) in order to call another URL I can hardly count. When working with WMS and WFS services and connected documents - when you call all kind of weird 'services' - it's something that needs to be done often. I decided to put a few in what seems to become my trade mark - extension methods. I created the following extensions to HttpContext
using System;
using System.Web;

namespace LocalJoost.Utilities.Web
{
  public static class HttpContextExtensions
  {
    public static string FullRootPath( this HttpContext context )
    {
      return context.Request.Url.AbsoluteUri.Split(
        new[] { context.Request.Url.AbsolutePath },
        StringSplitOptions.RemoveEmptyEntries)[0];
    }

    public static string FullApplicationPath(this HttpContext context)
    {
      return string.Concat(context.FullRootPath(), 
        context.Request.ApplicationPath);
    }
  }
}
In the Page_Load of an aspx page you can, for instance, use the methods as described below:
var r = Context.FullRootPath();
var a = Context.FullApplicationPath();
r will contain something like "http://localhost:3974" and a "http://localhost:3974/YourTestSite". Maybe there are simpler ways, but these work very well for me.

21 August 2009

Azure Tables and DateTime: some limitations

A nice thing I ran into yesterday: if you have a TableStorageEntity child class containing a DateTime property, you have two thing to remember:
  • You cannot use DateTime.Now, just should use DateTime.UtcNow when defining "now"
  • Make sure your DateTime does default to TableStorageConstants.MinSupportedDateTime (and not the standard DateTime.MinValue)
I had a class like this
using System;
using Microsoft.Samples.ServiceHosting.StorageClient;

namespace LocalJoost.CloudMapper.Data.MapBuild
{
  public class Job : TableStorageEntity
  {
    #region Properties
    public string JobId { get; set; }
    public string MapName { get; set; }

    public DateTime QueuedAt { get; set; }
    public DateTime StartedAt { get; set; }
    public DateTime FinishedAt { get; set; }
    #endregion

    public Job()
    {
      QueuedAt = DateTime.Now;
    }

    public override string PartitionKey
    {
      get{return MapName;}
      set{}
    }

    public override string RowKey
    {
      get{return JobId;}
      set
      { }
    }
  }
}
and I simply could not store it. "There was an error processing the request". Yeah. I was able to find out the restrictions mentioned above, changed the class to
using System;
using Microsoft.Samples.ServiceHosting.StorageClient;

namespace LocalJoost.CloudMapper.Data.MapBuild
{
  public class Job : TableStorageEntity
  {
    #region Properties
    public string JobId { get; set; }
    public string MapName { get; set; }

    public DateTime QueuedAt { get; set; }
    public DateTime StartedAt { get; set; }
    public DateTime FinishedAt { get; set; }
    #endregion

    public Job()
    {
      QueuedAt = DateTime.UtcNow;
      StartedAt = TableStorageConstants.MinSupportedDateTime;
      FinishedAt = TableStorageConstants.MinSupportedDateTime;
    }

    public override string PartitionKey
    {
      get{return MapName;}
      set{}
    }

    public override string RowKey
    {
      get{return JobId;}
      set
      { }
    }
  }
}
and then it worked. Thanks to Steve Marx for pointing me to the TableStorageConstants on twitter live while I was debugging

15 August 2009

Using extension methods to serialize objects to XML and compress the result - and deserialize again

Elaborating upon the string extension methods I created for compressing and decompressing strings to and from a byte array, it turned out fairly easy to create another set of compress / decompress methods to serialize any type of object to and from a byte array containing compressed XML. I once again took the StringZipExtensions class and added the following methods to compress any old object to a byte array:
/// <summary>
/// XmlSerializes the object to a compressed byte array
/// using the specified encoding.
/// </summary>
/// <param name="objectToCompress">The object to compress.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>bytes array with compressed serialized object</returns>
public static byte[] Compress(this object objectToCompress,
  Encoding encoding)
{
  var xmlSerializer = new XmlSerializer(objectToCompress.GetType());
  using (var stringWriter = new StringWriter())
  {
    xmlSerializer.Serialize(stringWriter, objectToCompress);
    return stringWriter.ToString().Compress(encoding);
  }
}

/// <summary>
/// XmlSerializes the object to a compressed byte array using default
/// UTF8 encoding.
/// </summary>
/// <param name="objectToCompress">The object to compress.</param>
/// <returns>bytes array with compressed serialized object</returns>
public static byte[] Compress(this object objectToCompress)
{
  return Compress(objectToCompress, new UTF8Encoding());
}
Here, once again, an overload using a default UTF8 encoding and a method which uses your own encoding to do the heavy lifting. Then, the methods for decompressing, which - in all modesty - are a rather neat usage of generics, I think:
/// <summary>
/// Decompress an array of bytes into an object via Xml Deserialization
/// using the specified encoding
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="compressedObject">The compressed string.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>Decompressed object</returns>
public static T DecompressToObject<T>(this byte[] compressedObject,
  Encoding encoding)
{
  var xmlSer = new XmlSerializer(typeof(T));
  return (T)xmlSer.Deserialize(new StringReader(
    compressedObject.DecompressToString(encoding)));
}

/// <summary>
/// Decompress an array of bytes into an object via Xml Deserialization
/// using default UTF8 encoding
/// </summary>
/// <param name="compressedObject">The compressed string.</param>
/// <returns>Decompressed object</returns>
public static T DecompressToObject<T>(this byte[] compressedObject )
{
  return DecompressToObject<T>(compressedObject, new UTF8Encoding());
}
Then you can take something like this hugely complex ;-) object Person
public class Person
{
  public int Id { get; set; }
  public string Name { get; set; }
  public DateTime Birthday { get; set; }

  public override bool Equals(object obj)
  {
    var toCompare = obj as Person;
    if( toCompare != null )
    {
      return
        Name.Equals(toCompare.Name) &&
        Id.Equals(toCompare.Id) &&
        Birthday.Equals(toCompare.Birthday);

    }
    return base.Equals(obj);
  }
}
and serialize/compress and decompress/deserialize it like this:
[Test]
public void CompressObjectTest()
{
  var baseLineObject = new Person
  {
  Id = 99, 
  Name = "Tom", 
  Birthday = new DateTime(1969, 06, 03)
  };
  var compressed = baseLineObject.Compress();
  var testObject = compressed.DecompressToObject<Person>();
  Assert.AreEqual(testObject, baseLineObject);
}
Code downloadable here

12 August 2009

Using extension methods to compress and decompress strings

After some rightful comments by Jarno Peschier I decided to once again look into my blog post about putting compressed object on the Azure message queue and came up with the following set of extensions methods that allow you to compress a string via gzip into a byte array, and unzip a byte array containing a compressed string to a regular string again:
using System.IO;
using System.IO.Compression;
using System.Text;

namespace LocalJoost.Utilities.Compression
{
 public static class StringZipExtensions
 {
  /// <summary>
  /// Compresses the specified string a byte array using the specified
  /// encoding.
  /// </summary>
  /// <param name="stringToCompress">The string to compress.</param>
  /// <param name="encoding">The encoding.</param>
  /// <returns>bytes array with compressed string</returns>
  public static byte[] Compress(
   this string stringToCompress, 
    Encoding encoding )
   {
    var stringAsBytes = encoding.GetBytes(stringToCompress);
    using (var memoryStream = new MemoryStream())
    {
     using (var zipStream = new GZipStream(memoryStream,
      CompressionMode.Compress))
     {
      zipStream.Write(stringAsBytes, 0, stringAsBytes.Length);
      zipStream.Close();
      return (memoryStream.ToArray());
     }
    }
   }

   /// <summary>
   /// Compresses the specified string a byte array using default
   /// UTF8 encoding.
   /// </summary>
   /// <param name="stringToCompress">The string to compress.</param>
   /// <returns>bytes array with compressed string</returns>
  public static byte[] Compress( this string stringToCompress )
  {
   return Compress(stringToCompress, new UTF8Encoding());
  }

  /// <summary>
  /// Decompress an array of bytes to a string using the specified
  /// encoding
  /// </summary>
  /// <param name="compressedString">The compressed string.</param>
  /// <param name="encoding">The encoding.</param>
  /// <returns>Decompressed string</returns>
  public static string DecompressToString(
   this byte[] compressedString, 
   Encoding encoding)
  {
   const int bufferSize = 1024;
   using (var memoryStream = new MemoryStream(compressedString))
   {
    using (var zipStream = new GZipStream(memoryStream,
     CompressionMode.Decompress))
    {
     // Memory stream for storing the decompressed bytes
     using (var outStream = new MemoryStream())
     {
      var buffer = new byte[bufferSize];
      var totalBytes = 0;
      int readBytes;
      while ((readBytes = zipStream.Read(buffer,0, bufferSize)) > 0)
      {
       outStream.Write(buffer, 0, readBytes);
       totalBytes += readBytes;
      }
      return encoding.GetString(
       outStream.GetBuffer(),0, totalBytes);     
     }
    }
   }
  }

  /// <summary>
  /// Decompress an array of bytes to a string using default
  /// UTF8 encoding.
  /// </summary>
  /// <param name="compressedString">The compressed string.</param>
  /// <returns>Decompressed string</returns>
  public static string DecompressToString(this byte[] compressedString )
  {
   return DecompressToString(compressedString, new UTF8Encoding());
  }
 }
}
You can now quite simply do something like
using LocalJoost.Utilities.Compression;
using NUnit.Framework;

namespace LocalJoost.Utilities.Test
{
  [TestFixture]
  public class TestCompress
  {
    [Test]
    public void TestZipUnzip()
    {
      const string test = "The quick brown fox jumps over the lazy dog";
      var compressed = test.Compress();
      var uncompressed = compressed.DecompressToString();
      Assert.AreEqual(test, uncompressed);
    }
  }
}
and that will work, of course ;-) Both the Compress and the DecompressToString methods use UTF8 as default encoding as Jarno suggested by Live Messenger, but both methods also sport an overload which allow you to provide an encoding yourself. This will support almost any encoding - provided of course you use the same encoding to compress and decompress. So, this can even be used to compress strings containing Klingon messages - I hope this will earn me a fond majQa' from Jarno ;-) Code downloadable here

05 August 2009

Putting multiple elements in a Silverlight 3 DataForm DataField while retaining auto Label and Description generation

Summary: if you want to use a DataForm, want to put multiple controls in one DataField, and still want Silverlight 3 to generate Label and Description from the attributes applied to the properties you bind, you need to
  • Name your DataField
  • Add an ItemsControl as wrapping element inside the DataField
  • Bind the property to the ItemsControl
  • Bind the property of your controls inside the ItemsControl to the ItemsControls' DataContext
Bear with me: I started out with the Mike Taulty video and made a nice little business class like this:
using System;
using System.ComponentModel.DataAnnotations;

namespace DataFormDemo
{
  public class Coordinate
  {   
    private double _xmin;
    public double XMin { get { return _xmin; } }

    private double _ymin;
    public double YMin { get { return _ymin; } }

    private double _xmax;
    public double XMax { get { return _xmax; } }

    private double _ymax;
    public double YMax { get { return _ymax; } }

    public Coordinate(double x, double y,
                      double xLow, double yLow,
                      double xHigh, double yHigh)
    {
      _x = x;
      _y = y;
      _xmax =  xHigh;
      _ymax =  yHigh;
      _xmin =  xLow;
      _ymin =  yLow;
    }

    private double _x;
    [Display(Name = "X Coordinate:",
      Description = "The X coordinate of the point")]
    public double X
    {
      get
      {
        return _x;
      }
      set
      {
        ValidateValue(value, XMin, XMax);
        _x = value;
      }
    }

    private double _y;
    [Display(Name = "Y Coordinate:",
      Description = "The Y coordinate of the point")]
    public double Y
    {
      get
      {
        return _y;
      }
      set
      {
        ValidateValue(value, YMin, YMax);
        _y = value;
      }
    }
   
    private void ValidateValue(double value, 
     double minValue, double maxValue)
    {
      if (value < minValue || value > maxValue)
      {
        throw new ArgumentException(
            string.Format("Valid value {0} - {1}", minValue,maxValue));
      }
    }
  }
}
and just like Mike did, I added some labels and descriptions (in red) to get a nicer looking form. The purpose of this class is that a user can enter an X and Y value that fall in a range, which is validated. All parameters are set by the constructor. Then I made some minimal XAML
<UserControl x:Class="DataFormDemo.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
  mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
 <Grid x:Name="LayoutRoot">
    <dataFormToolkit:DataForm x:Name="MyForm"
    HorizontalAlignment="Center" VerticalAlignment="Top">
    </dataFormToolkit:DataForm>
  </Grid>
</UserControl>
Then I bound a Coordinate in the constructor
public MainPage()
{
  InitializeComponent();
  MyForm.CurrentItem = new Coordinate(
      150000, 450000, 
      0, 300000, 
      300000, 600000);
}
and got the screen left, with a wee bit more than I bargained for. I was planning to customize the default dataform layout anyway, so I changed the DataForm to:

 

 

 

 

<dataFormToolkit:DataForm x:Name="MyForm" 
  HorizontalAlignment="Center" VerticalAlignment="Top">
  <dataFormToolkit:DataForm.EditTemplate>
    <DataTemplate>
      <StackPanel>
        <dataFormToolkit:DataField >
          <TextBox Text="{Binding X,Mode=TwoWay}"/>
        </dataFormToolkit:DataField>
        <dataFormToolkit:DataField >
          <TextBox Text="{Binding Y,Mode=TwoWay}"/>
        </dataFormToolkit:DataField>
      </StackPanel>
    </DataTemplate>
  </dataFormToolkit:DataForm.EditTemplate>
</dataFormToolkit:DataForm>

That worked out nicely, but wanting to make this a little more slickey and user friendly I introduced a slider connected to the text box with the new Silverlight 3 component-to-component binding:

 

<dataFormToolkit:DataForm x:Name="MyForm" 
  HorizontalAlignment="Center" VerticalAlignment="Top">
  <dataFormToolkit:DataForm.EditTemplate>
    <DataTemplate>
      <StackPanel>
        <dataFormToolkit:DataField >
          <StackPanel>
            <TextBox x:Name="tbX" Text="{Binding X, Mode=TwoWay}"/>
            <Slider Maximum="{Binding XMax}" Minimum="{Binding XMin}"
              Value="{Binding Text, ElementName=tbX, Mode=TwoWay}"/>
          </StackPanel>
        </dataFormToolkit:DataField>
        <dataFormToolkit:DataField >
          <StackPanel>
            <TextBox x:Name="tbY" Text="{Binding Y, Mode=TwoWay}"/>
            <Slider Maximum="{Binding YMax}" Minimum="{Binding YMin}"
              Value="{Binding Text, ElementName=tbY, Mode=TwoWay}"/>
          </StackPanel>
        </dataFormToolkit:DataField>
      </StackPanel>
    </DataTemplate>
  </dataFormToolkit:DataForm.EditTemplate>
</dataFormToolkit:DataForm>
That works swell, but where are my label and my description? Turns out that the Silverlight 3 DataForm shows label and description if and only if there is one element inside that DataField to which the property is bound. You use a StackPanel or something, and the DataForm seems to loose track of where to put which label and which description, so it does not show them at all. After some experimenting around, I found the following work around using the ItemsControl:
<dataFormToolkit:DataForm x:Name="MyForm" 
  HorizontalAlignment="Center" VerticalAlignment="Top">
  <dataFormToolkit:DataForm.EditTemplate>
    <DataTemplate>
      <StackPanel>
        <dataFormToolkit:DataField>
          <ItemsControl x:Name="icX" DataContext="{Binding X,Mode=TwoWay}">
            <StackPanel>
              <TextBox x:Name="tbX" 
               Text="{Binding DataContext, ElementName=icX, Mode=TwoWay}"/>
              <Slider Maximum="{Binding XMax}" Minimum="{Binding XMin}"
               Value="{Binding Text, ElementName=tbX, Mode=TwoWay}"/>
            </StackPanel>
          </ItemsControl>
        </dataFormToolkit:DataField>
        <dataFormToolkit:DataField >
          <ItemsControl x:Name="icY" DataContext="{Binding Y,Mode=TwoWay}">
            <StackPanel>
              <TextBox x:Name="tbY" 
               Text="{Binding DataContext, ElementName=icY, Mode=TwoWay}"/>
              <Slider Maximum="{Binding YMax}" Minimum="{Binding YMin}"
                Value="{Binding Text, ElementName=tbY, Mode=TwoWay}"/>
            </StackPanel>
          </ItemsControl>
        </dataFormToolkit:DataField>
      </StackPanel>
    </DataTemplate>
  </dataFormToolkit:DataForm.EditTemplate>
</dataFormToolkit:DataForm>
But now my sliders do not work anymore: they are locked in the right position. This is logical, because the DataContext of anything inside the ItemsControl is now no longer the Coordinate object, but its X or Y property. So XMin, XMax, YMin and YMax are now unknown (and thus zero). This, too, can be solved by naming your DataFields, using the Path binding syntax and then once again using control-to-control binding:
<dataFormToolkit:DataForm x:Name="MyForm" 
  HorizontalAlignment="Center" VerticalAlignment="Top">
  <dataFormToolkit:DataForm.EditTemplate>
    <DataTemplate>
      <StackPanel>
        <dataFormToolkit:DataField x:Name="dfX">
          <ItemsControl x:Name="icX" DataContext="{Binding X,Mode=TwoWay}">
            <StackPanel>
              <TextBox x:Name="tbX" 
               Text="{Binding DataContext, ElementName=icX, Mode=TwoWay}"/>
              <Slider 
               Maximum="{Binding Path=DataContext.XMax,ElementName=dfX}" 
               Minimum="{Binding Path=DataContext.XMin,ElementName=dfX}"
               Value="{Binding Text, ElementName=tbX, Mode=TwoWay}"/>
            </StackPanel>
          </ItemsControl>
        </dataFormToolkit:DataField>
        <dataFormToolkit:DataField x:Name="dfY">
          <ItemsControl x:Name="icY" DataContext="{Binding Y,Mode=TwoWay}">
            <StackPanel>
              <TextBox x:Name="tbY" 
               Text="{Binding DataContext, ElementName=icY, Mode=TwoWay}"/>
             <Slider 
               Maximum="{Binding Path=DataContext.YMax,ElementName=dfY}"
               Minimum="{Binding Path=DataContext.YMin,ElementName=dfY}"
               Value="{Binding Text, ElementName=tbY, Mode=TwoWay}"/>
             </StackPanel>
          </ItemsControl>
        </dataFormToolkit:DataField>
      </StackPanel>
    </DataTemplate>
  </dataFormToolkit:DataForm.EditTemplate>
</dataFormToolkit:DataForm>
Which gives the resulting - and fully functional - screen to the right. A prime example of something that started out so simple, and then became just a teeny bit more complex ;-) So, using the ItemsControl and control-to-control binding makes the DataForm retain its ability to generate Labels and Descriptions automatically. Which shows that Silverlight 3 databinding is about as flexible as a rubber band. One note of caution: when binding Maximum, Minimum and Value to a slider, this must be done in exactly the right order, as shown here. I blogged about this earlier this week.

01 August 2009

Bind Silverlight 3 Slider Value, Minimum and Maximum attributes in EXACTLY the right order!

Today I want to share a piece of very hard won knowlegde with you. That piece of knowlegde is: if you want to use a Silverlight Slider, and you want to get the Value, Minimum and Maximum attributes from binding, you need to bind them in exactly the right order. That order is:
  • Maximum
  • Minimum
  • Value
or else it simply won't work. It took me the better part of a sunny saturday afternoon to figure that out. To clarify things, I will show what I was doing when I ran into this. I have a simple business class that looks like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;

namespace LocalJoost.CloudMapper.Ui
{
  public class TileRequestParms 
  {
    private double _xmin;
    public double XMin { get { return _xmin; } }

    private double _ymin;
    public double YMin { get { return _ymin; } }

    private double _xmax;
    public double XMax { get { return _xmax; } }

    private double _ymax;
    public double YMax { get { return _ymax; } }

    private double _xLow;
    [Display(Name = "X lower left:")]
    public double XLow
    {
      get
      {
        return _xLow;
      }
      set
      {
        ValidateValue(value, XMin, XHigh);
        _xLow = value;
      }
    }

    private double _yLow;
    [Display(Name = "Y lower left:")]
    public double YLow
    {
      get
      {
        return _yLow;
      }
      set
      {
        ValidateValue(value, YMin, YHigh);
        _yLow = value;
      }
    }

    private double _xHigh;
    [Display(Name = "X upper right:")]
    public double XHigh
    {
      get
      {
        return _xHigh;
      }
      set
      {
        ValidateValue(value, XLow, XMax);
        _xHigh = value;

      }
    }

    private double _yHigh;
    [Display(Name = "Y upper right:")]
    public double YHigh
    {
      get
      {
        return _yHigh;
      }
      set
      {
        ValidateValue(value, YLow, YMax);
        _yHigh = value;
      }
    }

    public TileRequestParms(double xLow, double yLow, 
        double xHigh, double yHigh)
    {
      _xmax = _xHigh = xHigh;
      _ymax = _yHigh = yHigh;
      _xmin = _xLow = xLow;
      _ymin = _yLow = yLow;
    }

    private void ValidateValue(double value, 
      double minValue, double maxValue)
    {
      if (value < minValue || value > maxValue)
      {
        throw new ArgumentException(
           string.Format("Valid value {0} - {1}", minValue,
                             maxValue));
      }
    }
  }
}
Its purpose is very simple: the user can input two coordinates that are inside a rectangle defined by the max and min values in the constructor. In addition, XMax can never be smaller than XMin, and YMax never smaller than YMin. Now I wanted the user to be able to input the value both by a text box and a slider. And I decided to use the new Silverlight 3 DataForm and control to control binding:
<dataFormToolkit:DataField Grid.Row="0" Grid.Column="0" 
  Label="X lower left: ">
    <StackPanel>
        <TextBox x:Name ="tbXLow" Text="{Binding XLow,Mode=TwoWay}" />
        <Slider x:Name="slXLow" 
         Value="{Binding Text,ElementName=tbXLow, Mode=TwoWay}" 
         Minimum="{Binding XMin}" Maximum="{Binding XMax}"/>
    </StackPanel>
</dataFormToolkit:DataField>
<dataFormToolkit:DataField Grid.Row="0" Grid.Column="1" 
      Label="Y lower left: ">
    <StackPanel>
        <TextBox x:Name ="tbYLow" Text="{Binding YLow,Mode=TwoWay}" />
        <Slider x:Name="slYLow" 
         Value="{Binding Text,ElementName=tbYLow, Mode=TwoWay}" 
         Minimum="{Binding YMin}" Maximum="{Binding YMax}"/>
    </StackPanel>
</dataFormToolkit:DataField>
<dataFormToolkit:DataField Grid.Row="1" Grid.Column="0" 
    Label="X upper right: ">
    <StackPanel>
        <TextBox x:Name ="tbXHigh" Text="{Binding XHigh,Mode=TwoWay}" />
        <Slider x:Name="slXHigh" 
         Value="{Binding Text,ElementName=tbXHigh, Mode=TwoWay}" 
         Minimum="{Binding XMin}" Maximum="{Binding XMax}"/>
    </StackPanel>
</dataFormToolkit:DataField>
<dataFormToolkit:DataField Grid.Row="1" Grid.Column="1" 
  Label="Y upper right: ">
    <StackPanel>
        <TextBox x:Name ="tbYHigh" Text="{Binding YHigh,Mode=TwoWay}" />
        <Slider x:Name="slYHigh" 
         Value="{Binding Text,ElementName=tbYHigh, Mode=TwoWay}" 
         Minimum="{Binding YMin}" Maximum="{Binding YMax}"/>
    </StackPanel>
</dataFormToolkit:DataField>
Now the annoying thing is: is worked for X, but not for Y. The sliders stayed put and refused to work. After a considerable time of debugging and I noticed one thing: it worked when I put hard coded values for Minimum and Maximum, but not if I used binded values. For X, I was using a minimum 0 and a maximum of 300000. But I was using a minimum of 300000 for Y, and 600000 for a maximum (Yes, that's the RD coordinate system, for my Dutch readers). With a bit of debugging I was able to find out the both minimum and maximum value of the first slider where set to 300000, as was it's value. So I reasoned: maybe, by setting the value, the maximum value is set already (because the minimum is already 0) and it does 'not like' to get a max value twice. Or something among that lines. So what if I first set the maximum, then the minimum, and only then the value? So I changed the XAML for the first slider
<dataFormToolkit:DataField Grid.Row="1" Grid.Column="0" 
  Label="X upper right: ">
    <StackPanel>
        <TextBox x:Name ="tbXHigh" Text="{Binding XHigh,Mode=TwoWay}" />
        <Slider Maximum="{Binding XMax}" Minimum="{Binding XMin}"
         x:Name="slXHigh" 
         Value="{Binding Text,ElementName=tbXHigh, Mode=TwoWay}"/>
    </StackPanel>
</dataFormToolkit:DataField>
And it worked. It's these little things that make a developer's life interesting, isn't it? Although, in my case, it also gave this here developer a pretty potent headache.