Friday 30 December 2011

Tombstoning and Mango: Something’s Awry!

I’ve wasted another day.

I’ve been trying without success to understand what’s happening with Tombstoning, and why my properties which are data bound are not being updated.

To cut a long story short, you can reproduce this using Matt Lacey’s Tombstone Helper Toolkit as follows:

First, download the latest source from CodePlex.

Build for OS 7.1, which requires you to modify the Demo Projects WMAPPManifest.xml file to have the correct AppPlatformVersion 7.1, or you get an error from Microsoft.Phone.PreImport.targets ValidateWMAppManifest rule.

Go to the Debug tab of the demo, and switch on Tombstoning.

Build and run the file, and go to the TextBox demo, and verify that if you change field 1, and navigate away and back, that its value returns.

Now make the following changes:

On line 30 of TextBoxes.xaml, change the line to this, so it’s DataBound as follows:

<TextBox Name="first" Text="{Binding Path=UserName, Mode=TwoWay}"/>


Now, add this to the top of TextBoxes.xaml.cs, inside the namespace declaration.  It’s based on Jesse Liberty's ViewModel here:


public class MainPageViewModel : INotifyPropertyChanged {
private string _userName;
public string UserName
{
get { return _userName; }
set { _userName = value; NotifyPropertyChanged("UserName"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propName)); }
}

public MainPageViewModel()
{
UserName = "I AM SAM";
}
}


Finally, modify Matt’s TextBoxes constructor to set my ViewModel as the DataContext:


public TextBoxes()
{
DataContext = new MainPageViewModel();
InitializeComponent();
}

If you now run the code, and change “I AM SAM” to “SAM I AM”, navigate away, and then back, you’ll see that the name is not replaced.

 

I’ve found a bodge, based on what I read here on the WP7 forums:  Change OnNavigatedTo like this:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
// Uncomment the next line for changes to be reloaded
// Loaded += (s, ea) =>
{
this.RestoreState();
};
}

 



If you uncomment the Loaded+= line, the code will now work as expected.


Clearly there’s something odd going on with Mango here, as I’ve seen this in my own simplistic implementation where I set the Text property of a TextBox directly without all the framework like this:


Long.Text = (string)State["Longitude"];


and it too does not get updated correctly.

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.

Wednesday 21 December 2011

Fast and Loose with WP7 Mutex

While trying to figure out how to synchronize access between my ScheduledAgent and my main program, I started to investigate how Mutex objects behave.

Normally on Windows, when a Mutex is abandoned, an exception is raised when the Mutex is next acquired.  After searching online, I failed to find a description of what happens, particularly if you are holding a Mutex when WP7 decides to kill your process.

Here’s my sample I dropped onto a phone page:

private void OnLoaded(object sender, RoutedEventArgs e)
{
m = new Mutex(false, "KeepOff");
bgw = new Thread(new ThreadStart(bgw_DoWork));
bgw.Start();
try
{
Thread.Sleep(500);
m.WaitOne();
Thread.Sleep(500);
}
catch (Exception ex)
{
}
m.ReleaseMutex();
}

void bgw_DoWork()
{
m.WaitOne();
Thread.Sleep(5000);
Thread.Sleep(0);

}


Surprisingly, when the thread exits, the Mutex is acquired by the code in the try block, and no exception is raised.  I also tested the behavior in my ScheduledAgent, and in that circumstance, the Mutex is also released when the thread is terminated.


Eventually, I did find a reference to the behavior on the phone, but rather than being on WaitOne where I thought it belonged, it’s here: ReleaseMutex Method


Now , I wouldn’t recommend being this sloppy with your Mutex objects, but it looks like for now at least we can assume WP7 will clean up after us.


Peter Torr points out WaitOne might be dangerous, particularly in light of FAS (Fast Application Switching) if it doesn’t include a timeout.  I’ll look into that more in another post.

Thursday 15 December 2011

ScheduledAgent on WP7 and Asynchronous Events

While working on my LiveTiles, I’ve had a few suggestions that I should wait for an event to occur on loading images.  That’s actually not the problem from what I can tell, but it raised an interesting question.

Let’s say that that I implement my OnInvoke like this:

protected override void OnInvoke(ScheduledTask task)
{
MyEventRaisingObject obj = new MyEventRaisingObject();
obj.SomeAsynchronouslyRaisedEvent += (sender, e) =>
{
// do some work to handle the event, and update our tile
// for example, fetch an xml feed asynchronously
}
NotifyComplete();
}


It looks to me like the OnInvoke will return before my event gets a chance to execute, or in any case I’ll hit my NotifyComplete call.

 

How does one effectively spin until an event has been raised without blocking the thread the object was created on?  Sure I can do this:

 


AutoResetEvent ev1 = new AutoResetEvent(false);
AutoResetEvent ev2 = new AutoResetEvent(false);

...

WaitHandle.WaitAll(new WaitHandle[] { ev1, ev2});

But won’t that block the thread we’re on?

-- update 16 dec --


Don’t even think about using the AutoResetEvent trick above. 


I tried it to wait for my images to be loaded into a BitmapSource, but it seems that the load is done on the UI thread, and as a result, I block the thread waiting for an event to be raised to signal my AutoReset, so that the event can never be raised.

Dynamically generating WP7 Live Tiles

I’ve been struggling to add a generic mechanism for generating live Tiles to my applications.

My requirements are:

  • Save to png format (so I can have a transparent background to always match the users theme)
  • Generate the tile based on a XAML layout.

Saving to PNG

I managed to waste a day creating my own mashup of various source code on line to write PNG’s from a WriteableBitmap.  I started out with some code from Joe Stegman's blog, but hadn’t noticed that I had to fill the EditableImage.  After a day’s rewriting, I managed to build a version that could save an uncompressed png from a WriteableBitmap.  Unfortunately, that produced a LiveTile image of 115KB, which I thought was using too many resources on the phone, just to flip a tile.  And that’s the cost for each side.

I then looked into the .NET ImageTools on CodePlex.  After downloading the source, I couldn’t find some of the source for the referenced DLLs, particularly the gzip libraries.  To make matters worse, it isn’t clear what the license actually is for that component, so after wasting several hours I decided to abandon it, and roll my own compressor from the DotNetZip project on Codeplex.

Finally success after inadvertently using the DeflateStream instead of the GZipStream to write, and now my tiles are down to around 8KB each.

Generating the Bitmap in the first place

As I said above, my second requirement was to generate the bitmap from XAML.

This work in progress class does all of it:

public static class TileExtensions
{
public static readonly string tiledirectory = @"Shared/ShellContent/tiles";
public static IsolatedStorageFile _appstore;

public static IsolatedStorageFile ISO
{
get
{
// lazy open
if (_appstore == null)
{
_appstore = IsolatedStorageFile.GetUserStoreForApplication();
}
return _appstore;
}

}

public static WriteableBitmap GetWriteableBitmapTile(this UIElement item)
{
item.Arrange(new Rect(0, 0, 173, 173));
WriteableBitmap wbmp = new WriteableBitmap(item, null);

return wbmp;
}

public static string GetImageTilePath(string name)
{
return tiledirectory + @"/" + name + ".png";
}

public static Uri GetImageTileUri(string fullpath)
{
return new Uri("isostore:"+fullpath);
}


public static string SaveImageTile(this UIElement tile, string name)
{
WriteableBitmap bmp = tile.GetWriteableBitmapTile();

string fullpath = GetImageTilePath(name);
if (!ISO.DirectoryExists(tiledirectory))
{
ISO.CreateDirectory(tiledirectory); // guarantee our directory exists
}

using (var stream = ISO.OpenFile(fullpath, System.IO.FileMode.OpenOrCreate))
{
bmp.SavePng(stream);
}

return fullpath;
}
}

 

 

 

 


 

The idea then is to load up some XAML as a UserControl, set it’s DataContext, and then render it like this:
var toRender = new MyUserControl;
toRender.DataContext = MyDataContext;
string tilepath = g.SaveImageTile("tilename");
ShellTile primary = ShellTile.ActiveTiles.First();
StandardTileData tile = new StandardTileData()
{
BackBackgroundImage = TileExtensions.GetImageTileUri(tilepath)
};
primary.Update(tile);


This would be a joy to update a tile.  And it all appears to work great.  The databinding works, the control renders.


BUT none of the images from the XAML render.


At the moment, I’ve had to force the loading of the images from my UserControl derived items’ constructor like this:


BitmapImage bmi = new BitmapImage();
bmi.CreateOptions = BitmapCreateOptions.None;
StreamResourceInfo streamInfo =
Framework.App.GetResourceStream(
new Uri(@"images\img1.png", UriKind.Relative));
bmi.SetSource(streamInfo.Stream);
img1.Source = bmi;


I’ve posted a question on the WP7 Forums, to see what suggestions people might come up with.


A solution:


After much experimentation, and rewriting, the magic came down to subscribing to the LayoutUpdated event of a FrameworkElement.
When the LayoutUpdated event is fired, it appears that the images are guaranteed to be loaded. From that point on you can begin generating the bitmap.
However, there is no way to wait for the event to be fired, so you need to implement some kind of callback.
In the end, my call now looks something like this:

 

TileRenderer tr = new TileRenderer(
new MyUserControl() { DataContext = new MyTileInfo() });
tr.SaveImageTile("tilefilename",
(x) => {
ShellTile primary = ShellTile.ActiveTiles.First();
StandardTileData tile = new StandardTileData()
{
BackBackgroundImage = x.GetImageTileUri()
};
primary.Update(tile);
});

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 7 December 2011

Lost Exceptions inside DispatcherTimer Tick Handler

I have been using LittleWatson in my WP7 apps to catch unhandled exceptions and report them back to me by email if the user so chooses.

However, I found that I had a stack trace on Marketplace but no report.  I thought it might be because the user hadn’t sent the message, so instead forced the exception I was guessing I had an OutOfMemory exception, as the stack trace had a LoadJpeg call in it for a 1600x800 image.

So I added this line:

throw new OutOfMemoryException()

To my surprise, LittleWatson didn’t catch it.  So I threw the same exception at various other places in my code, and LittleWatson did catch it.

What I discovered (In 7.10.7720.68 at least) is that OutOfMemoryExceptions are swallowed, and appear to terminate your application inside a DispatcherTimer Tick Handler.

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

Where did my resources go

I have been trying to understand the correct way to set my DataContext.  I have been doing it in code behind, but have been looking for a better way to do it with DataBinding.

Yesterday, I tried adding it to my local resources in my App() constructor, like this:

Resources.Add("MyGlobalViewModel", mgvm);



My application also had some resources defined in a ResourceDictionary in it’s own xaml file.


Unfortunately, I chose to add my resources in code before I called InitializeComponent(), and as a result, my additions were removed.


Once I did that I was able to set my data context as


<… DataContext={Binding {StaticResource MyGlobalViewModel}} … >

Tombstoning and Trial Mode for Window Phone 7

Yesterday, I attended the Windows Phone developer camp in London, courtesy of Microsoft.  I took the opportunity while there to add trial functionality to my two existing WP7 apps.

I based my code on the information found here:

http://msdn.microsoft.com/en-us/library/hh286402(v=VS.92).aspx

I naively bound some of my UI elements to be visible when the Trial Mode flag was set.  Because I was running in the debugger, and I tried to purchase the application, my app was tombstoned.  When the app is resurrected, Trial Mode defaults to true.

However, my debug version prompts as to whether to run in TrialMode or not, and as a result when the app is resurrected, it has already bound to the TrialMode flag.  Therefore you must implement INotifyPropertyChanged to broadcast the information.

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.

Friday 2 September 2011

VS2010/WiX Porting issues

Upgrading some old VS2005/Wix 3.5 projects to the new compiler raised some issues:

WiX gave some build errors described here:

http://sourceforge.net/tracker/index.php?func=detail&aid=3293788&group_id=105970&atid=642714

this was also set incorrectly in my wixproj file:

<WixToolPath>$(ProgramFiles)\Windows Installer XML v3.5\bin\</WixToolPath>

 

 

 

Saturday 6 August 2011

Battling with PDO

I’ve been having a lot of problems with error HY093, and having the wrong number of parameters.

There are some blogs around that imply that you can pass an object converted to an array to the execute function documented below.

 http://php.net/manual/en/pdostatement.execute.php

$query = $dbh->prepare(
"INSERT INTO `myTable` (ID, Val1, Val2) "
."VALUES(:ID, :Val1, :Val2)");
$query->execute((array) $crud);


This does appear to work, and seems to occasionally work when there are more fields in the object than are in the query. 

 

Generally though it does not work.  Instead bind explicitly like this.


$query->bindParam(":ID", $crud->ID);
$query->bindParam(":Val1", $crud->Val1);
$query->bindParam(":Val2", $crud->Val2);
However, the documentation above states an FALSE will be returned if you bind more parameters than specified.

Friday 5 August 2011

Json serialization and DateTime

Oh dear.  You don’t want to go there.

First, go here:

http://msdn.microsoft.com/en-us/library/bb412170.aspx

Good luck.

Silverlight 4 DataForm EditEnd(ing). Your current item is not current if you use AutoCommit=true.

Stumbled onto this strange one today.  My DataForm EditEnding and EditEnded events are being caught, and I’m using that to push my data across to my web server.

Unfortunately, the CurrentItem does not point to the item you are editing, as it has been moved onto the new item you’ve navigated to.  Strangely though, the CurrentIndex is still pointing to the correct index.

However, as everything seems to be in an inconsistent state, I’ve instead chosen to store the CurrentItem when BeginningEdit is raised.

How can I add/remove with a Silverlight 4 DataForm?

More hair pulling ensues, trying to figure out what to do to add or remove rows from the data form.

Again, after much googling, step 1 is to make the buttons visible, which you can do like this:

<toolkit:DataForm CommandButtonsVisibility="All" 
ItemsSource="{Binding Gigs}" ... />

where Gigs is an ObservableCollection of my items.



Alas, that now shows the add/delete buttons, but unfortunately, the add button in my case still wasn’t enabled.


The cause?  For the add button to work, you must ensure that your item has a constructor that takes no parameters!

Thursday 4 August 2011

Binding a DataGrid Column to an ComboBox containing an enumeration

In WPF it’s quite easy to bind a combo box to an enumerated type.  Unfortunately in Silverlight, I found myself in for a world of pain.

In the end, I found the following solution (starting off from this blog)

First, we need to collect up all of the names in our enumerated type.  We due this by using Reflection.

public static ObservableCollection<T> walkEnum<T>()
{
Type t = typeof(T);

var v = new ObservableCollection<T>() ;
// Retrieve the info for the type (it'd be nice to use Enum.GetNames here but alas, we're stuck with this)
FieldInfo[] infos;
infos = t.GetFields(BindingFlags.Public | BindingFlags.Static);

// Add each proper enum value to the collection
foreach (FieldInfo fi in infos)
{
v.Add((T)Enum.Parse(t, fi.Name, true));
}

return v;
}


The static method above returns an observable collection of all of the individual public named values in an enumerated type, by walking over the fields, getting the name, and then parsing the name back into the enumerated type itself.


Next we need to define our editing template in our DataGrid.


<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Instrument, Mode=TwoWay}"
ItemsSource="{Binding Source={StaticResource Sections}}"/>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>


The key here is the binding to a StaticResource.  I initially tried to bind to a property of an element name, but because this is in a data template, the LogicalTree is yet available, and the element fails to bind.


Finally, there’s the matter of setting the static resource.  Rather than create a new object so I could construct it in XAML, I decided it would be better to dynamically add the object to the StaticResources in the code behind.  Here’s how I did it:


First, we define our enum.


[DataContract(Name = "Section", Namespace = "YourNamespaceHere")]
public enum Section
{
[EnumMember] Conductor = 1,
[EnumMember] Soprano,
[EnumMember] TuttiCornets,
[EnumMember] RepianoCornets
// ...
}






Notice that the enum starts with 1.  That’s something else I found out along the way on this project.  MySQL database enums reserve 0 as an undefined value, but that will have to wait for another blog entry.


Next, we need to create our collection to be used as the ItemsSource as the first line of our objects constructor.


this.Resources.Add("Sections", walkEnum<Section>());

 

And there you have it.  A combo box bound to enums in a Silverlight 4 DataGrid.

Customizing the Silverlight 4 DataForm

I’ve been working on a web based project in my spare time, and was trying to edit an object downloaded from my web server.

In the end, I eventually found that the DataForm from the Silverlight toolkit should make it easy to display the data in my object.  So, Initially, I let it wire up to my object like this:

<toolkit:DataForm Grid.Column="1"          
Name="dataForm1" ItemsSource="{Binding Gigs}"
CurrentItem="{Binding ElementName=dataGridGigs,
Path=SelectedItem, Mode=TwoWay}"
/>




 

Unfortunately, the Gigs object had a member declared like this:

 

public DateTime StartTime { get; set; }


which generated a default field editor of a date picker, which didn’t allow me to set the time!  Unfortunately, the time is just as important as the date in my application.


I searched around the web for awhile to find out how to customize the DataForm, but unfortunately most of the references were for Silverlight 3, and there have been many breaking changes since then.


So, I dug out my copy of Silverlight 4 unleashed and found the Section in Chapter 8, on page 212 that specifies how to define the fields manually.  Alas, the description warns off the user, saying that the validation summary won’t appear.


Sure enough, the code included didn’t show the validation summary as advertised.


Nevertheless, I persisted.


<toolkit:DataForm Grid.Column="1"  
Name="dataForm1" AutoGenerateFields="False"
ItemsSource="{Binding Gigs}"
CurrentItem="{Binding ElementName=dataGridGigs,
Path=SelectedItem, Mode=TwoWay}"
>
<toolkit:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<!-- ... -->
<toolkit:DataField Label="StartTime">
<TextBox Text="{Binding Path=StartTime,

Mode=TwoWay}"
/>
</toolkit:DataField>
</StackPanel>
</DataTemplate>
</toolkit:DataForm.EditTemplate>
</toolkit:DataForm>




Suddenly, not only did validation appear, but my data was also updated in the object.


The key is setting the TextBox databinding to be TwoWay!