Windows Phone 7 TextBox, binding and ApplicationButton

Consider a Windows Phone 7 application with a textbox and an ApplicationButton.

TextBox and ApplicationButton

UI:

  1. <Canvas x:Name="ContentGrid" Grid.Row="1" Height="545" Width="480">
  2.     <TextBlock Text="Enter text here" Canvas.Left="6" Canvas.Top="187" />
  3.     <TextBox x:Name="myTextBox" Text="{Binding Test, Mode=TwoWay}" Height="69" Width="480" Canvas.Left="0" Canvas.Top="209" />
  4. </Canvas>

Backend:

  1. namespace WindowsPhoneApplication1
  2. {
  3.     using System;
  4.     using System.ComponentModel;
  5.     using System.Windows.Controls;
  6.     using Microsoft.Phone.Controls;
  7.  
  8.     public partial class MainPage : PhoneApplicationPage, INotifyPropertyChanged
  9.     {
  10.         private string _test;
  11.         // Constructor
  12.         public MainPage()
  13.         {
  14.             this.InitializeComponent();
  15.  
  16.             this.DataContext = this;
  17.         }
  18.  
  19.         public string Test
  20.         {
  21.             get
  22.             {
  23.                 return this._test;
  24.             }
  25.             set
  26.             {
  27.                 if (value != this._test)
  28.                 {
  29.                     this._test = value;
  30.                     this.PropertyChanged(this, new PropertyChangedEventArgs("Test"));
  31.                 }
  32.             }
  33.         }
  34.  
  35.         #region INotifyPropertyChanged Members
  36.  
  37.         public event PropertyChangedEventHandler PropertyChanged;
  38.  
  39.         #endregion
  40.  
  41.     }
  42. }

The textbox is bound to a property on the backend (two way binding) and when you click the bottom button the text is saved/send/encrypted/whatever.

Now the problem is that clicking the button doesn’t do a UI –> source binding update as it would do with a normal button.

Testing the app

I enter the text ‘test’ on the TextBlock and IMMIDIATLY click the ApplicationButton. I don’t do anything else. This is a common user practice, he changes something and clicks the save/send/whatsoever button.

This is the result:

Visual Studio 2010 Watch 1

Check the Watch 1. As you can see the value is still null. The value is not sent to the _test value. How do we solve this?

You can add the following line to the click event handler:

  1. this.myTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();

So the result will look like this:

  1. private void AppbarButton1Click(object sender, EventArgs e)
  2. {
  3.     this.myTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
  4.  
  5.     // handle save/send/encrypt/whatever here
  6. }

There are 3 downsides I think:

  1. You need to name your TextBlock which (I think) unnecessarily clutters your scope with otherwise unused variables (that’s why we use bindings too!)
  2. Your UI and ViewModel (in my case) aren’t decoupled 100% anymore. In Windows Phone 7 this is no issue though, since WP7 has no ICommand and I have to couply my UI and ViewModel anyway 🙂
  3. You need to remember to write this line! Which can be quite cumbersome with a lot of application buttons.

And items in a ApplicationBar.MenuItems (ApplicationBarMenuItem) have the same problem. The UI doesn’t push the update to the ViewModel.

I hope this will be fixed in the final version, and I will post this to Microsoft Connect. If anybody has a better solution please feel free to share it.

A more general INotifyPropertyChangedHandler extension method for strongly typed reflection

  1. static class ExtensionMethods
  2. {
  3.     public static void Raise<TValue>(this PropertyChangedEventHandler handler, Expression<Func<TValue>> propertySelector)
  4.     {
  5.         var memberExpression = propertySelector.Body as MemberExpression;
  6.  
  7.         if (handler == null || memberExpression == null)
  8.         {
  9.             return;
  10.         }
  11.  
  12.         var sender = ((ConstantExpression)memberExpression.Expression).Value;
  13.         handler(sender, new PropertyChangedEventArgs(memberExpression.Member.Name));
  14.     }
  15. }

Use this as an extension method like this on a class that implements INotifyPropertyChanged

  1. class SomeClass : INotifyPropertyChanged
  2. {
  3.     private string _someProperty;
  4.  
  5.     public string SomeProperty
  6.     {
  7.         get
  8.         {
  9.             return this._someProperty;
  10.         }
  11.         set
  12.         {
  13.             if (value != this._someProperty)
  14.             {
  15.                 this._someProperty = value;
  16.                 this.PropertyChanged.Raise(() => this.SomeProperty);
  17.             }
  18.         }
  19.     }
  20.  
  21.     public event PropertyChangedEventHandler PropertyChanged;
  22. }

Good luck 🙂

-Kristof

MvvmLight: strongly typed INotifyPropertyChanged

Since MvvmLight exposes it’s own ViewModelBase you cannot use this little script to raise the INotifyPropertyChangedEventHandler, because .NET doesn’t allow you to raise events in derived classes. This is because the compiler generates a private delegate (it’s in VB.NET but that doesn’t matter).

And since the ViewModelBase only exposes a protected RaiseProperty(string propertyName) I cannot use strongly typed reflection.

So I found some code, and rewrote it like this.

Now you can create a class that inherits from ViewModelBase, and make your ViewModels inherit from your just created class.

Now you have a pattern like this:

ViewModelBase <—DefaultViewModel <—MainPageViewModel

And your DefaultViewModel should look like this:

  1. public abstract class DefaultViewModel : ViewModelBase
  2. {
  3.     protected void RaisePropertyChanged<TValue>(Expression<Func<TValue>> propertySelector)
  4.     {
  5.         var memberExpression = (propertySelector.Body as MemberExpression);
  6.  
  7.         if (null != memberExpression)
  8.         this.RaisePropertyChanged(memberExpression.Member.Name);
  9.     }
  10. }

And your MainPageViewModel (for example):

  1. public class MainPageViewModel : DefaultViewModel
  2. {
  3.     private decimal _remainingCredits;
  4.  
  5.     /// <summary>
  6.     /// Remaining credits of the current account
  7.     /// </summary>
  8.     public decimal RemainingCredits
  9.     {
  10.         get
  11.         {
  12.             return this._remainingCredits;
  13.         }
  14.  
  15.         set
  16.         {
  17.             if (this._remainingCredits != value)
  18.             {
  19.                 this._remainingCredits = value;
  20.  
  21.                 this.RaisePropertyChanged(() => RemainingCredits);
  22.             }
  23.         }
  24.     }
  25. }

You can use the form of RaisePropertyChanged(() => NameOfProperty);

This way, when you rename a variable in your code the refactor engine picks it up and renames it accordingly. (it forget strings 🙂

Good luck,

Kristof

Winforms: Cross-thread operation not valid.

Hi all,

I sometimes get this error when I forget that controls cannot be accessed across threads:

Cross-thread operation not valid: Control 'someLabel' accessed from a thread other than the thread it was created on.

What many people do is this:

  1. private void UpdateLabel(string text)
  2. {
  3.     this.Invoke(new Action(() => this.someLabel.Text = text));
  4. }

This is not the right pattern. You don’t always HAVE to invoke. You are better off by checking if you have to invoke it on the underlying thread, and if not, just execute it directly. It’s all about correctness 🙂

The right pattern would be:

 

  1. private void UpdateLabel(string text)
  2. {
  3.     if(this.InvokeRequired)
  4.     {
  5.         this.Invoke(new Action(() => this.UpdateLabel(text)));
  6.         return;
  7.     }
  8.  
  9.     this.someLabel.Text = text;
  10. }

Use Control.InvokeRequired 🙂

Good luck

-Kristof

LINQ 2 SQL and WCF, the definitive guide

In my last project I had the assignment to create a client server application. Not that hard. We chose to use WCF for the communication itself, because that was easy, straightforward and well supported with .NET 3.5SP1 (and Visual Studio 2008 of course).

As we didn’t have the time to create our own mapper to map the database to code, nor did we have time to study another mapper (nHibernate came to the question) we choose for something we already knew. LINQ 2 SQL.

Now we all used it in a connected environment. But as you all know, WCF is not connected. The DataContext is lost upon transmitting the entity to the client.

Now that’s the problem, change tracking is lost and LINQ 2 SQL doesn’t play well with reattaching detached entities.

Transmitting an entity from server to the client was no problem, with the DataContext.LoadOptions we were able to eager load all the entities required and transmit them to the client. And we didn’t want to use a third party template or baseclass because those weren’t approved by management 🙂 .

In order to make LINQ to SQL add DataMember and DataContract properties to your classes you need to set the Serialization Mode to Unidirectional (as shown below):

Serialization Mode: Unidirectional

In code you can now see attributes like this:

DataMemberAttribute

and this:

DataContractAttribute

But the DataMemberAttribute is not applied on EntityRef, only EntitySet.

EntityRef is used to lazy-load a reference to 1 entity. EntitySet is used for a list of lazy-loadable entities.

Let me explain with an example (I’m using the Northwind database).

A Product has 1 category and a Category has multiple Products.

This means that a Category has an EntitySet<Product> (as shown below, notice the DataMemberAttribute):

Category -> EntitySet<Product>

While a Product has only 1 Category (notice the absence of the DataMemberAttribute):

image

When working in a connected environment the absence of those attributes is not a problem. But on WCF it is. Since you cannot do lazy loading when the entity is transmitted to the client you need to make sure you load everything in front.

While you can create DataLoadOptions for a Product.Category link the absence of the DataMemberAttribute prevents the WCF serializer from serializing the Category Entity.

  1. using (NorthwindDataContext northwindDataContext = new NorthwindDataContext())
  2. {
  3.     var dataLoadOptions = new DataLoadOptions();
  4.  
  5.     dataLoadOptions.LoadWith<Product>(p => p.Category);
  6.  
  7.     northwindDataContext.LoadOptions = dataLoadOptions;
  8.  
  9.     return northwindDataContext.Products.Where(p => p.ProductID == 1).SingleOrDefault();
  10. }

When invoking this method on the client you will see that the received Product’s Category is null.

First of all: why is there no DataMemberAttribute on the EntityRef in LINQ2SQL?

Imagine me having a Category, and eager (opposite of lazy) loading all the Product entities. All those products have 1 category, and they if I set the Product.Category to eager load… think about it, this will cause an infinite loop:

Category –> x Products, for each of those products load their category, for each of those categories, load their products and so on.

Boom, the WCF XML serializer cannot handle this.

How do we solve this?

In the LINQ2SQL designer take the Product.Category (or Category.Products if you will) link, and set the Child Property to false.

Child Property

This will remove the Category.Products property and generate a DataMemberAttribute on the Product.Category. Of course now you cannot get all the products in a category through a Category link this:

Category.Products is now gone

But you have to do something like this:

The right way, through the foreign key

This generates the same SQL statement on the backend and now you can properly sent those entities to your client.

Signing off,

Kristof

Windows Phone 7, adding and displaying icons.

Hi all,

I tried to display an icon in my Windows Phone 7 application:

<shell:ApplicationBarIconButton x:Name="appbar_button1" IconUri="/Images/appbar.feature.settings.rest.png" Text="Settings" />

By default, when you add an image to the solution folder it sets the build action as Resource, as shown below:

Build Action Resource

But when you run the application with the Build Action as Resource you will get a result looking like this:

Wrong icon

While I really meant an icon looking like this:

Gear

How do we fix it?

Set the Build Action to Content!

Build Action Content

And this is the result:

Result