Windows Phone 7, MVVM and TDD (Part 5 – creating the basic UI)
by Peter Daukintis
Okay, so we haven’t touched on any of the user interface yet but this supports the theory that we should be able to develop the user interface independently of the code. So, these are the areas we need to create for the user interface:
- Create the user name and password inputs
- Create a way to execute the login command
- Create a busy indicator UserControl
- Create a transition to a welcome screen after successfully logging in </ul>
I am going to use Expression Blend 4 RC for this part of the post as I can work more quickly inside Blend – also you will need Microsoft Expression Blend Add-in Preview 2 for Windows Phone and Microsoft Expression Blend Software Development Kit Preview 2 for Windows Phone all of which can be downloaded here http://www.microsoft.com/expression/windowsphone/.
Opening the solution up in Expression Blend looks like this:
So, let’s start by dragging some input controls and a button:
<TextBlock Text="{Binding Welcome}" Style="{StaticResource PhoneTextNormalStyle}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="30" Margin="0,592,-12,6" /> <Button Content="{Binding LoginButtonName}" Margin="187,239,7,0" VerticalAlignment="Top" Height="114"> </Button> <TextBox Height="31" HorizontalAlignment="Left" Margin="137,57,0,0" Text="{Binding UsernameText, Mode=TwoWay, UpdateSourceTrigger=Explicit}" VerticalAlignment="Top" Width="336"> </TextBox> <PasswordBox Height="31" HorizontalAlignment="Left" Margin="137,144,0,0" Password="{Binding PasswordText, Mode=TwoWay, UpdateSourceTrigger=Explicit}" VerticalAlignment="Top" Width="336" PasswordChar="*"> </PasswordBox> <TextBlock HorizontalAlignment="Left" Margin="18,79,0,0" Text="{Binding UsernameLabel}" VerticalAlignment="Top" TextAlignment="Right" /> <TextBlock HorizontalAlignment="Left" Margin="33,167,0,0" Text="{Binding PasswordLabel}" VerticalAlignment="Top" />
and adding some additional properties to our view model:
public string ApplicationTitle { get { return "Application"; } } public string ListName { get { return "Login:"; } } public string LoginButtonName { get { return "Login"; } } public string Welcome { get { return "Welcome to My Application"; } } public string UsernameLabel { get { return "User Name :"; } } public string PasswordLabel { get { return "Password :"; } }
results in the following user interface (the view model is already bound to the DataContext of the page using the MVVM Light ViewModelLocator class):
So, the text input parameters are bound we need to bind the click from the login button to the Login Command exposed by the MainViewModel. We will wire the button up using the EventToCommand behavior included in the MVVM Light framework. Silverlight behaviors allow some functionality to be associated with a FrameworkElement by dragging it from the toolbox onto the element in Expression Blend. To achieve this carry out the following:
- First install and reference the assemblies here http://blog.galasoft.ch/archive/2010/04/03/mvvm-light-toolkit-v3-sp1-for-windows-phone-7.aspx as the Silverlight behaviors are not available in Blend without these.
- Open the Assets panel in Blend, click on behaviors – you should see the EventToCommand behavior.
from here you can see all of the view model properties and can select the LoginCommand property to complete the data-binding….nice.
- Set the MustToggleIsEnabled to true as we want to automatically disable the button if the command is unavailable and also set the Event Name to Click if it isn’t already.
Unfortunately, this is not enough for the behaviour I was looking for since in Silverlight there is no option to set the UpdateSourceTrigger to fire when the property changes, the best we can do is when the control loses focus. As a result the login button does not disable/enable when the fields are empty – until focus is lost from the input controls. To remedy this I have added some custom behaviors:
namespace WP7Login.Behaviors { public class DataBindTextBoxOnPropertyChangedBehaviour : Behavior<TextBox> { public BindingExpression BindingExpression { get; set; } protected override void OnAttached() { base.OnAttached(); BindingExpression = AssociatedObject.GetBindingExpression(TextBox.TextProperty); AssociatedObject.TextChanged += AssociatedObject_TextChanged; } protected override void OnDetaching() { base.OnDetaching(); BindingExpression = null; } private void AssociatedObject_TextChanged(object sender, TextChangedEventArgs e) { if (BindingExpression != null) { BindingExpression.UpdateSource(); } } } }
and a similar one aimed at a password box. Once recompiled these will be available via the Assets panel in Blend and can be dragged onto the user name input and the password input boxes appropriately.
Also, CanExecute in MainViewModel must be implemented
private bool CanExecuteLogin() { return UsernameText.Length > 0 && PasswordText.Length > 0; }
And also the RaiseExecuteChanged event must be raised in response to the data in the text boxes changing – this can be achieved by the following:
PropertyChanged += MainViewModel_PropertyChanged;
Subscribe to the property changed event on the view model with this handler:
void MainViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "UsernameText" || e.PropertyName == "PasswordText") { var relayCommand = LoginCommand as RelayCommand; if (relayCommand != null) { relayCommand.RaiseCanExecuteChanged(); } } }
Finally we get the required behaviour – if either username text box or the password box is empty the login button is disabled otherwise it is enabled as we type.
So, that’s the basic user interface in place – next we move to the loading screen and the transition when logged in….
Windows Live Tags: MVVM,Part,Peter,Daukintis,Okay,haven,user,interface,supports,theory,code,Create,password,indicator,UserControl,transition,Expression,Blend,Microsoft,Preview,Software,Development,solution,TextBlock,Text,Welcome,Style,StaticResource,PhoneTextNormalStyle,HorizontalAlignment,Center,VerticalAlignment,FontSize,Margin,Button,Content,LoginButtonName,TextBox,Left,UsernameText,Mode,TwoWay,UpdateSourceTrigger,Explicit,Width,PasswordBox,PasswordText,PasswordChar,UsernameLabel,TextAlignment,PasswordLabel,ApplicationTitle,Application,ListName,Login,Name,DataContext,ViewModelLocator,Command,MainViewModel,wire,EventToCommand,framework,FrameworkElement,element,reference,archive,Open,Assets,panel,Drag,Objects,Timelines,pane,Click,data,LoginCommand,Event,behaviour,option,result,custom,Behaviors,DataBindTextBoxOnPropertyChangedBehaviour,Behavior,BindingExpression,AssociatedObject,GetBindingExpression,sender,TextChangedEventArgs,UpdateSource,Once,Also,CanExecute,CanExecuteLogin,Length,response,Subscribe,handler,PropertyChangedEventArgs,PropertyName,RelayCommand,areas,parameters,options,boxes </div>
Comments