Showing posts with label WPF. Show all posts
Showing posts with label WPF. Show all posts

Thursday, 23 October 2014

When Bindings Fail, and text blocks show double quote greater than

Today I was tracking down a bug where my text block was showing “> (quote greater than) when the data context was null.

I’d run into this problem once before in a WPF application, but couldn’t remember what the resolution was. 

After playing around for awhile, with various things fixing the problem, but for no apparently good reason, I had another look at the XAML.  A really close look.

Here was the XAML:

<TextBlock Style="{StaticResource StrokeSummaryBigValue}" 
                          Text="{Binding Rating, Converter={StaticResource rating}}">"></TextBlock>


It looks like while typing, intellisense had entered an extra “> into the code, so that when the binding failed, it showed the text contained in the text block.


So there you have it, if you see this behavior, search your code for “>”>

Wednesday, 2 October 2013

Working with SQLite with both VS2012 and VS2010

I’ve recently started building a new component that needs database access, and when once again faced with a choice of database technology (the existing codebase is using SQL CE 3.5) I chose SQLite this time, for the following reasons

  • SQLCE can’t seem to work with the entity framework and create autonumbered entries
  • SQLite is supported on other platforms.

So, I began designing my tables, following the instructions here: https://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki

I downloaded the 32 bit installer for VS2010, and installed it, but was surprised to find that VS2012 support wasn’t included, so I downloaded the vs2012 installer too, and installed that, using the defaults of the installer.

That didn’t work, giving me errors when I tried to open the VS2012 designer, so I uninstalled from Programs & Features and started again with the 2012 installer.

That then worked for 2012, but no support was installed for VS2010 this time.  So, I ran the installer again for VS2010, and made sure to change the installation folder to something other than the defaults.

And then it worked.

Tuesday, 20 November 2012

DateTime.ParseExact can be misleading

Last night, I was made aware of a crash in our Payslips for PAYE Tools program that occured pretty much every time it ran.

It turns out the user had changed the short date format on their system, so it included either a . or a – as the separator.

I had this line in my code:

Date = DateTime.ParseExact(value, "dd/MM/yyyy", null);

While this looks like it might do the right thing, it turns out it doesn’t as I didn’t use the invariant culture for the conversion.  The word exact is a bit misleading, as I’d assumed (incorrectly) that / was a character.  In fact, it’s replaced with the date separator, and no longer matches the string that came out of the database with a /

Thursday, 26 July 2012

Data-Binding Pitfalls on WPF, WP7, Silverlight and WinRT Part 1

I've been doing a lot of work on all four platforms (WPF, WP7, Silverlight and WinRT), and am puzzled by how data-binding is supposed to be implemented.

For example, in David Anson's Data Visualization toolkit, you can be penalized with an extraordinarily long delay if property changes happen in the wrong order.
http://forums.silverlight.net/t/101287.aspx/1

The problem in that specific instance is that the control sets the default range to 1 year, but the order of changing properties can cause the problem to occur at any point.  For example, if you were to change a date time axis from a range of 1 day to 1 year and back, with intervals of 1 hour and 1 month respectively, on the way to 1 year, you'd need to set the interval first, and the range second.  In the other direction, it's the opposite.

Now with Data Binding, it's not clear how you'd arrange this.  In my case in WPF everything appeared to be working just fine, until I newed up a control for printing.  Printing worked fine, but PrintPreview had a hugely long delay, before the preview window appeared.  The culprit?  The DateTimeAxis.

It turned out the problem was provoked by my code behind setting the Interval and Interval type.  When I changed it to data-binding, the problem disappeared.

Now, I suspect the reason this fixed the problem has to do with the options on Dispatcher.BeginInvoke and the Dispatcher itself.  If you look there (in WPF) the DispatcherPriorities includes Render and DataBind levels in addition to Normal.  Shawn Wildermuth wrote an article a long time ago about the dispatcher http://msdn.microsoft.com/en-us/magazine/cc163328.aspx that provides some insights.

If you examine the code in the DateTimeAxis, you’ll see that on a property change, most of what happens is a simple call to Invalidate().  That pushes the refresh down to the Render level, allowing all of the other Invalidate calls to occur before doing any work for changes resulting from property changes.  I’ll come back to that scheduling trick in a part 2 tomorrow.

Now, my code didn’t use data binding for all of it’s properties.  It all ran on the UI thread in response to the user asking to print.  As a result, (if my analysis is correct) the initial data binding has not yet run, as we haven’t ceded control to the dispatcher.

Eventually, my UI control is asked to Measure and Arrange from my code on the UI thread, and finally UpdateLayout is called by my code.  Because I had set some of the properties in code (the Interval, and Interval Types) but not the Max and Min, when the control is being laid out it does all the work to create the stupid number of ticks for each hour in a year.

Then, inside UIElement.UpdateLayout() I have this call stack:

PresentationFramework.dll!MS.Internal.Data.DataBindEngine.Task.Run(bool lastChance) + 0x31 bytes   
PresentationFramework.dll!MS.Internal.Data.DataBindEngine.Run(object arg) + 0xb6 bytes   
PresentationFramework.dll!MS.Internal.Data.DataBindEngine.OnLayoutUpdated(object sender, System.EventArgs e) + 0x1e bytes   
PresentationCore.dll!System.Windows.ContextLayoutManager.fireLayoutUpdateEvent() + 0x154 bytes   
PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout() + 0x926 bytes   
PresentationCore.dll!System.Windows.UIElement.UpdateLayout() + 0x16 bytes   

So, it finally data-binds the rest of my values, and lays out the axis correctly with the intended values for Max and Min.

By switching everything to use data-binding instead mixing it with code-behind, the default values in the DateTimeControl work correctly (though the work is still wasted), and then the data-binding sets them to the new values.

It’s this wasted effort that concerns me, so future instalments will examine what can be done about it.

Wednesday, 25 July 2012

Landscape Printing and Preview in WPF: It’s the overload, stupid!

We’re just finishing up a new release for our weather station software, and thought it might be nice to add printing for our users.

The printouts lend themselves to Landscape orientation, but much as we searched, we couldn’t find a way to get the DocumentViewer control to show us the preview in Landscape mode.

In the end, there are a sequence of things that need to be done.

First, when you create your PrintDialog, make sure you set the PageOrientation property to PageOrientation.Landscape.  That in itself isn’t enough though.

So, now store the size of the paper from the successful print dialog, and construct your DocumentPaginator object.

var ms = printDialog.PrintTicket.PageMediaSize;
Size pageSize = (printDialog.PrintTicket.PageOrientation == PageOrientation.Portrait) ?
new Size(ms.Width.Value, ms.Height.Value) :
new Size(ms.Height.Value, ms.Width.Value);

var Paginator = new SamplePaginator(uc, pageSize);


Where uc some control or Framework Element.


And here’s the important bit.  When you return a DocumentPage, make sure you use the overload that takes the PageSize you calculated above.  If you do, then preview works great. 


I had naively assumed that overriding the PageSize abstract property would do this job, but it did not.

Wednesday, 4 July 2012

I really do hate hardware


Today one of my customers was trying to print with a Kodak ESP C310 printer and our program (http://www.wieser-software.com/payslips) crashed when calling a WPF 4.0 program's Print Dialog.

We installed the latest drivers, but the problem remained.


As a last ditch effort I selected "print directly to printer" on the preferences for the printer, and it worked, so it appears there's some incompatibility with the Kodak print spooler.

Thursday, 28 June 2012

Burnt by a faulty WPF install

Over the past couple of days, we’ve been trying to debug a problem reported by a customer of our WPF program, Payslips for PAYE Tools.

The customer was reporting that the program was crashing, and not generating any error reports (internally, we hook into the unhandled exception handler, and generate an email message containing the report, so we can examine the flaw).

Eventually after connecting to his machine remotely, we discovered that there were events in the event log, and they were TypeInitializationExceptions.

That exception is thrown when a static constructor fails for some reason.

Unfortunately, that implied that some of our static members were failing to initialize correctly.  So, we took the initialization and moved it into the instance constructor (as it was the App class), hooked up the error handler at the beginning of the class constructor, and crossed my fingers that it would catch the error.

But it didn’t work!  So I added a message box at the beginning of the class constructor.  That didn’t show either on the afflicted system.  With hundreds of working installs, it had to be something to do with the installation, so another connection to the remote PC was made, and we noticed that there were outstanding Windows updates for .NET 4.0

We left the customer to install them, but he told us that the updates also failed.

Eventually, he told us that the error messages led him to the .NET removal tool (link to follow).

We suggested that he should run that, reinstall our program, and let it download the .NET framework.

And what a surprise, our program now works correctly.

But what a horrible experience for the end user!  How does an install get into that state, and how can we diagnose it?

Tuesday, 12 June 2012

WPF window rendering woes

Today, I started adding some functionality to a new WPF app, and noticed that as I resized my window to be larger, there were black strips appearing on the bottom and right sides of the client area, that were eventually filled with the controls on the page.

This looked horrible, and it appears I’m not the first one to run across this, however, there still seems to be no resolution.

As I’m an old school MFC/C++ guy, I figured there had to be a way to do this.

My first strategy was to hook in a new WndProc handler, and process WM_ERASEBKGND messages.  I thought, claiming I’d erased it would do the job, but that made no difference.

So, I actually implemented code to fill the clip region with the WINDOW_COLOR brush, and that did fix the problem.  No more black edges, but that was all a little bit unsatisfactory.  What I really wanted to do was register my own class for the WPF window, and set the background brush correctly to match the window.

That doesn’t appear to be possible, but it is possible to PInvoke and call SetClassLong on the background brush.

So my preferred solution is set the class background brush, and watch WPF draw it correctly. It appears that WPF creates a new class for each application so this should be safe, though if it’s not the previously described solution would definitely work.

Back to the Desktop and WPF, no WinRT for me

I’ve been working pretty much full time lately on Windows Phone 7 apps, and making pretty much no money.  As I have a house and a family, this isn’t a situation that can continue for very much longer.  One of our previous WPF apps is doing quite well on the desktop, and makes more in a single sale than I make in a month of Windows Phone App sales and ad revenue.

I have considered porting some of my apps to WinRT for Windows 8, but based on what Josh Smith says here http://joshsmithonwpf.wordpress.com/2012/03/20/does-anyone-actually-care-about-winrt/ I think my gut feel that it is a lot of work, and it will probably come with more pain than gain for me is probably spot on.

I was one of first into WP7 development, and the theory was that we’d be discoverable and get early sales.  What happened instead is that others got in on the bandwagon, and produced similar apps, so in my view, there’s no benefit to showing my hand early. 

If it really is that good of an environment, it shouldn’t be that hard to port the apps to the new platform.

Friday, 16 March 2012

My splash screen closed my message box

Today, while trying to debug on a very slow virtual machine, I decided to add a splash screen to my WPF application.  After all, my phone applications have one, so how hard could it be?

First I found these instructions on MSDN for adding a splash screen

Wow.  That’s easy!  However to my surprise, I found that in some circumstances, my application caused a message box to show before the main window was displayed, as my MessageBox was shown by the constructor.

And to my surprise, the splash screen closed that message box.  Now I’m not the first to find this problem.  There are various discussions and links here, but I have to admit, I didn’t really like any of the three options presented there.

So, I thought, there must be a better way.  Well, I think I’ve found one!

First, construct your SplashScreen object as shown in this constructor:

MainWindow()
{
SplashScreen ss = new SplashScreen("Images/splash.png");

// show the splash screen
ss.Show(false);

// do the rest of the constructor work.
// ...
//

ss.Close(new Timespan.FromSeconds(0.1));
}

 

When I first wrote the above, I had my splash screen flicker, as my main application appeared on top of the splash screen.  To solve that I came up with a very confusing BeginInvoke, which is only necessary to solve a different problem.

 

Turns out, that because I followed the initial instructions, I had added my splash screen with a build action of “SplashScreen”.  That’s wrong.  The build action should be resource if you want to do it manually, otherwise, it appears that you end up with two splash screens!


Wednesday, 14 March 2012

WPF program crashes when printing

I had a customer this week let me know that he was unable to print from our Payslips for HMRC PAYE Tools program.

After a long time spent debugging, it transpires that the computer had a problem in the registry, where a font was incorrectly installed.

In his case, it was this font: MT Extra (TrueType)
and it was installed here on his XP machine:
C:\Program Files\Common Files\Microsoft Shared\Equation\mtextra.ttf

We deleted the entry, and managed to print to the XPS driver, but printing to the printer itself still didn’t work.  Maybe it requires a reboot?  Still looking into that one.

And we’re not the first to see this either.  Here are a couple more reports in the wild:

Bradley Grainger's 2009 blog entry

and this one from 2011 by Divya N Singh

Friday, 9 March 2012

Getting an integer identity for an object on WP7

Today’s quirk was discovered while trying to get an object’s identity.

Jeffrey Richter in CLR via C# (version 2)mentions that you can call RuntimeHelpers static GetHashCode method in a box at the bottom of page 148 to get a unique ID for an object.

The documentation for the .NET framework 4 and earlier all say this about GetHashCode:

The RuntimeHelpers.GetHashCode method always calls the Object.GetHashCode method non-virtually, even if the object's type has overridden the Object.GetHashCode method. Therefore, using RuntimeHelpers.GetHashCode differs from calling GetHashCode directly on the object with the Object.GetHashCode method.

But, if you change to the Silverlight version of the documentation of GetHashCode that statement is missing, and if you actually build the code you’ll find that it just calls GetHashCode on the object itself, with whatever override is provided on that type (as these people found out)

Further investigation into GetHashCode all the way back to V2 of the framework looks unpromising as well:

The default implementation of the GetHashCode method does not guarantee unique return values for different objects. Furthermore, the .NET Framework does not guarantee the default implementation of the GetHashCode method, and the value it returns will be the same between different versions of the .NET Framework. Consequently, the default implementation of this method must not be used as a unique object identifier for hashing purposes.

There is some information here on other possible strategies on stackoverflow for getting an identity.

My intended use was for serialization, and the above thread points out the ObjectIDGenerator class which looks like exactly the functionality I’m looking for, but it doesn’t work on Silverlight!

GCHandle also doesn’t look like it will do the job, as most members are security critical, and the only value it gives out is a pinned address anyway, though there is a promising Narrowing operator, so casting to an IntPtr looks like it might work.

However, when I try to run it I get an exception:

Attempt to access the method failed: System.Runtime.InteropServices.GCHandle.set_Target(System.Object)

which I guess is expected as they’re marked with [SecurityCritical] attributes.

Despite all that, it does appear that object.ReferenceEquals will tell me if two objects are the same, and a promising approach appears to be

Dictionary<object, int> dict = new Dictionary<object, int>(10);

I initially created two objects and added them, and when querying dict.Count I got two items.

 

So, on my objects I created, I added an override of object.Equals to always return true.

Disappointingly, I now only have one object in my dictionary, as it obviously uses equality rather than identity.

 

However, hidden among all of the dictionary constructors is a constructor that takes an IEqualityComparer<T>, if only I could figure out how to create one.

 

Luckily, there’s an implementation here, the only change i made was to remove the call to RuntimeHelpers.GetHashCode() and use obj.GetHashCode() instead, as they do the same thing anyway on the phone.

 

After that, even with my operator= and GetHashCode overridden in my class to always return true and 0 respectively, my code as follows successfully labels two unique objects  in its Dictionary.

var datesTmp = new EphemeralState();
Dictionary<object, int> dict =
new Dictionary<object, int>
(10, ObjectReferenceEqualityComparerer<object>.Default);

dict[datesTmp] = 1;

var datesTmp2 = new EphemeralState();
dict[datesTmp2] = 2;

Debug.WriteLine(dict.Count);


 

So now we know how to build something equivalent to the ObjectIDGenerator class.  Find the value of an object key in the dictionary above, and if it doesn’t exist, generate a new id, and add it to the dictionary.

 

Back to work now.


 

Wednesday, 7 March 2012

Thread Safety on WPF, Silverlight and WP7

About a week ago I posted about IsolatedStorage and Thread Safety and what exactly the statement Any instance members are not guaranteed to be thread safe means in the documentation of the class libraries.

A lot of discussion making superstitious claims about serializing access to instance members ensued, however all of those led to illogical conclusions, implying that it was never possible to write a program that used multiple threads.

Eventually, I contacted members of the Base Class Library team who responded with the following definitive statements:

In general, the overall Framework guidance should apply to IsolatedStorage as well.

- Static members should be thread safe.

- Instance members cannot be expected to be thread safe unless otherwise specified.

- Instance members on different instances should be safe to use concurrently.

In addition, specifically related to IsolatedStorage, I got this definitive statement:

If you work on distinct instances of IsolatedStorage / ..File / ..Stream on different threads you will not run into any concurrency issues.

Different instances of those types have private copies of all internal data; and in cases where we need to update global state (e.g. isolated storage quota) we do all the necessary locking for you.

Note, however, that if you open two distinct Stream instances on the same file – whether through the iso storage or directly thought FileStream, we do not make any guarantees about the order in which these instances will access the underlying physical file. When using two distinct stream instances on the same file concurrently from different threads, you may end up with interleaved, mixed or invalid data in the file. However, the stream objects themselves will remain consistent and valid.

This is different from using one single Stream object from different threads: That is not supported and can break the stream object itself, not only resulting in data corruption, but also in weird runtime behaviour.

So there you have it.  Put your mutexes and locks down and step away from the code face and think!

If you are seeing what looks like a threading problem, and you found a mutex or lock solved it, it probably has, but not for the reason you thought.  There is probably some other multithreading logic error you had not foreseen, and your lock will either kill performance, or just make the bug even harder to find.

Friday, 24 February 2012

Asynchronous actions with RX

I was trying to come up with a syntactically tidy way to run some code in the ThreadPool.

Assume I have a function as follows that performs the work:

void PopulateIsolatedStorageList()
{
using (iso = IsolatedStorageFile.GetUserStoreForApplication())
{
string[] found = iso.ISO.GetDirectoryNames("Logs/*.*");
foreach (string s in found)
{
// do some time consuming processing here
}
}
}


First I attempted to create an Action delegate for the above, and then tried to do this:


Action b = this.PopulateIsolatedStorageList;
Observable.FromAsyncPattern(b.BeginInvoke, b.EndInvoke )().
ObserveOn(Scheduler.ThreadPool).Subscribe();



This did not work.  First, PopulateIsolatedStorageList ended up with a null this pointer.


Secondly, it seemed to run on the main thread in any case.





A bit of lateral thinking required


What are my goals?



  1. I want to run my action on a worker thread.

  2. I want to perform some completed action on the UI thread (namely OnPropertyChanged).

Why not create an enumeration of things I want to get done on the worker thread then?


Action[] a = { PopulateIsolatedStorageList};
a.ToObservable(Scheduler.ThreadPool).Subscribe
( action => action(),
      () => Deployment.Current.Dispatcher.BeginInvoke(() => UINotify() ));


Where UINotify() is the expression to perform on the main thread.

 

And surprisingly, it all works.

Update – It helps if you find the right method


Having searched some more I found another mechanism, which removes the need to create the array, and is probably the proper way to do it.


Observable.ToAsync(PopulateIsolatedStorageList)()
.Subscribe(
     _ => { },
() => Deployment.Current.Dispatcher.BeginInvoke(() => UINotify() ));


Friday, 23 December 2011

US Culture is more pervasive than you might expect

While getting an application ready to ship, I switched the locale of my WP7 device to Danish.  Fingers crossed, my app will magically display Pi as 3,14159

Alas, that’s not what happens.  When I put up a text box, and enter 3,14159, my software actually gets a value of 314159 instead.

Posting a query on Culture to the WP7 Forums got no result, so I dug into Laurent Bugnion’s excellent Silverlight 4 Unleashed book, and found that he suggests in Listing 6.10 that you set the Language on the Page.  I didn’t really want to build a page for every language, as I’ll never hit them all, so instead modified the constructor of my FrameworkElement classes as follows:

InitializeComponent();
this.Language = System.Windows.Markup.XmlLanguage.GetLanguage(Thread.CurrentThread.CurrentUICulture.Name);


Eventually I tracked down several sources for why this is the case:


http://www.pedrolamas.com/2011/07/28/cuidado-com-o-frameworkelement-language/
http://connect.microsoft.com/VisualStudio/feedback/details/442569/wpf-binding-uses-the-wrong-currentculture-by-default


 


imageimageI also attempted to make it easier for the user to enter numbers into my window, using the InputScope attribute of my TextBoxes, I assumed that InputScope=”Number” would be what I wanted. How Naive.  The image on the left shows how unsuitable this is.


In the Danish Locale, we really want a comma on the keyboard, not a decimal point.  In addition, there is no negative sign.


The best compromise I could find (after randomly trying many of the InputScope values that looked appropriate was Time, which gives the keyboard on the right.


Now I just need to come up with a strategy for translating the Validation Exceptions that I’m currently displaying in the UI.

Thursday, 8 December 2011

Why isn’t my TaskDialog PInvoke call working?

I’m trying to track down a problem today where one of our programs apparently locks up.

Here’s the screengrab:

what

Notice the partially drawn window around the center, with leftover high scores from the previous window.  I’m pretty sure this outer box is the window frame for the task dialog, as that’s where it’s positioned when it is shown correctly.  The high scores dialog box that was on the screen was larger, and the buttons and bottom of the screen have redrawn.

Here’s how I declare the signature:

[DllImport("comctl32.dll", PreserveSig = false, CharSet = CharSet.Unicode)]
private static extern TaskDialogResult TaskDialog(IntPtr hwndParent, IntPtr hInstance,
string title, string mainInstruction, string content,
TaskDialogButtons buttons, TaskDialogIcon icon);



And here’s how I call it:


int split = text.IndexOf('\n');

TaskDialogResult tdr = TaskDialog(hWnd, IntPtr.Zero, caption,
text.Substring(0, split),
text.Substring(split + 1), taskBtn, taskIcon);



Now, what I think is probably happening is that for some reason, the two strings passed into the TaskDialog are being garbage collected, but that’s just a wild guess at the moment.

Wednesday, 12 October 2011

Connecting to SQL CE 3.5 databases from C#

I’ve been scratching my head for a long time about how I should be connecting to my .sdf files from c#.  I’ve had a couple of data based projects on the go for some time now, and continually find myself bumping up against some problem as the project progresses.

I tried creating the data with the wizard and an Entity Data Model/Entity Framework.

This leads to pain, as your tables cannot have an server generated identity field.

I tried creating the database using EDM and code first

But for the life of me, I couldn’t work out how to bind it to tables in the database.

I tried using OLE DB

And then had to struggle with code to fetch the identity like this:

_lastIndexCmd = _sqlConn.CreateCommand();
_lastIndexCmd.CommandText = "SELECT @@IDENTITY";


and had to be very careful about inserting only one record at a time.

 

This spring, I went to TechDays in London, and Andy Wigley gave a talk http://blogs.msdn.com/b/mikeormond/archive/2011/07/12/tech-days-live-video-sql-server-compact-and-user-data-access-in-mango.aspx
on using SQL CE in mango.  I asked a bunch of questions in the talk (yes, that’s me in the orange jumper at the front) about server generated keys.


The reason I asked, was because I had misunderstood the comment earlier in the talk that mentioned EDM 4.1, and didn’t realize that we were actually talking about LINQ to SQL, not EF.  In other projects I had problems with inserting multiple server generated keys as described in the OLE DB section above.


Today, I watched the talks again, very carefully, and realized that I should have been using Linq to SQL, and that that will let me work on the phone too.


To create your objects from an already existing database file, run the following command:


sqlmetal /dbml:mydb.dbml mydb.sdf /pluralize


Afterwards, simply add the mydb.dbml file to your project.


Don’t forget the pluralize switch if you’re trying to replace your EDM implementation, or you may be in for a lot of member renaming.


The other gotchas?  AddObject in EF is replaced with InsertOnSubmit on the tables, and  SaveChanges on the DataContext becomes SubmitChanges.


After these changes, I was pleasantly surprised that my query time dropped from 95msec for 11520 records to 78, and improvement of  18%.


I hope I’ve got this right, but if not, hope ErikEJ over at http://erikej.blogspot.com/ will put me right.

Friday, 30 September 2011

Who says I don’t know anything about Culture

I’ve just had a good dose today, because I got bit doing some csv export for a customer in Germany.  As a dyed in the wool C++ programmer, the move to C#/.NET contains a few unexpected gotchas.
Today’s?  The culture used by string.Format() isn’t at all like the one used by sprintf.
It turns out that though they both use the current culture/locale for the thread, in C++, that’s set to the C_LOCALE, whereas in C#, it’s set to the current culture of the operating system.
So in France for example, 1,000.45 gets printed like this: 1 000,45
That’s really going to mess up your CSV.
Of course, you can specify a culture in every print, but really, who’s going to remember to do that, and even then I haven’t checked if it gets passed down to the ToString method when formatted as {0} with no specifier.
So, I really need to do something like this:
CultureInfo ciEntry = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo(String.Empty);

// do my culture invariant work here...

Thread.CurrentThread.CurrentCulture = ciEntry;



And, remember to do it on thread pool methods too, because who knows what Culture they were last looking about.

Sunday, 18 September 2011

WPF/Silverlight quirks

 

I’ve been trying to share some code between some of my projects, and occasionally struggle to get things to work.

Today, I moved some data binding from my WPF project to my Silverlight project.  The WPF project was using a string indexer on the current DataContext like this:

{Binding Path=.[version]}

This page:

http://msdn.microsoft.com/en-us/library/system.windows.data.binding.path.aspx

states

Optionally, a period (.) path can be used to bind to the current source. For example, Text="{Binding}" is equivalent to Text="{Binding Path=.}".

Silverlight on the other hand does not like the leading period, but works correctly (as does WPF) when specified like this:

{Binding Path=[version]}

Moral of the Story:  Don’t use leading periods.