This walkthrough shows how to create to create the contact editing form to perform save action using MVVM pattern.
In this walkthrough, you perform the following tasks:
- Create a ClientUI MVVM project using Intersoft ClientUI MVVM Application project template.
- Create the Model class that represents the Contact data entity.
- Create the View page that represents the user interface of the application.
- Create the ViewModel class that describes the View and provides the commands for save action.
- Create DelegateCommand for save action.
- Bind the properties of UI controls in the View, such as the Command property of the UXToolBarButton to the ViewModel.
Prerequisites
You need the following components to complete this walkthrough:
- Visual Studio 2010
- Silverlight 4 Tools for Visual Studio 2010
- Intersoft ClientUI
Creating a new ClientUI MVVM Project
The first step is to create a new ClientUI MVVM Application using Intersoft ClientUI MVVM Application project template in Visual Studio.
To create MVVM Application project
- Start Visual Studio 2010.
- Create a new Silverlight MVVM project using Intersoft ClientUI MVVM Application project template. To learn more, see Walkthrough: Create New Intersoft ClientUI MVVM Application Template.
- Add Data Folder to the Silverlight project.
- Add the ContactDataSource.xml (available in [Intersoft Installation Folder]\Intersoft WebUI Studio 2010 R1\Samples\SL4\ClientUI 2010\Contacts_MVVM\SampleData) to the Silverlight project Data folder.
- Click on the ContactDataSource.xml file and press F4 to open the Property Window. Change the Build Action property to Resources.
- Add Assets folder to the Silverlight project.
- Add Icons folder that contains the icon's image from [Intersoft Installation Folder]\Intersoft WebUI Studio 2010 R1\Samples\SL4\ClientUI 2010\Contacts_MVVM\Assets to the Silverlight project Assets folder.
- Add Photos folder that contains contact's photo image from [Intersoft Installation Folder]\Intersoft WebUI Studio 2010 R1\Samples\SL4\ClientUI 2010\Contacts_MVVM\Assets to the Silverlight project Assets folder.
- Add System.Xml.Linq.dll as the silverlight project's reference. The System.Xml.Linq is required to perform LINQ query against XML data.
Creating Model Class
This section shows how to create the Contact model class that represents the data entity used in this walkthrough. The Contact model class contains methods such as SetPhoto to assign value to Photo property.
To create the StateCode Enum Class
- Create StateCode enum class. Add a new class to Models folder and named it State.cs.
C# Copy Code
namespace ClientUIMVVMApp2.Models { public enum StateCode { AL, // Alabama AK, // Alaska AS, // American Samoa AZ, // Arizona AR, // Arkansas CA, // California CO, // Colorado CT, DE, DC, FL, GA, GU, HI, ID, IL, IN, IA, KS, KY, LA, ME, MD, MH, MA, MI, FM, MN, MS, MO, MT, NE, NV, NH, NJ, NM, NY, NC, ND, MP, OH, OK, OR, PW, PA, PR, RI, SC, SD, TN, TX, UT, VT, VA, VI, WA, WV, WI, WY, } }
To create EnumDataProvider Class
This class is used as the StateCode enum collection.
- Create EnumDataProvider class for StateCode enum. Put it in Converter folder.
C# Copy Code
public abstract class EnumDataProvider<T> { IEnumerable _data; public IEnumerable Data { get { if (this._data == null) { Type enumType = typeof(T); List<object> list = new List<object>(); int value = 0; while (Enum.IsDefined(enumType, value)) { list.Add(Enum.ToObject(enumType, value)); value++; } this._data = list; } return this._data; } } } public sealed class StateCodeDataProvider : EnumDataProvider<StateCode> { }
To create the Contact Model Class
- Create a model class that inherit from ModelBase class under the Models folder, named it Contact.cs.
- Add Id property to the Contact model class by defining the backing field along with complete getter and setter in the property. In the setter property, you need to call the OnPropertyChanged method after the backing field is assigned to the new value.
C# Copy Code
private string _id; public string Id { get { return this._id; } set { if (this._id != value) { this._id = value; this.OnPropertyChanged("Id"); } } }
- Also add Name, Email, Phone, Fax, Cell, Address, ZipCode, Blog, Twitter and Website property to the Contact model class by repeating step number 2.
C# Copy Code
private string _name; private string _email; private string _phone; private string _fax; private string _cell; private string _address; private string _zipCode; private string _blog; private string _twitter; private string _website; public string Name { get { return this._name; } set { if (this._name != value) { this._name = value; this.OnPropertyChanged("Name"); } } } public string Email { get { return this._email; } set { if (this._email != value) { this._email = value; this.OnPropertyChanged("Email"); } } } public string Phone { get { return this._phone; } set { if (this._phone != value) { this._phone = value; this.OnPropertyChanged("Phone"); } } } public string Fax { get { return this._fax; } set { if (this._fax != value) { this._fax = value; this.OnPropertyChanged("Fax"); } } } public string Cell { get { return this._cell; } set { if (this._cell != value) { this._cell = value; this.OnPropertyChanged("Cell"); } } } public string Address { get { return this._address; } set { if (this._address != value) { this._address = value; this.OnPropertyChanged("Address"); } } } public string ZipCode { get { return this._zipCode; } set { if (this._zipCode != value) { this._zipCode = value; this.OnPropertyChanged("ZipCode"); } } } public string Blog { get { return this._blog; } set { if (this._blog != value) { this._blog = value; this.OnPropertyChanged("Blog"); } } } public string Twitter { get { return this._twitter; } set { if (this._twitter != value) { this._twitter = value; this.OnPropertyChanged("Twitter"); } } } public string Website { get { return this._website; } set { if (this._website != value) { this._website = value; this.OnPropertyChanged("Website"); } } }
- Add StateCode property.
C# Copy Code
private StateCode _state; public StateCode State { get { return this._state; } set { if (this._state != value) { this._state = value; this.OnPropertyChanged("State"); } } }
- Add Photo property.
C# Copy Code
private Uri _photo; public Uri Photo { get { return _photo; } set { if (this._photo != value) { _photo = value; OnPropertyChanged("Photo"); } } }
- Add Constructors to the Contact model class.
C# Copy Code
private Contact(string id, string name, string email, string address, StateCode state, string zipCode, string phone, string cell, string fax, string blog, string twitter, string website, Uri photo) { this._id = id; this._name = name; this._email = email; this._address = address; this._state = state; this._zipCode = zipCode; this._phone = phone; this._cell = cell; this._fax = fax; this._blog = blog; this._twitter = twitter; this._website = website; this._photo = photo; } public Contact() : this(id: string.Empty, name: string.Empty, email: string.Empty, address: string.Empty, state: StateCode.AK, zipCode: string.Empty, phone: string.Empty, cell: string.Empty, fax: string.Empty, blog: string.Empty, twitter: string.Empty, website: string.Empty, photo: null) { }
Creating the View
This section steps you through the process of creating a page that uses a variety of ClientUI controls such as UXWindow, UXComboBox, ContentReflector, and ExpandableGroupBox. The UXComboBox is used to display a collection of StateCode, while the UXToolBarButton is used to execute the save and cancel action.
To Create the View
- Right click on Views folder, and add new item. Select Intersoft UXWindow as the template page. Name it EditClientProfile.xaml then click Add.
- Create Intersoft:UXWindow.PrimaryToolBarTemplate and put it before the Grid layout.
In this steps, two UXToolBarButton will be created such as shown in the following sample.
XAML Copy Code
<Intersoft:UXWindow.PrimaryToolBarTemplate> <DataTemplate> <Grid> <Intersoft:UXToolBar Intersoft:DockPanel.Dock="Top" OverflowHandleVisibility="AsNeeded" Margin="0,8,0,0" GripHandleVisibility="Collapsed" HorizontalAlignment="Left"> <Intersoft:UXToolBar.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#90FFFFFF" Offset="0"/> <GradientStop Color="#00B1D0E3" Offset="1"/> </LinearGradientBrush> </Intersoft:UXToolBar.Background> <Intersoft:UXToolGroup> <Intersoft:UXToolBarButton Content="Save" DisplayMode="Content" ToolTipService.ToolTip="Save Changes"/> <Intersoft:UXSeparator/> <Intersoft:UXToolBarButton Content="Cancel" DisplayMode="Content" ToolTipService.ToolTip="Cancel Changes (Not implemented in this sample)"/> </Intersoft:UXToolGroup> </Intersoft:UXToolBar> </Grid> </DataTemplate> </Intersoft:UXWindow.PrimaryToolBarTemplate>
- Add Grid layout inside the LayoutRoot.
XAML Copy Code
<Grid x:Name="LayoutRoot"> <Grid> </Grid> </Grid>
- Add DockPanel control inside the Grid layout. Clear the Intersoft DockPanel default properties and then set the FillChildMode to Custom.
XAML Copy Code
<Grid x:Name="LayoutRoot"> <Grid> <Intersoft:DockPanel FillChildMode="Custom"> </Intersoft:DockPanel> </Grid> </Grid>
- Add ContentReflector and set the following properties.
Property Value Width 160 HorizontalAlignment Left VerticalContentAlignment Bottom Margin 12
XAML Copy Code
... <Intersoft:ContentReflector Name="contentReflector1" Width="160" HorizontalAlignment="Left" VerticalContentAlignment="Bottom" Margin="12"> <Grid /> </Intersoft:ContentReflector> ...
- Remove the Grid layout inside ContentReflector and replace it with Image control.
XAML Copy Code
... <Intersoft:ContentReflector Name="contentReflector1" Width="160" HorizontalAlignment="Left" VerticalContentAlignment="Bottom" Margin="12"> <Image /> </Intersoft:ContentReflector> ...
- Add StackPanel control after ContentReflector. Set Intersoft:DockPanel.Dock to Right and Intersoft:DockPanel.IsFillElement to True.
XAML Copy Code
... <StackPanel Intersoft:DockPanel.Dock="Right" Intersoft:DockPanel.IsFillElement="True"> </StackPanel> ...
- Add ExpandableGroupBox control to StackPanel and set the Header to General and VerticalAlignment to Top.
XAML Copy Code
... <StackPanel Intersoft:DockPanel.Dock="Right" Intersoft:DockPanel.IsFillElement="True"> <Intersoft:ExpandableGroupBox Header="General Information" VerticalAlignment="Top" Name="expandableGroupBox1"> <Grid Height="150" Width="200" /> </Intersoft:ExpandableGroupBox> </StackPanel> ...
- Clear the content of ExpandableGroupBox. Replace it with UXItemControl and set the ItemContainerStyle to {StaticResource FieldLabelStyle}.
XAML Copy Code
... <StackPanel Intersoft:DockPanel.Dock="Right" Intersoft:DockPanel.IsFillElement="True"> <Intersoft:ExpandableGroupBox Header="General Information" VerticalAlignment="Top" Name="expandableGroupBox1"> <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> </Intersoft:UXItemsControl> </Intersoft:ExpandableGroupBox> </StackPanel> ...
- Now, create FieldLabelStyle as the UXWindow's resources. This is style that will be assiged to FieldLabel.
XAML Copy Code
... <Intersoft:UXWindow.Resources> <Style x:Key="FieldLabelStyle" TargetType="Intersoft:FieldLabel"> <Setter Property="HeaderWidth" Value="160"/> <Setter Property="HorizontalHeaderAlignment" Value="Right"/> <Setter Property="Padding" Value="2"/> <Setter Property="HeaderMargin" Value="0,0,8,0"/> </Style> </Intersoft:UXWindow.Resources> <Intersoft:UXWindow.PrimaryToolBarTemplate> ...
- Back to the UXItemsControl, Add ID field to the UXItemsControl.
XAML Copy Code
... <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:FieldLabel Header="ID:"> <Intersoft:UXTextBox Width="100" IsReadOnly="True" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> ...
- Create SimpleTextBoxStyle to the UXWindow resources. This is the style that will be assigned to UXTextBox.
XAML Copy Code
... <Intersoft:UXWindow.Resources> <Style x:Key="FieldLabelStyle" TargetType="Intersoft:FieldLabel"> <Setter Property="HeaderWidth" Value="160"/> <Setter Property="HorizontalHeaderAlignment" Value="Right"/> <Setter Property="Padding" Value="2"/> <Setter Property="HeaderMargin" Value="0,0,8,0"/> </Style> <Style x:Key="SimpleTextBoxStyle" TargetType="Intersoft:UXTextBox"> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="Width" Value="200"/> </Style> </Intersoft:UXWindow.Resources> ...
- Add FullName, Address, State, and ZipCode field by repeating step 10, but remove the IsReadOnly property. For State field, replace the UXTextBox with UXComboBox.
XAML Copy Code
... <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:FieldLabel Header="ID:"> <Intersoft:UXTextBox Width="100" IsReadOnly="True" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Full Name:"> <Intersoft:UXTextBox Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Address:"> <Intersoft:UXTextBox Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="State:"> <Intersoft:UXComboBox DropDownHeight="150" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Zip Code:"> <Intersoft:UXTextBox Width="100" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> </Intersoft:UXItemsControl> ...
- Add ExpandableGroupBox after the last ExpandableGroupBox. Set Header property to Contact Details, VerticalAlignment to Top and Margin to 0,0,2,0.
XAML Copy Code
<Intersoft:ExpandableGroupBox Header="Contact Details" VerticalAlignment="Top" Margin="0,0,2,0"> <Grid Height="150" Width="200" /> </Intersoft:ExpandableGroupBox>
- Clear the content of the ExpandableGroupBox and replace it with UXItemsControl. Set ItemContainerStyle to {StaticResource FieldLabelStyle}. Like step 8.
XAML Copy Code
... <Intersoft:ExpandableGroupBox Header="Contact Details" VerticalAlignment="Top" Margin="0,0,2,0"> <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> </Intersoft:UXItemsControl> </Intersoft:UXItemsControl> </Intersoft:ExpandableGroupBox> ...
- Add Mobile, Email and Twitter field inside the UXItemsControls by repeating step 12, but remove the IsReadOnly property.
XAML Copy Code
... <Intersoft:ExpandableGroupBox Header="Contact Details" VerticalAlignment="Top" Margin="0,0,2,0"> <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:FieldLabel Header="Mobile:"> <Intersoft:UXTextBox Width="200" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Email:"> <Intersoft:UXTextBox Width="200" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Twitter:"> <Intersoft:UXTextBox Width="200" /> </Intersoft:FieldLabel> </Intersoft:UXItemsControl> </Intersoft:UXItemsControl> </Intersoft:ExpandableGroupBox> ...
Creating the ViewModel
This section steps you through the process of creating a ViewModel class that contains the properties to describe the View that you created in the previous section. The ViewModel defines a DelegateCommand to represent the view command and logic for save action.
To create the Contact ViewModel
- Create a new ContactViewModel that inherit from ViewModelBase class by add new class to ViewModels folder and named it ContactViewModel.cs.
C# Copy Code
... namespace ClientUIMVVMApp2.ViewModels { public class ContactViewModel : ViewModelBase { // Fields private Contact _contact; // Constructor public ContactViewModel(Contact contact) { _contact = contact; } // Views public Contact Contact { get { return this._contact; } } public string EmailUri { get { return "mailto:" + _contact.Email; } } } } ...
To create the EditClientProfileViewModel
- In the ViewModels folder, add new class and named it EditClientProfileViewModel.cs. This new class will inherit ViewModelBase class. This is the class that will become the DataContext's property of View.
C# Copy Code
namespace ClientUIMVVMApp2.ViewModels { public class EditClientProfileViewModel : ViewModelBase { } }
- Start creating the EditClientProfileViewModel class. Here is the complete EditClientProfileViewModel.cs.
C# Copy Code
public class EditClientProfileViewModel : ViewModelBase { #region Constructor public EditClientProfileViewModel() { this.Contact = new Contact { Id = "JOSE", Name = "José Pedro Freyre", Address = "C/ Romero, 33", Phone = "1-843-371-8211", Cell = "1-555-123-9250", Email = "jose@fabrikam.com", Fax = "", Blog = "http://www.cpandl.com/", Twitter = "http://twitter.com/jose", Website = "http://www.fabrikam.com/", State = StateCode.GA, ZipCode = "57998", Photo = new Uri("/ClientUIMVVMApp2;component/Assets/Photos/8.jpg", UriKind.RelativeOrAbsolute) }; this.StateCodes = new StateCodeDataProvider().Data; } #endregion #region Fields private Contact _contact; #endregion #region Properties public IEnumerable StateCodes { get; private set; } public Contact Contact { get {return _contact; } set { if (_contact != value) { _contact = value; OnPropertyChanged("Contact"); } } } #endregion }
- Create DelegateCommand to EditClientProfileViewModel class.
C# Copy Code
private DelegateCommand _saveCommand = null; public EditClientProfileViewModel() { this.Contact = new Contact { Id = "JOSE", Name = "José Pedro Freyre", Address = "C/ Romero, 33", Phone = "1-843-371-8211", Cell = "1-555-123-9250", Email = "jose@fabrikam.com", Fax = "", Blog = "http://www.cpandl.com/", Twitter = "http://twitter.com/jose", Website = "http://www.fabrikam.com/", State = StateCode.GA, ZipCode = "57998", Photo = new Uri("/ClientUIMVVMApp2;component/Assets/Photos/8.jpg", UriKind.RelativeOrAbsolute) }; this.StateCodes = new StateCodeDataProvider().Data; this._saveCommand = new DelegateCommand(ExecuteSave, CanExecuteSave); }
- Create DelegateCommand SaveCommand property.
C# Copy Code
public DelegateCommand SaveCommand { get { return _saveCommand; } set { if (_saveCommand != value) { _saveCommand = value; OnPropertyChanged("SaveCommand"); } } }
- Create CanExecuteSave method. This is the method that will validate whether or not the Save UXToolBarButton is active or not.
C# Copy Code
private bool CanExecuteSave(object parameter) { return true; }
- Create ExecuteSave method. This is the method that will be triggered if the Save UXToolBarButton is clicked.
C# Copy Code
private void ExecuteSave(object parameter) { MessageBoxServiceProvider.Standalone = true; MessageBoxServiceProvider.Show("Execute Save DelegateCommand", "Information", Intersoft.Client.UI.Aqua.UXDesktop.MessageBoxButton.OK, Intersoft.Client.UI.Aqua.UXDesktop.MessageBoxImage.Information, null); }
- Specify Standalone property to MessageBoxServiceProvider.cs. In this sample, the Standalone property will always set to True so the dependency checking will be ignored.
C# Copy Code
... public static IWindow Owner { get { if (Standalone) return null; FrameworkElement element = ISFocusManager.GetFocusedElement() as FrameworkElement; IWindow owner = null; // check if the caller is hosted within a known container // if the dependency container is of IWindow type, use it as the owner of this modal window if (element != null) { DependencyObject logicalContainer = ISFocusManager.GetLogicalContainerScope(element); if (logicalContainer is IWindow) owner = logicalContainer as IWindow; } return owner; } } public static bool Standalone { get; set; } ...
- Here is the final EditClientProfileViewModel class after implement DelegateCommand.
C# Copy Code
public class EditClientProfileViewModel : ViewModelBase { #region Constructor public EditClientProfileViewModel() { this.Contact = new Contact { Id = "JOSE", Name = "José Pedro Freyre", Address = "C/ Romero, 33", Phone = "1-843-371-8211", Cell = "1-555-123-9250", Email = "jose@fabrikam.com", Fax = "", Blog = "http://www.cpandl.com/", Twitter = "http://twitter.com/jose", Website = "http://www.fabrikam.com/", State = StateCode.GA, ZipCode = "57998", Photo = new Uri("/ClientUIMVVMApp2;component/Assets/Photos/8.jpg", UriKind.RelativeOrAbsolute) }; this.StateCodes = new StateCodeDataProvider().Data; this._saveCommand = new DelegateCommand(ExecuteSave, CanExecuteSave); } #endregion #region Fields private Contact _contact; private DelegateCommand _saveCommand = null; #endregion #region Properties public IEnumerable StateCodes { get; private set; } public Contact Contact { get {return _contact; } set { if (_contact != value) { _contact = value; OnPropertyChanged("Contact"); } } } public DelegateCommand SaveCommand { get { return _saveCommand; } set { if (_saveCommand != value) { _saveCommand = value; OnPropertyChanged("SaveCommand"); } } } #endregion #region Methods private bool CanExecuteSave(object parameter) { return true; } private void ExecuteSave(object parameter) { MessageBoxServiceProvider.Standalone = true; MessageBoxServiceProvider.Show("Execute Save DelegateCommand", "Information", Intersoft.Client.UI.Aqua.UXDesktop.MessageBoxButton.OK, Intersoft.Client.UI.Aqua.UXDesktop.MessageBoxImage.Information, null); } #endregion }
Binding View to ViewModel
This section show how to bind the ViewModel that was created in the previous section to the View for example you will bind the Source property of Image to Contact.Photo property.
In the previous sections, you have learned how to create the Model and ViewModel classes, as well as the View that contains the user interface and controls used in this walkthrough. This section shows how to instantiate the ViewModel in the XAML page and bind the UI elements to the properties in the ViewModel such as the data context, selection and command.
To bind the EditClientProfileView to EditClientProfileViewModel
- Open EditClientProfile.xaml. Declare the namespace that maps to the EditClientProfileViewModel class in the EditClientProfile page.
XAML Copy Code
.. <Intersoft:UXWindow 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:ViewModels="clr-namespace:ClientUIMVVMApp2.ViewModels" mc:Ignorable="d" .. <Intersoft:UXWindow.DataContext> <ViewModels:EditClientProfileViewModel /> </Intersoft:UXWindow.DataContext> ...
- Bind Source property of the Image control. Set the Source to {Binding Contact.Photo}.
XAML Copy Code
... <Intersoft:ContentReflector HorizontalAlignment="Left" Width="160" VerticalContentAlignment="Bottom" Margin="12"> <Image Source="{Binding Contact.Photo}"/> </Intersoft:ContentReflector> ...
Note that if you can not see any data appear, please try to rebuild. - Bind all UXTextBox that represent ID, FullName, Address, State and ZipCode fields to the Contact properties, such as shown in the following code.
XAML Copy Code
... <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:FieldLabel Header="ID:"> <Intersoft:UXTextBox Width="100" Text="{Binding Contact.Id}" IsReadOnly="True" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Full Name:"> <Intersoft:UXTextBox Text="{Binding Contact.Name, Mode=TwoWay}" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Address:"> <Intersoft:UXTextBox Text="{Binding Contact.Address, Mode=TwoWay}" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="State:"> <Intersoft:UXComboBox DropDownHeight="150" ItemsSource="{Binding Path=StateCodes}" SelectedItem="{Binding Path=Contact.State, Mode=TwoWay}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Zip Code:"> <Intersoft:UXTextBox Width="100" Text="{Binding Contact.ZipCode, Mode=TwoWay}" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> </Intersoft:UXItemsControl> ...
- Bind all the UXTextBox that represent Mobile, Email and Twitter fields to the Contact properties, such as shown in the following example.
XAML Copy Code
... <Intersoft:ExpandableGroupBox Header="Contact Details" VerticalAlignment="Top" Margin="0,0,2,0"> <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:FieldLabel Header="Mobile:"> <Intersoft:UXTextBox Width="200" Text="{Binding Contact.Cell, Mode=TwoWay}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Email:"> <Intersoft:UXTextBox Width="200" Text="{Binding Contact.Email, Mode=TwoWay}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Twitter:"> <Intersoft:UXTextBox Width="200" Text="{Binding Contact.Twitter, Mode=TwoWay}" /> </Intersoft:FieldLabel> </Intersoft:UXItemsControl> </Intersoft:UXItemsControl> </Intersoft:ExpandableGroupBox> ...
- Bind the UXToolBarButton to SaveCommand in EditClientProfile.xaml such as shown in the following example.
XAML Copy Code
... <Intersoft:UXToolGroup> <Intersoft:UXToolBarButton Content="Save" DisplayMode="ContentAndImage" Icon="/Intersoft.ClientUI.Samples.Assets;component/Images/Office/SaveHS.png" ToolTipService.ToolTip="Save Changes" Command="{Binding SaveCommand}"/> <Intersoft:UXSeparator/> <Intersoft:UXToolBarButton Content="Cancel" DisplayMode="ContentAndImage" Icon="/Intersoft.ClientUI.Samples.Assets;component/Images/Commands/Cancel.png" ToolTipService.ToolTip="Cancel Changes (Not implemented in this sample)"/> </Intersoft:UXToolGroup> ...
- Since EditClientProfile.xaml is a window, then there should be a page that have or refers to EditClientProfile.xaml. Double click on MainPage.xml and add EditClientProfile window into it.
XAML Copy Code
... <Intersoft:UXPage 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:window="clr-namespace:ClientUIMVVMApp2.Views" mc:Ignorable="d" xmlns:Intersoft="http://intersoft.clientui.com/schemas" x:Class="ClientUIMVVMApp2.MainPage" Title="MainPage Page" d:DesignWidth="640" d:DesignHeight="480"> <Grid x:Name="LayoutRoot"> <window:EditClientProfile IsClientVisible="True" IsActive="True" VerticalAlignment="Center" Width="600" Height="400"/> </Grid> </Intersoft:UXPage> ...
Make sure that in App.xaml.cs, the Application StartUp should be set to MainPage.C# Copy Code
... private void Application_Startup(object sender, StartupEventArgs e) { this.RootVisual = new MainPage(); } ...
- Save, build and run the project.
- The following screenshot describe when Save button is clicked.
In this walkthrough, you have learned how to create ClientUI MVVM project using project template, and create the classes and page based on the Model, View and ViewModel pattern. You also learned how to bind the UXToolBarButton to a DelegateCommand in the ViewModel which performs save action.
Complete Code Listing
This section lists the complete code used in this walkthrough.
Contact.cs
C# | ![]() |
---|---|
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using MVVMApplicationWalkthrough.ViewModels; using System.ComponentModel; using System.Xml.Linq; namespace MVVMApplicationWalkthrough.Models { public class Contact : ModelBase { #region Constructor private Contact(string id, string name, string email, string phone, string twitter, string website, Uri photo) { this._id = id; this._name = name; this._email = email; this._phone = phone; this._twitter = twitter; this._website = website; this._photo = photo; } public Contact() : this(id: string.Empty, name: string.Empty, email: string.Empty, phone: string.Empty, twitter: string.Empty, website: string.Empty, photo: null) { } #endregion #region Fields private string _id; private string _name; private string _email; private string _phone; private string _twitter; private string _website; private Uri _photo; #endregion #region Properties public string Id { get { return this._id; } set { if (this._id != value) { this._id = value; this.OnPropertyChanged("Id"); } } } public string Name { get { return this._name; } set { if (this._name != value) { this._name = value; this.ClearError("Name"); this.OnPropertyChanged("Name"); } } } public string Email { get { return this._email; } set { if (this._email != value) { this._email = value; this.ClearError("Email"); this.OnPropertyChanged("Email"); } } } public string Phone { get { return this._phone; } } public string Twitter { get { return this._twitter; } } public string Website { get { return this._website; } } public Uri Photo { get { return _photo; } set { if (this._photo != value) { _photo = value; OnPropertyChanged("Photo"); } } } #endregion #region Methods public void SetPhoto(string uri) { this.Photo = new Uri("/MVVMApplicationWalkthrough;component/Assets/Photos/" + uri + ".jpg", UriKind.RelativeOrAbsolute); } #endregion } } |
State.cs
C# | ![]() |
---|---|
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace ClientUIMVVMApp2.Models { public enum StateCode { AL, // Alabama AK, // Alaska AS, // American Samoa AZ, // Arizona AR, // Arkansas CA, // California CO, // Colorado CT, DE, DC, FL, GA, GU, HI, ID, IL, IN, IA, KS, KY, LA, ME, MD, MH, MA, MI, FM, MN, MS, MO, MT, NE, NV, NH, NJ, NM, NY, NC, ND, MP, OH, OK, OR, PW, PA, PR, RI, SC, SD, TN, TX, UT, VT, VA, VI, WA, WV, WI, WY, } } |
EnumDataProvider.cs
C# | ![]() |
---|---|
using System; using System.Collections; using System.Collections.Generic; using ClientUIMVVMApp2.Models; namespace ClientUIMVVMApp2.Converter { public abstract class EnumDataProvider<T> { IEnumerable _data; public IEnumerable Data { get { if (this._data == null) { Type enumType = typeof(T); List<object> list = new List<object>(); int value = 0; while (Enum.IsDefined(enumType, value)) { list.Add(Enum.ToObject(enumType, value)); value++; } this._data = list; } return this._data; } } } public sealed class StateCodeDataProvider : EnumDataProvider<StateCode> { } } |
EditClientProfile.xaml
XAML | ![]() |
---|---|
<Intersoft:UXWindow 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:ViewModels="clr-namespace:ClientUIMVVMApp2.ViewModels" mc:Ignorable="d" xmlns:Intersoft="http://intersoft.clientui.com/schemas" x:Class="ClientUIMVVMApp2.Views.EditClientProfile" Header="EditClientProfile" d:DesignWidth="640" d:DesignHeight="480"> <Intersoft:UXWindow.DataContext> <ViewModels:EditClientProfileViewModel /> </Intersoft:UXWindow.DataContext> <Intersoft:UXWindow.Resources> <Style x:Key="FieldLabelStyle" TargetType="Intersoft:FieldLabel"> <Setter Property="HeaderWidth" Value="160"/> <Setter Property="HorizontalHeaderAlignment" Value="Right"/> <Setter Property="Padding" Value="2"/> <Setter Property="HeaderMargin" Value="0,0,8,0"/> </Style> <Style x:Key="SimpleTextBoxStyle" TargetType="Intersoft:UXTextBox"> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="Width" Value="200"/> </Style> </Intersoft:UXWindow.Resources> <Intersoft:UXWindow.PrimaryToolBarTemplate> <DataTemplate> <Grid> <Intersoft:UXToolBar Intersoft:DockPanel.Dock="Top" OverflowHandleVisibility="AsNeeded" Margin="0,8,0,0" GripHandleVisibility="Collapsed" HorizontalAlignment="Left"> <Intersoft:UXToolBar.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#90FFFFFF" Offset="0"/> <GradientStop Color="#00B1D0E3" Offset="1"/> </LinearGradientBrush> </Intersoft:UXToolBar.Background> <Intersoft:UXToolGroup> <Intersoft:UXToolBarButton Content="Save" DisplayMode="Content" ToolTipService.ToolTip="Save Changes" Command="{Binding SaveCommand}"/> <Intersoft:UXSeparator/> <Intersoft:UXToolBarButton Content="Cancel" DisplayMode="Content" ToolTipService.ToolTip="Cancel Changes (Not implemented in this sample)"/> </Intersoft:UXToolGroup> </Intersoft:UXToolBar> </Grid> </DataTemplate> </Intersoft:UXWindow.PrimaryToolBarTemplate> <Grid x:Name="LayoutRoot"> <Grid> <Intersoft:DockPanel FillChildMode="Custom"> <Intersoft:ContentReflector Name="contentReflector1" Width="160" HorizontalAlignment="Left" VerticalContentAlignment="Bottom" Margin="12"> <Image Source="{Binding Contact.Photo}"/> </Intersoft:ContentReflector> <StackPanel Intersoft:DockPanel.Dock="Right" Intersoft:DockPanel.IsFillElement="True"> <Intersoft:ExpandableGroupBox Header="General Information" VerticalAlignment="Top" Name="expandableGroupBox1"> <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:FieldLabel Header="ID:"> <Intersoft:UXTextBox Width="100" Text="{Binding Contact.Id}" IsReadOnly="True" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Full Name:"> <Intersoft:UXTextBox Text="{Binding Contact.Name, Mode=TwoWay}" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Address:"> <Intersoft:UXTextBox Text="{Binding Contact.Address, Mode=TwoWay}" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="State:"> <Intersoft:UXComboBox DropDownHeight="150" ItemsSource="{Binding Path=StateCodes}" SelectedItem="{Binding Path=Contact.State, Mode=TwoWay}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Zip Code:"> <Intersoft:UXTextBox Width="100" Text="{Binding Contact.ZipCode, Mode=TwoWay}" Style="{StaticResource SimpleTextBoxStyle}" /> </Intersoft:FieldLabel> </Intersoft:UXItemsControl> </Intersoft:ExpandableGroupBox> <Intersoft:ExpandableGroupBox Header="Contact Details" VerticalAlignment="Top" Margin="0,0,2,0"> <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:UXItemsControl ItemContainerStyle="{StaticResource FieldLabelStyle}"> <Intersoft:FieldLabel Header="Mobile:"> <Intersoft:UXTextBox Width="200" Text="{Binding Contact.Cell, Mode=TwoWay}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Email:"> <Intersoft:UXTextBox Width="200" Text="{Binding Contact.Email, Mode=TwoWay}" /> </Intersoft:FieldLabel> <Intersoft:FieldLabel Header="Twitter:"> <Intersoft:UXTextBox Width="200" Text="{Binding Contact.Twitter, Mode=TwoWay}" /> </Intersoft:FieldLabel> </Intersoft:UXItemsControl> </Intersoft:UXItemsControl> </Intersoft:ExpandableGroupBox> </StackPanel> </Intersoft:DockPanel> </Grid> </Grid> </Intersoft:UXWindow> |
ContactViewModel.cs
C# | ![]() |
---|---|
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using ClientUIMVVMApp2.Models; namespace ClientUIMVVMApp2.ViewModels { public class ContactViewModel : ViewModelBase { // Fields private Contact _contact; // Constructor public ContactViewModel(Contact contact) { _contact = contact; } // Views public Contact Contact { get { return this._contact; } } public string EmailUri { get { return "mailto:" + _contact.Email; } } } } |
EditClientProfileViewModel.cs
C# | ![]() |
---|---|
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using ClientUIMVVMApp2.Converter; using ClientUIMVVMApp2.Models; using System.Collections; using Intersoft.Client.Framework.Input; namespace ClientUIMVVMApp2.ViewModels { public class EditClientProfileViewModel : ViewModelBase { #region Constructor public EditClientProfileViewModel() { this.Contact = new Contact { Id = "JOSE", Name = "José Pedro Freyre", Address = "C/ Romero, 33", Phone = "1-843-371-8211", Cell = "1-555-123-9250", Email = "jose@fabrikam.com", Fax = "", Blog = "http://www.cpandl.com/", Twitter = "http://twitter.com/jose", Website = "http://www.fabrikam.com/", State = StateCode.GA, ZipCode = "57998", Photo = new Uri("/ClientUIMVVMApp2;component/Assets/Photos/8.jpg", UriKind.RelativeOrAbsolute) }; this.StateCodes = new StateCodeDataProvider().Data; this._saveCommand = new DelegateCommand(ExecuteSave, CanExecuteSave); } #endregion #region Fields private Contact _contact; private DelegateCommand _saveCommand = null; #endregion #region Properties public IEnumerable StateCodes { get; private set; } public Contact Contact { get {return _contact; } set { if (_contact != value) { _contact = value; OnPropertyChanged("Contact"); } } } public DelegateCommand SaveCommand { get { return _saveCommand; } set { if (_saveCommand != value) { _saveCommand = value; OnPropertyChanged("SaveCommand"); } } } #endregion #region Methods private bool CanExecuteSave(object parameter) { return true; } private void ExecuteSave(object parameter) { MessageBoxServiceProvider.Standalone = true; MessageBoxServiceProvider.Show("Execute Save DelegateCommand", "Information", Intersoft.Client.UI.Aqua.UXDesktop.MessageBoxButton.OK, Intersoft.Client.UI.Aqua.UXDesktop.MessageBoxImage.Information, null); } #endregion } } |
ExpandableGroupBox Class
UXItemsControl Class
UXTextBox Class
UXToolBarButton Class
DelegateCommand Class
Concepts
Commanding Overview
MVVM Pattern Overview
Other Resources
Locating the Samples in Local Installation
Commanding How-to Topics
MVVM Pattern How-to Topics
MVVM Pattern Walkthroughs
Walkthroughs and How-to Topics