Deploying WCF Service

Deploying WCF to ASP.NET Development Server

 

Deploying WCF to IIS

 

Hosting a WCF Service by Using Windows ProcessActivation Service (with TCP)

Setting Up Visual Studio for WPF and Silverlight Development

Visual Studio Tool Window Layout  

In order to customize the layout of Visual Studio we will open a new project. Opening a new project makes all tool windows available.

  1. Start Visual Studio 2010.
  2. From the File menu hover over New, then select New Project…

    FileNewProject

    Figure 1 New Project Menu Selection

  3. New Project Dialog will be displayed.

    NewProjectDialog

    Figure 2 New Project Dialog

Note
The Target Framework Version ComboBox value defaults to the last selection made. When creating a new project, verify the desired Target Framework Version is selected.
  • Step 1 – Select language; Visual Basic or C# then select Windows.
  • Step 2 – Select WPF Application.
  • Step 3 – Enter project name.
Tip
While it is permissible to have spaces in the project name this practice discouraged.
  • Step 4 – Click OK to create your new WPF Application.

Suggested Visual Studio Layout

Visual Studio provides a feature rich and powerful development environment for developers to create a wide range of application types. Figure 3 below pictures a suggested layout for WPF and Silverlight application development.

VisualStudioLayoutOriginal

Figure 3 Visual Studio Layout

  1. Text Editor Toolbar – gives quick access to commonly used features like indenting, commenting and bookmarks. To add the Text Editor Toolbar, right click on the on the Toolbar, then select Text Editor from the drop down menu.
  2. Left Tool Windows
  3. DataSources Window – Allows dragging of database or objects onto the design view. Used to quickly create data bound forms. To add the DataSources window, click on the Data menu and select Show Data Sources.
  4. Server Explorer – Allows setting up database connections and browsing available servers. Is displayed by default.
  5. Toolbox – Allows dragging installed controls or solution controls onto the design view or XAML editor. Is displayed by default.
  6. Document Outline – Allows navigating the structure of your application. Used to single or multi-select objects. Provides thumbnail preview ToolTip of an object as the mouse hovers over it. To add the Document Outline, click on the View menu, click on Other Windows and select Document Outline.
  7. Some developers like to have the Document Outline visible at all times, so they position the Document Outline tool window below the above three tool windows group as in Figure 4 below.

AlternateDocumentOulinePosition

Figure 4 Alternate Document Outline Position

Tip
For developers new to WPF or Silverlight, the Document Outline is an invaluable tool for understanding the sometimes deeply nested control tree. The Document Outline tree view makes it very easy to navigate and select objects in your application.
  • Bottom Tool Windows. These windows are not unique to the XAML editing environment.
  • Error List – Displays build errors and XAML errors in design view or XAML editor. To add the Error List, click on the View menu and select Error List. The Error List is also displayed by default when an error is detected.
  • Task List – Displays code Comments and User Tasks. To add the Task List, click on the View menu and select Task List.
  • Output – Displays build output, debug messages and runtime diagnostic output. To add the Output tool window, click on the View menu and select Output. The Output tool window is also brought into view when compiling or debugging.
  • Bookmarks – Displays bookmarks. Used to manage and navigate bookmarks. To add the Bookmarks tool window, click on the View menu and select Bookmarks.
  • Tip
    Bookmarks are available in XAML files as well as code files. This feature makes it very easy to switch between files.
  • Designer region – contains Design View, Split View Bar, XAML View, Tag Navigator and Code View. This region is where most of the developers time is spent designing layouts and writing application code. Each of these areas will be covered below.
  • Top right Tool Windows
  • Solution Explorer – used to open files for editing in various Visual Studio editors. Displays all solution projects and project items. Is displayed by default.
  • Class View – used to view the types and referenced types in the solution. Is displayed by default.
  • Properties Window – used to edit properties for the selected object. Object selection can be initiated from the Solution Explorer, Class View, Document Outline, Design View, XAML View or the Tag Navigator. Is displayed by default. The Properties Window will be covered below.
  • Design View  

    Design view provides true WYSIWYG (What You See Is What You Get) application design experience. Developers use this view to layout their applications.

    DesignView

    Figure 5 Design View 

    Zoom Control

    Design view provides a Zoom control in the upper left corner to allow the developer to zoom design view in and out. See Figure 5 above. At the bottom of the zoom control is a toggle button. This button toggles between, “Fit in view” and “100% zoom level.”

    Pan Tool

    The Pan tool is available when the design view is displaying horizontal and or vertical scrollbars. When the designer has focus and you press the SPACE BAR, the mouse cursor will change to the hand cursor indicating that you can pan the design surface. Click and drag the design surface to reposition it.

    The Pan tool will only allow movement of the design surface if the scrollbar for the direction of movement is visible. When both horizontal and vertical scrollbars are visible the design surface can be repositioned in all directions.

    Root Size Mode Button

    The root size mode button toggles between a Fixed Size root and an Auto Sized root. The root is the outmost control. In Figure 5 the root control or container is a Window.

    When set to Fixed Size, the root container will have the same height and width at design time and run time.

    When set to Auto Size, the root container will have a fixed design time height and width and but will auto size itself at run time. See the Auto Size UserControl root in Figure 6 below.

    AutoSizedRoot

    Figure 6 Auto Sized Root

    Note
    The root sizing becomes very important when designing UserControls. Typically UserControls consume the available space given to them by their hosting control and auto sizing is desirable. However, at design time if the UserControl was auto sized and had no content, it would shrink to size 0, 0. Auto Size root allows the UserControl to have a design time height and width and but also have auto size behavior at run time.

    Information Bar

    After building a solution, when you select another tab that has a XAML file and the designer is visible, you’ll see the below Information bar. Click the Information bar to reload design view.

    InformationBar

    Figure 7 Information Bar

    Note
    The Information bar is only shown for XAML files that were not the active Visual Studio document when the solution was built. If the current document is the XAML file, that document will automatically be reloaded during the build process.

    After building the solution, attempting to use Design View when an error exists will cause the below Information bar to appear at the top of Design View. You need to correct the errors and this Information bar will disappear, making Design View usable again.

    InformationBarTwo

    Figure 8 Information Bar

    Split View Bar

     

    The Split View Bar gives the developer powerful tools for controlling and sizing the design and XAML views.

    SplitViewBar

    Figure 9 Split View Bar

    1. Design is a tab header. When clicked it will bring the Design view into view. This feature is used when the Design and XAML views consume all of the space and one is hidden from view. Double clicking the Design tab will cause you to enter full Design view mode.
    2. The Switch Panes button will swap the positions of the Design and XAML views. This button is only visible when the Design and XAML views are both visible.
    3. XAML is a tab header. When clicked will bring the XAML view into view. This feature is used when the Design and XAML views consume all of the space and one is hidden from view. Double clicking the XAML tab will cause you to enter full XAML view mode. Full XAML view mode is described later.
    4. Dragging the resize bar up and down will resize the Design and XAML views.
    5. Split Buttons
    6. The left button is the Vertical Split button. When clicked will split the Design and XAML views vertically instead of the default, horizontally.
    7. The middle button is the Horizontal Split button. When clicked will split the Design and XAML views horizontally. This is the default.
    8. The right button is the Collapse Pane, Expand Pane toggle button.
    9. If the designer region is displaying both Design and XAML views, when clicked will cause the top pane to consume all the space of the designer region.
    10. If the designer region is consumed by either the Design or XAML view, when click will return the designer region back to split view.

    XAML View  

    XAML view provides a complete editing experience for XAML documents, that includes IntelliSense, auto-formatting, syntax highlighting, and tag navigation. When you edit XAML, Design view updates to reflect your changes.

    To easily navigate through your XAML document, you can either select elements on the design surface or use Document Outline to change your selection.

    Visual Studio 2010 supports Markup Extension IntelliSense. IntelliSense options will appear as a dropdown list after typing a “{” to begin an element. Navigate IntelliSense with the arrow keys and press tab to select a value.

    XAML errors are marked in the XAML editor with a blue squiggle underline, which can be hovered over to produce a tooltip with error information. Those same errors are also listed in the Error List as in Figure 10 below.

    XAMLEditorError

    Figure 10 XAML Editor Error

    Note
    Notice the yellow bar to the left of the <Button tag in Figure 10 above. This indicates this line of XAML has changed since the last time the file was saved.  If the bar was green, this would indicate this line of XAML was changed and saved since the file was opened.

    Full XAML View Limitations

    If you open a XAML file in full XAML view the Properties Window for selected objects will not be available. The Properties Window depends on Design View being loaded. If you switch to Design View and click the displayed link to reload the designer, you can then switch back to full XAML view and the Properties Window will display the selected object.

    NoSelectionSync

    Tag Navigator  

    The Tag Navigator allows easy navigation of the current leaf of the XAML document.

    TagNavigator

    Figure 11 Tag Navigator

    • Document Outline button. Click this to bring the Document Outline into view.
    • Currently selected control type and control name.
    • Click on any of the parent controls of the currently selected control to select that control.
    • Click this right arrow button to select any children of the currently selected control.

    Properties Window  

    The Properties Window provides rich property editing for the selected object’s properties.

    PropertiesWindowOriginal

    Figure 12 Properties Window

    1. Thumbnail Preview displays a thumbnail image of the selected control.
    2. Control Type is the full type name of the selected control.
    3. Control Name is the name of the selected control.
    4. Property Search Textbox provides quick access to properties. The search algorithm finds all properties that contain the characters entered in the Search Textbox.
    5. Categorized button changes the property listing to group properties by categories.
    6. Properties button changes the list to list properties instead of events.
    7. Alphabetical button changes the property listing to list properties alphabetically.
    8. Events button changes the list to list events instead of properties.
    9. Property Markers indicate where the value is coming from. In Figure 12 above you see 5 different Property Marker icons.
    10. Clicking or right clicking on the Property Marker brings up the Property Marker content menu as in Figure 13 below.

    PropertyMarkerContentMenu

    Figure 13 Property Marker Content Menu

  • Reset Value will reset the property value back to its default value.
  • Apply Data Binding will open the Data Binding Builder and enables a data binding expression to be assigned to this property. This will be covered in a future article.
  • Apply Resource will open the Resource picker and allow a resource to be assigned to this property. This will be covered in a future article.
  • Extract value to resource will open the Create resource dialog enabling the current property value to be assigned to a new resource and that new resource assigned to the property value. This will be covered in a future article.
  • XAML Text Editor Options  

    There are a number of configurable XAML text editor options that can be set using the Visual Studio Options dialog. To open the Options dialog, click the Tools menu and select Options. When the Options dialog is displayed find Text Editor in the left TreeView and expand it. Then expand XAML and finally expand Formatting and click on Spacing.

    ToolsOptionsSpacingOriginal

    Figure 14 XAML Editor Options

    1. The Attribute Spacing section in Figure 14 determines how XAML element attributes are formatted when XAML is pasted in the document or when the document is formatted with the Format Document command (CTRL+K, CTRL+D.) You can also run the Format Document command from the menu by clicking on the Edit menu, clicking on Advanced and selecting Format Document.
    2. If “Preserve newlines and spaces between attributes” is selected, document format will not be changed.
    3. If “Insert a single space between attributes” is selected, element attributes will be collapsed to a single line with a single space between attributes.

      XAMLSingleLineSpacing

      Figure 15 Attribute Single Space Formatting

    4. If “Position each attribute on a separate line” is selected, element attributes will be expanded and each attribute will be on a separate line.
    5. Position of the first attribute is controlled by the “Position first attribute on same line as start tag” Checkbox.

    XAMLMultiLineSpacing

    Figure 16 Attribute Multi-Line Formatting

  • The Element Spacing section in Figure 14 determines how XAML elements are formatted when the XAML is pasted in the document or when the document is formatted with the Format Document command.
    1. If “Preserve new lines in content” is selected, document format will not be changed.
    2. If “Collapse multiple empty lines in content to a single line” is selected, multiple empty lines will be collapsed to a single line.
    3. If “Remove empty lines in content” is selected, all empty lines in the document will be removed.
  • Take a few minutes to change the above settings with the simple XAML document you have loaded. After making a settings change, use the Format Document command and reformat the XAML to view how the command changes your XAML document formatting.

    Reopen the Options dialog and navigate to the XAML, Miscellaneous tab.

     Options

    Figure 17 XAML Editor Options

    If “Closing tags” is selected, when editing XAML and you enter the close tag for the element, a matching end element tag will automatically be inserted. In Figure 18 below, <Button> was typed, </Button> was automatically inserted by Visual Studio.

    ClosingTag

    Figure 18 Close Tag

    If “Attribute quotes” is selected, when editing XAML and you enter a property name, both the closing and ending double quote will automatically be inserted after the equals sign for you.

    If “Closing braces for MarkupExtensions” is selected, when editing a property value in XAML and you type “{” a corresponding “}” will be inserted for you.

    If “Commas to separate MarkupExtension parameters” is selected, when editing a MarkupExtension in XAML, pressing the SPACE BAR will automatically insert a comma for you. After typing FirstName, pressing the SPACE BAR causes a comma to be inserted, and the insertion point moved one space to the right of the comma.

    MarkupExtensionCommainsertion

    Figure 19 MarkupExtension Comma Insertion

    If “Always open documents in full XAML view” is selected, when a XAML document is opened, XAML view will consume the designer region. When using this feature keep in mind the limitations discussed in the above section, “Full XAML View Limitations.”

    Automatically Populate Toolbox Items

    By default, in Visual Studio 2010, each time you build a WPF and Silverlight project, any types that derive from FrameworkElement or higher will be automatically added to the Toolbox. The types that are added are grouped into Toolbox tab by assembly. The purpose of this feature is to make it easy for developers to consume the UserControls and CustomControls they create on a form.

    To you enable/disable this feature by checking or un-checking the CheckBox Automatically populate toolbox items in Figure 17 above.  The red arrow points to this CheckBox.

    Navigation Template Kullanma

    Assets: Klasorunde Styles.xaml adında ResourceDictionary (bu uygulamada kullanılan görsel styleler) bulunur.

    Views: Varsayılan olarak Uygulamadaki sayfalar bulunur bu sayfalar Page sınıfı tipinde xaml dosyasıdır.

    Normal silvelight uygulamsı template inde bulunmayan 2 referans navigation uygulamaya ekli olarak oluşturulmuştur. Bunlar:

    System.Windows.ControlsFrame controlunu içerir bu control silverlight Page controllerinde gezinmeyi sağlar, kullanıcı isteğine yada programsal olarak bu gezinme yani navigation geçerli bir URI ye göre sağlanır.

    Sytem.Windows.Controls.Navigation – UriMapper sınıfını içerir: İstenilen URI ler başka URI lere dönüştürmek için UriMapping nesne koleksiyonunu içerir. UriMapper nesnesi ve içerdiği UriMapping nesnesini Frame kontrolünün UriMapper propertiesine oluşturulan bu UriMapping dictionary nesneleri atanır.

    MainPage.xaml dosyasına bakıllığında bu namespacelerin ilgili prefix ile eklenip bildirimi yapılmıştır. Navigation ve uriMapper adı verilen iki prefixe xaml parserın tanıyabilmesi için iki assembly eklenmiştir.

    xmlns:navigation=”clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation”

        xmlns:uriMapper=”clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation”

     

    Ve alt kodlara baktığımızda ilgili prefixlerde ilgili sınıflar çağrılmıştır.

    <navigation:Frame x:Name=”ContentFrame” Style=”{StaticResource ContentFrameStyle}”

                                  Source=”/Home” Navigated=”ContentFrame_Navigated” NavigationFailed=”ContentFrame_NavigationFailed”>

                    <navigation:Frame.UriMapper>

                      <uriMapper:UriMapper>

                        <uriMapper:UriMapping Uri=”" MappedUri=”/Views/Home.xaml”/>

                        <uriMapper:UriMapping Uri=”/{pageName}” MappedUri=”/Views/{pageName}.xaml”/>

                      </uriMapper:UriMapper>

                    </navigation:Frame.UriMapper>

                </navigation:Frame>

     

    UriMapper koleksiyon sınıfı ilgili prefixde belirtilmiştir ve aynı prefixde UriMapping koleksiyon nesneleri belli property değerleri alacak şekilde oluşturulmuştur. Dikkat edilirse Frame sınıf nesnesi için navigation prefixi belirtilmiş ve bu prefixde Frame için UriMapper tipindeki dependency property tanımlaması yapılmıştır. (Frame.UriMapper)

    Yani: Navigasyonun sağlanması için Page haritalandırılması yapılmalı bu page Urilerini UriMapper koleksiyon sınıfı UriMapping tipinde propertyler ile tutuyor, bu property tipindeki nesne propertylerinede ilgili uriler belirtiliyor, oluşan bu UriMapper koleksiyon nesnesi Frame nesnesindeki dependency propertye atanıyor ve yapı sağlanmış oluyor.

     

    Oluşturulan bu Frame ve UriMapping nesnelerinin propertylerine baktığımızda:

    Frame:

    Source : Frame in load anındaki varsayılan sayfa

    Navigated : Bu bir event dır Framede navigasyon sağlandığında çalışacak event adı. Event içeriği

    private void ContentFrame_Navigated(object sender, NavigationEventArgs e)

            {

                foreach (UIElement child in LinksStackPanel.Children)

                {

                    HyperlinkButton hb = child as HyperlinkButton;

                    if (hb != null && hb.NavigateUri != null)

                    {

                        if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))

                        {

                            VisualStateManager.GoToState(hb, “ActiveLink”, true);

                        }

                        else

                        {

                            VisualStateManager.GoToState(hb, “InactiveLink”, true);

                        }

                    }

                }

    NavigationFailed : Gezinmede yani navigasyonda bir hata olduğunda işleyecek event adı

    private void ContentFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)

            {

                e.Handled = true;

                ChildWindow errorWin = new ErrorWindow(e.Uri);

                errorWin.Show();

            }

     

    UriMapper koleksiyonundaki UriMapping nesne propertyleri ise:

    Uri ve MappedUri property değerleri görünmekte bunlar string değerlerdir Uri çağırlacak sayfayı MappedUri ise çağıralacak sayfanın tam yoluna dönüştürülmesi ile sağlanır. Mesela

    <uriMapper:UriMapping Uri=”/{pageName}” MappedUri=”/Views/{pageName}.xaml”/>

    Gibi urimapping tanımında Uri { } arasındaki bir sayfa adı olan çağrılarda gideceği uri (mappeduri) belli yolu istenilen sayfa adı ile tanımlanarak belirtilir. Burada pageName değişken adıdır.

    <uriMapper:UriMapping Uri=”" MappedUri=”/Views/Home.xaml”/>

    Örneğinde ise herhangi bir boş Uri olmadığı durumda gideceği yol belirtilir.

    Buradaki cağrılacak uri değerleri için bir hyperlink butonunda aşağıdaki gibi belirlenir.

      <HyperlinkButton x:Name=”Link1″

     Style=”{StaticResource LinkStyle}”

     NavigateUri=”/Home”

     TargetName=”ContentFrame”

     Content=”home”/>

    Ve

    <HyperlinkButton x:Name=”Link2″

       Style=”{StaticResource LinkStyle}”

                     NavigateUri=”/About”

                     TargetName=”ContentFrame”

        Content=”about”/>

    Şeklinde NavigateUri propertye göre hangi link tıklanırsa ilgili Uri değeri UriMapper koleksiyonundanki UriMapping nesne özelliklerindeki Uri ye göre işaretlenir ve Navigate oluşacak Page çağrılır. /About bir pagename değişken değeri olarak alınır ve MappedUri ye ilgili yola işaretlenir. Aynı şekilde /Home da bir değişkenin değeridir ilgili koleksiyon nesnesi bu iki sayfa için: <uriMapper:UriMapping Uri=”/{pageName}” MappedUri=”/Views/{pageName}.xaml”/>

    dir. Link özelliklerine bakarsak TargetName özelliğindede uygulamadaki x:Name ile belirtilmiş Frame nesne adımız atanmakta.

    Bu uygulamaya yeni sayfalar ekleyebilir o sayfa içeriğini değiştirebilir bu sayfa için bir linkbutonu oluşturabilir yada kod ile çağırma işlemi yapabiliriz hatta querystring oluşturabilir başka sayfalarda querystring değerine göre istenilen işlemleri gerçekleştirebiriz. Bunun için:

    UriMapping eşleşmesi yapılması için koleksiyona ilgili sayfayı eklemeliyiz, Sayfa.xaml page oluşturduktan sonra, main.xaml da.

    <uriMapper:UriMapping Uri=”girilen {not}” MappedUri=”/Views/Sonuc.xaml?not={not}”/>

    Eğerki bir hyperlink buttonun click metodunda Uri nesnesindeki uri stringi “girilen {not}” şeklinde not adında bir değişkenle oluştuğudan eşleşen bu UriMapping nesnesindeki MappedUri sayfası çağrılacak,

    Linki oluşturacağımız sayfaya bir hyperlink butonu oluşturup click eventında navigate edilecek uri stringini oluşturalım.

    <HyperlinkButton x:Name=”link” FontSize=”14″ Content=”QueryString –>” Click=”link_Click”/>

    Event için

    private void link_Click(object sender, RoutedEventArgs e)

            {

                this.NavigationService.Navigate(new Uri(string.Format(“girilen {0}”, 32), UriKind.Relative));

                //32 nolu ogrenciye git!

            }

    Link butonu tıklandığında UriMappingden eşleşme yapılır ve ilgili sayfaya gidilir, gidilen sayfa için sayfa navigate olduğunda işleyecek metoda

    <TextBlock x:Name=”sonuc” FontSize=”24″></TextBlock>

     

    protected override void OnNavigatedTo(NavigationEventArgs e)

            {

                sonuc.Text = this.NavigationContext.QueryString["not"];

            }

    İle querystring den değişken alınır ve sayfanın text kontrolünde yazdırılır.

    Not: Hyperlinkbutton kontrolü özelliklerinden Tag ile hangi eğer hyperlink bir listedeki her bir eleman olarak ayarlanmışsa o elaman birer sınıf nesnesi ise ör Customer, ve hyperlinkbutton nesnesinde Tag özelliği Bu customer nesnesinin id si için Binding edilmişse,ilgili Tag ilgili id yi vede customer ismini yada diğer özelliklerini gösterecektir.

    Frame Sınıf Metodları

    GoBack() : Bir geri navigasyon sağlar tabiki varsa, yoksa hata fırlatır.

     

    GoForward() : Bir ileri navigasyon sağlar. Yoksa hata fırlatılır.

     

    Navigate() : UriMapper sınıfında eşleşen uri verilerek o sayfaya navigasyon sağlar.

    private void Link4_Click(object sender, RoutedEventArgs e)

            {

                ContentFrame.Navigate(new Uri(“/About”, UriKind.Relative));

            }

    Bu Uri UriMapperda tanımlıydı:

    <uriMapper:UriMapping Uri=”/{pageName}” MappedUri=”/Views/{pageName}.xaml”/>

     

    Refresh() : Bu metod gösterilen sayfanın tekrar yüklenmesini sağlar. Dikkat edilirse Frame sınıfını MainPage.xamlda oluşturmuştuk ve bu metodlarıda bu cs de kodla çağırıyoruz.

     

    StopLoading() : Bu metod sayfa henüz navigasyona uğramadığında (durduğunda NavigationStopped eventi çalışır) navigasyonu durdurmak için kullanır.

     

     

    Frame Sınıf Propertyleri

    CacheSize : Navigasyon oluştuğunda oluşan Page tekrardan oluşturulmaz bir cache de tutulur ve ordan alınır eğerki cache de ki sayfa sayısının sınırlamak istersek bu özelliği kullanırız, bu özelliği kullanmak için Page sınıfının NavigationCacheMode özelliğinin Enabled yapmalıyız, eğerki bu özelliği Required yaprsak sayfa CacheSize özelliğine bakmadan yani page sayısının hesaplamadan sayfalar oluşturulacaktır yani cache atılacaktır.

     

    Şeklinde 10 olarak cache ayarlanır bununla birlikte hangi sayfanın cachelenmeden etkilenip etkilenmemesi sağlanır.

     

    Şeklinde About.xaml a cache adete bakılmaksızın cachelenme oluşacaktır, bunun yanında disapled değer verilerekte cache yapılmaması sağlanır.

    CanGoBack, CanGoForward : true false döndüren özelliktir framede navigasyon oluşan sayfadaki geriyada iler gidebilme durumunu döndürür.

    CurrentSource: Mevcut sayfanın Uri bilgisini elde etmek yada atamak için kullanılır

     

     

    Frame Sınıfı Eventları

    Gördükki sayfalar Frame sınıfı contentleri ve olaylar Frame tarafından kontrol ediliyor o halde devam edelim. Frame sınıfı eventlarıda contentlere yönelik olmakta.

    FrameNavigation : Bu eventa content navigasyonu oluştuğunda gerekli işlemler yaptırılır.

    Navigated : Bu event ile content (herhangi sayfa) oluştuğunda çalışacak işlemler belirtilir. Mesela

           private void ContentFrame_Navigated(object sender, NavigationEventArgs e)

            {

                foreach (UIElement child in LinksStackPanel.Children)

                {

                    HyperlinkButton hb = child as HyperlinkButton;

                    if (hb != null && hb.NavigateUri != null)

                    {

                        if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))

                        {

                            VisualStateManager.GoToState(hb, “ActiveLink”, true);

                        }

                        else

                        {

                            VisualStateManager.GoToState(hb, “InactiveLink”, true);

                        }

                    }

                }

            }

    Event ile herbir sayfa navigasyon için belli işlemler yaptırılmakta.

    Navigating: Bir navigasyon oluşturulma isteği anında oluşur.

      private void ContentFrame_Navigating(object sender, NavigatingCancelEventArgs e)

            {

                MessageBox.Show(“you have navigating using mode “+ e.NavigationMode);

            }

    Örnekte her bir navigasyon isteği anında çalışan bir messagebox, burada NavigationMode kim navigate edilecekse edilecek navigasyonun modunu elde ediyoruz., Mesela navigasyon bir refresh yada new olabilir.

    Mesela aşağıda bir linke tıklandığında ContentFrame in refresh medotu ile navigasyon olan page in tekrar refresh edilmesi  isteniyor o da navigasyonun modunun refresh olduğunu gösteriyor.

     

    NavigationFailed : Contentlerin navigasyon halinde hata olduğunda çalıştırılacak kodlar bu event da belirtilir.

     

    Burada ErrorWindow adında oluşturduğumuz bir childclass(page değil) nesnesini gösteriyoruz show ile. Gösterdiğimiz bu sayfa (childclass) da tanımladığımız kodlarla nasıl bir hata görüntüsü çıkarılacağı ayarlanıyor. Tabiki biz childclassımızı istediğimiz yapıcı metodlarla ayarlayabiliriz yukarıdaki örnekte

          public ErrorWindow(Uri uri)

            {

                InitializeComponent();

                if (uri != null)

                {

                    ErrorTextBox.Text = “Page not found: \”" + uri.ToString() + “\”";

                }

            }

    Yapıcı metoda uri verilecek şekilde overload edilmiş diğer overload metodlar:

            public ErrorWindow(Exception e)

            {

                InitializeComponent();

                if (e != null)

                {

                    ErrorTextBox.Text = e.Message + Environment.NewLine + Environment.NewLine + e.StackTrace;

                }

            }

            public ErrorWindow(string message, string details)

            {

                InitializeComponent();

                ErrorTextBox.Text = message + Environment.NewLine + Environment.NewLine + details;

            }

    Bunun la birlikte sayfamıza ok vs butonlarıda bulunacağından bununla ilgili eventlardan OK butnu için

            private void OKButton_Click(object sender, RoutedEventArgs e)

            {

                this.DialogResult = true;

            }

     

    NavigationStopped : navigasyon durduğunda çalışacak kodlar bu eventta belirtilir. StopLoading metodu çağrıldığında bu event çalışır.

    Getting Started with TFS in VS2010

    Earlier this month we announced the new Basic configuration for TFS.  This configuration gives you an easy to set up version of TFS that supports source control, work items, and builds.  It is a great place to migrate your Visual Source Safe assets and pick up new features in the process.  Now that we have released the formal Beta 2 bits for VS2010, I wanted to do a walk through on how to get started using the system. 

    This post will be most helpful for those of you who have not installed or used TFS before.  TFS can support sophisticated environments that include reporting, SharePoint integration, support across multiple domains, distributed databases, etc.  I’m not going to cover any of that here.  Instead my goal is to help you understand “why TFS” and how you can get started using the system.  In a future post I will walk through the process to convert a Visual Source Safe database into TFS in case you are coming from a VSS deployment today.

    First let’s talk about “why TFS?”  The goal of Team Foundation Server is to create a central repository with a set of tools that make it really easy to collaborate between roles.  You could try to stitch together multiple disparate systems as follows:

    image

    In this case each system has its own storage, own set of identity for assets, own commands and tools.  Getting this going is like trying to hook up a set of custom stereo components:  you can pull it off but it’s going to be a lot of work and you are missing out on some stuff.

    What I’d rather have is a system which can integrate these items together and then enable my default work flow through the system:

    image

    This integration enables some very common scenarios.  In a typical day I’m going to edit source code, build the product, test it, file bugs, fix them, lather/rinse/repeat.  When the entire work flow is supported with one integrated repository then all items can be related to each other.  As an example when I check in bug fixes I’d really like to see those defects resolved with the change set recorded (see sample below).

    The Basic configuration of TFS allows you to do precisely this which is a big improvement over just having source control.  The full version of TFS then adds new features including automated testing, virtual lab deployment, and architectural validation (to name a few).  This expands your work flow as follows:

    image

    You can decide which of these new components you add when you use the Visual Studio Premium and Ultimate SKUs. 

    There are many ways to access TFS.  As an engineer your most typical access point will be a version of Visual Studio.  But if you are a tester you can use the new Test and Lab Manager product (no VS install necessary).  If you are a project manager, you can also use the Web interface, Excel, Microsoft Project, or (new for VS2010) MOSS support for dashboards.  More on this later.

    In the rest of this post I will give you some step by step instructions on how to get going with TFS using the basic configuration for your first project.

    Getting Started

    Now that you have the conceptual level it’s time to hook things up.  You should start by doing the steps listed in Brian Harry’s TFS post here.  This will get all the required software on your machine with a default collection, creatively enough called DefaultCollection.

    At this point we can connect to the TFS collection from Visual Studio.  The easiest way to accomplish this is to use the Team Menu (you can also use the link from the home page):

    image

    You will be asked to find the Server where TFS lives.  In this case my Windows 7 machine is called JLZB2REL.  Add the server to the list using the Add button, then click Close:

    image

    At this point you can select the server from the combo box and then the DefaultCollection, then click Connect:

    image

    The Team Explorer tab will now have your server connection and DefaultCollection, but we don’t yet have a TFS Project to store anything in yet:

    image

    For this tutorial I have created a new Windows Form project to act as our sample solution (File, New Project, Windows Forms).  If you try to add this new code project to source control, you will get an error.  For example:

    image

    After you select the “Add Solution to Source Control” menu item you get the error “No team project source control folders are available.”:

    image

    The error isn’t that intuitive (especially given the word project is used for both TFS and inside your VS code solution but are different things).  The error means you must create an actual TFS project to contain all of the assets for your work.  In the Team Explorer, right click your collection and choose New Team Project:

    image

    In this case I’m going to create a TFS project for an Accounts Payable system.  This project will contain all of the solutions, data, etc required for the overall system.  After entering the data, click Next:

    image

    The Agile template is the default but you can also select CMMI.  You can find more details on MSDN about the project template types.  If you are using any agile methodologies (like TDD) this is a fine choice.  After making a choice, just click Finish:

    image

    You will get various status updates as the project is being created:

    image

    After success, click the Close button:

    image

    Team Explorer will now have the project ready to hold Work Items, Builds, and Source Control:

    image

    At this point you can update the project collection.  Let’s add the new solution to TFS again.  Right click the project in the Solution Explorer and select Add Solution to Source Control:

    image

    At this point you could create a new folder in TFS for the solution or just take the default.  When you are happy, click the OK button:

    image

    Upon success the Solution Explorer will now show the files are under source control (see the ‘+’ symbol in front of the files):

    image

    You will also see the list source control actions taken to publish the solution.  Add a comment and then click Check-In:

    image

    Confirm the check-in by clicking Yes.

    image

    At this point your new solution is in TFS and ready for Work Items.

    Work Items

    You can create work items directly inside Visual Studio using the Team Explorer, through the web front end, and the Test and Lab Management tool.  To look at your work items, open the Team Explorer and expand the Work Items, Team Queries item.  You can then double click any query (such as Active Bugs) to see any items available to you:

    image

    Our TFS project is empty so there are no active bugs in the list:

    image

    Let’s create a new item just to get us in business.  Select the Team, New Work Item menu.  Here you can create several types of work items to track features, defects, etc.  We’ll select Bug to get going:

    image

    Enter any data you want for the new bug and click Save Work Item to commit it to the database:

    image

    If you now refresh your active bug query list, you will see the new bug:

    image

    Let’s add a real bug to fix our project.  In my example I just created a default Windows Forms application.  We’ll want to update the title:

    image

    Now we need to fix the bug.  Navigate back to the Solution Explorer, select Form1.cs then choose Check Out for Edit:

    image

    Click the Check Out button to confirm:

    image

    The file will now have a check mark next to it so you know it is open for edit:

    image

    As you update the Text property of the main window, VS will automatically check out any dependent files for you:

    image

    This is a Windows Forms application but it works on all solution/project types.  Now that we are happy with the code change, select the Pending Changes tab in the bottom of VS:

    image

    In this case we are fixing a bug, so click the Work Items icon button:

    image

    Select bug #2 which tracks our title error.  We are going to resolve it with this check-in:

    image

    Add any comments you want and click Check-In, then Yes to confirm:

    image

    image

    If you refresh bug #2, you will now see the status changed to Resolved and the history is updated:

    image 

    Notice the change set (the set of source control changes) have been automatically included in the history.

    image

    At this point you could continue to create and fix bugs as required to ship your solution. 

    Other Ways to Explore TFS

    I mentioned that you don’t have to use VS itself to access the TFS repository.  We’ve done a lot of deep integration with other clients like the Web and Office.  As an example, I can simply pull up my web browser and go right to my server by using the server name (where 8080 is the default port):  http://jlzb2rel:8080/tfs/

    image

    At this point I can explore my collections and projects.  If you select the new AccountsPayable project we just created then the Continue button, you get more details.  In this case by navigating to the Work Items tab I can find the bugs in the system including our newly resolved bug:

    image

    This is a really easy way to explore your project from any machine with no setup required.  There is similar support for using Excel, Microsoft Project, etc.  This kind of access makes it easy for all people working on your project (engineers and project managers) to work together.

    At this point you have a very useful set of assets you can use to get your job done.  For those of you using VSS today, you might be very happy with just this level of support.  You can put down this tutorial now and come back later if you want to try some advanced features, for example the testing scenario I showed using beta 1 in this tutorial.

    Build Support

    The next typical part of your work flow will be to automate your builds for the product.  Assuming you followed Brian’s installation instructions, you now have local build support on your machine with TFS Basic.  The first step is to navigate to the Team Explorer, right click on Build Definitions, and select New Build Definition:

    image

    This will give us a set of definitions to fill out, just like a code project property page:

    image

    The Trigger page allows us to decide when builds are kicked off.  You have many options to pick from:

    • By default this is manual which is the option we will use.  We will have to kick of our own builds with this option.
    • Continuous Integration is very helpful when you want to have a new build after every check-in.  This allows you to validate new changes right away rather than waiting later when many check-ins get mixed together. 
    • Rolling builds give you a way to batch changes together which is very handy when builds are starting to take a while and you can’t afford to do every one. 
    • Gated Check-ins allow you to ensure all check-ins build before they are committed to TFS.  This makes sure you never expose a build break to other members of your team.
    • Scheduled builds are useful for getting a daily build out for the entire team to try out.

    You can create and use many different build definitions which allow you to employ different build types for different purposes.

    You can explore all the tabs at your leisure (each is fully documented with the product).  But we need to resolve the yellow warning sign on Build Defaults by giving the build a place to store our new build, in this case a public UNC I created on my machine:

    image

    Now you can save the build definition to TFS.  If we go back to the Team Explorer we can queue a build of the project:

    image

    We’ll get a confirmation dialog where we can just select the Queue button:

    image

    This will then queue off a build on my machine as shown by the status page:

    image  

    If you double click the queued build you will get the detailed status of the build:

    image

    From here you can see warnings and errors, view the log file, navigate to the drop, etc.  As an example if you select the “View Log File” link you can see the executed build script (subset):

    image

    If you select the Open Drop Folder link you will be taken to our drop location:

    image

    Now anyone can pick up the build and do their daily testing, release to a customer, etc.

    At this point you really have everything you need to make full use of the Basic configuration of TFS. 

    In the future I will do a tutorial on how to hook up the Virtual Lab system (part of Visual Studio Ultimate) which allows you to deploy complex applications to a Hyper-V environment and do automated testing.

    Creating a New TFS Collection

    [Note this section is totally optional]  You can store all of your work in one TFS Collection if you like.  If you are a Visual SourceSafe user today, this is just fine and you can skip this whole section.  But if you want to create a new top level collection, it is pretty easy.  The first step is to start then Team Foundation Administration Console: 

    image

    After the console starts, select the Team Project Collections item and click the “Create Team Project Collection” link:

    image

    Fill in a name for the project collection and any description you want, then click Next:

    image

    Accept the defaults for the data tier, then click Next:

    image

    The Basic configuration for TFS does not support Lab Management, so just click Next on this step:

    image

    At this point all the required data is configured and you can click the Verify button:

    image

    The verification step will ensure the collection can be created:

    image

    Once it passes, click the Create button:

    image

    This will provision all the required pieces of the TFS store per our configurations.  Click Next and you are done:

    image

    You will now see the new project collection with the default version:

    image

    WPF Apps With The Model-View-ViewModel Design Pattern

    Developing the user interface of a professional software application is not easy. It can be a murky blend of data, interaction design, visual design, connectivity, multithreading, security, internationalization, validation, unit testing, and a touch of voodoo. Considering that a user interface exposes the underlying system and must satisfy the unpredictable stylistic requirements of its users, it can be the most volatile area of many applications.
    There are popular design patterns that can help to tame this unwieldy beast, but properly separating and addressing the multitude of concerns can be difficult. The more complicated the patterns are, the more likely that shortcuts will be used later on which undermine all previous efforts to do things the right way.
    It is not always the design patterns at fault. Sometimes we use complicated design patterns, which require writing a lot of code because the UI platform in use does not lend itself well to a simpler pattern. What’s needed is a platform that makes it easy to build UIs using simple, time-tested, developer-approved design patterns. Fortunately, Windows Presentation Foundation (WPF) provides exactly that.
    As the software world continues to adopt WPF at an increasing rate, the WPF community has been developing its own ecosystem of patterns and practices. In this article, I’ll review some of those best practices for designing and implementing client applications with WPF. By leveraging some core features of WPF in conjunction with the Model-View-ViewModel (MVVM) design pattern, I will walk through an example program that demonstrates just how simple it can be to build a WPF application the “right way.”
    By the end of this article, it will be clear how data templates, commands, data binding, the resource system, and the MVVM pattern all fit together to create a simple, testable, robust framework on which any WPF application can thrive. The demonstration program that accompanies this article can serve as a template for a real WPF application that uses MVVM as its core architecture. The unit tests in the demo solution show how easy it is to test the functionality of an application’s user interface when that functionality exists in a set of ViewModel classes. Before diving into the details, let’s review why you should use a pattern like MVVM in the first place.

     

    Order vs. Chaos
    It is unnecessary and counterproductive to use design patterns in a simple “Hello, World!” program. Any competent developer can understand a few lines of code at a glance. However, as the number of features in a program increases, the number of lines of code and moving parts increase accordingly. Eventually, the complexity of a system, and the recurring problems it contains, encourages developers to organize their code in such a way that it is easier to comprehend, discuss, extend, and troubleshoot. We diminish the cognitive chaos of a complex system by applying well-known names to certain entities in the source code. We determine the name to apply to a piece of code by considering its functional role in the system.
    Developers often intentionally structure their code according to a design pattern, as opposed to letting the patterns emerge organically. There is nothing wrong with either approach, but in this article, I examine the benefits of explicitly using MVVM as the architecture of a WPF application. The names of certain classes include well-known terms from the MVVM pattern, such as ending with “ViewModel” if the class is an abstraction of a view. This approach helps avoid the cognitive chaos mentioned earlier. Instead, you can happily exist in a state of controlled chaos, which is the natural state of affairs in most professional software development projects!

     

    The Evolution of Model-View-ViewModel
    Ever since people started to create software user interfaces, there have been popular design patterns to help make it easier. For example, the Model-View-Presenter (MVP) pattern has enjoyed popularity on various UI programming platforms. MVP is a variation of the Model-View-Controller pattern, which has been around for decades. In case you have never used the MVP pattern before, here is a simplified explanation. What you see on the screen is the View, the data it displays is the model, and the Presenter hooks the two together. The view relies on a Presenter to populate it with model data, react to user input, provide input validation (perhaps by delegating to the model), and other such tasks. If you would like to learn more about the Model View Presenter, I suggest you read Jean-Paul Boodhoo’s August 2006 Design Patterns column.
    Back in 2004, Martin Fowler published an article about a pattern named Presentation Model (PM). The PM pattern is similar to MVP in that it separates a view from its behavior and state. The interesting part of the PM pattern is that an abstraction of a view is created, called the Presentation Model. A view, then, becomes merely a rendering of a Presentation Model. In Fowler’s explanation, he shows that the Presentation Model frequently updates its View, so that the two stay in sync with each other. That synchronization logic exists as code in the Presentation Model classes.
    In 2005, John Gossman, currently one of the WPF and Silverlight Architects at Microsoft, unveiled the Model-View-ViewModel (MVVM) pattern on his blog. MVVM is identical to Fowler’s Presentation Model, in that both patterns feature an abstraction of a View, which contains a View’s state and behavior. Fowler introduced Presentation Model as a means of creating a UI platform-independent abstraction of a View, whereas Gossman introduced MVVM as a standardized way to leverage core features of WPF to simplify the creation of user interfaces. In that sense, I consider MVVM to be a specialization of the more general PM pattern, tailor-made for the WPF and Silverlight platforms.
    In Glenn Block’s excellent article “Prism: Patterns for Building Composite Applications with WPF” in the September 2008 issue, he explains the Microsoft Composite Application Guidance for WPF. The term ViewModel is never used. Instead, the term Presentation Model is used to describe the abstraction of a view. Throughout this article, however, I’ll refer to the pattern as MVVM and the abstraction of a view as a ViewModel. I find this terminology is much more prevelant in the WPF and Silverlight communities.
    Unlike the Presenter in MVP, a ViewModel does not need a reference to a view. The view binds to properties on a ViewModel, which, in turn, exposes data contained in model objects and other state specific to the view. The bindings between view and ViewModel are simple to construct because a ViewModel object is set as the DataContext of a view. If property values in the ViewModel change, those new values automatically propagate to the view via data binding. When the user clicks a button in the View, a command on the ViewModel executes to perform the requested action. The ViewModel, never the View, performs all modifications made to the model data.
    The view classes have no idea that the model classes exist, while the ViewModel and model are unaware of the view. In fact, the model is completely oblivious to the fact that the ViewModel and view exist. This is a very loosely coupled design, which pays dividends in many ways, as you will soon see.

     

    Why WPF Developers Love MVVM
    Once a developer becomes comfortable with WPF and MVVM, it can be difficult to differentiate the two. MVVM is the lingua franca of WPF developers because it is well suited to the WPF platform, and WPF was designed to make it easy to build applications using the MVVM pattern (amongst others). In fact, Microsoft was using MVVM internally to develop WPF applications, such as Microsoft Expression Blend, while the core WPF platform was under construction. Many aspects of WPF, such as the look-less control model and data templates, utilize the strong separation of display from state and behavior promoted by MVVM.
    The single most important aspect of WPF that makes MVVM a great pattern to use is the data binding infrastructure. By binding properties of a view to a ViewModel, you get loose coupling between the two and entirely remove the need for writing code in a ViewModel that directly updates a view. The data binding system also supports input validation, which provides a standardized way of transmitting validation errors to a view.
    Two other features of WPF that make this pattern so usable are data templates and the resource system. Data templates apply Views to ViewModel objects shown in the user interface. You can declare templates in XAML and let the resource system automatically locate and apply those templates for you at run time. You can learn more about binding and data templates in my July 2008 article, “Data and WPF: Customize Data Display with Data Binding and WPF.”
    If it were not for the support for commands in WPF, the MVVM pattern would be much less powerful. In this article, I will show you how a ViewModel can expose commands to a View, thus allowing the view to consume its functionality. If you aren’t familiar with commanding, I recommend that you read Brian Noyes’s comprehensive article, “Advanced WPF: Understanding Routed Events and Commands in WPF,” from the September 2008 issue.
    In addition to the WPF (and Silverlight 2) features that make MVVM a natural way to structure an application, the pattern is also popular because ViewModel classes are easy to unit test. When an application’s interaction logic lives in a set of ViewModel classes, you can easily write code that tests it. In a sense, Views and unit tests are just two different types of ViewModel consumers. Having a suite of tests for an application’s ViewModels provides free and fast regression testing, which helps reduce the cost of maintaining an application over time.
    In addition to promoting the creation of automated regression tests, the testability of ViewModel classes can assist in properly designing user interfaces that are easy to skin. When you are designing an application, you can often decide whether something should be in the view or the ViewModel by imagining that you want to write a unit test to consume the ViewModel. If you can write unit tests for the ViewModel without creating any UI objects, you can also completely skin the ViewModel because it has no dependencies on specific visual elements.
    Lastly, for developers who work with visual designers, using MVVM makes it much easier to create a smooth designer/developer workflow. Since a view is just an arbitrary consumer of a ViewModel, it is easy to just rip one view out and drop in a new view to render a ViewModel. This simple step allows for rapid prototyping and evaluation of user interfaces made by the designers.
    The development team can focus on creating robust ViewModel classes, and the design team can focus on making user-friendly Views. Connecting the output of both teams can involve little more than ensuring that the correct bindings exist in a view’s XAML file.

     

    The Demo Application
    At this point, I have reviewed MVVM’s history and theory of operation. I also examined why it is so popular amongst WPF developers. Now it is time to roll up your sleeves and see the pattern in action. The demo application that accompanies this article uses MVVM in a variety of ways. It provides a fertile source of examples to help put the concepts into a meaningful context. I created the demo application in Visual Studio 2008 SP1, against the Microsoft .NET Framework 3.5 SP1. The unit tests run in the Visual Studio unit testing system.
    The application can contain any number of “workspaces,” each of which the user can open by clicking on a command link in the navigation area on the left. All workspaces live in a TabControl on the main content area. The user can close a workspace by clicking the Close button on that workspace’s tab item. The application has two available workspaces: “All Customers” and “New Customer.” After running the application and opening some workspaces, the UI looks something like Figure 1.
    Figure 1 Workspaces
    Only one instance of the “All Customers” workspace can be open at a time, but any number of “New Customer” workspaces can be open at once. When the user decides to create a new customer, she must fill in the data entry form in Figure 2.
    Figure 2 New Customer Data Entry Form
    After filling in the data entry form with valid values and clicking the Save button, the new customer’s name appears in the tab item and that customer is added to the list of all customers. The application does not have support for deleting or editing an existing customer, but that functionality, and many other features similar to it, are easy to implement by building on top of the existing application architecture. Now that you have a high-level understanding of what the demo application does, let’s investigate how it was designed and implemented.

     

    Relaying Command Logic
    Every view in the app has an empty codebehind file, except for the standard boilerplate code that calls InitializeComponent in the class’s constructor. In fact, you could remove the views’ codebehind files from the project and the application would still compile and run correctly. Despite the lack of event handling methods in the views, when the user clicks on buttons, the application reacts and satisfies the user’s requests. This works because of bindings that were established on the Command property of Hyperlink, Button, and MenuItem controls displayed in the UI. Those bindings ensure that when the user clicks on the controls, ICommand objects exposed by the ViewModel execute. You can think of the command object as an adapter that makes it easy to consume a ViewModel’s functionality from a view declared in XAML.
    When a ViewModel exposes an instance property of type I­Command, the command object typically uses that ViewModel object to get its job done. One possible implementation pattern is to create a private nested class within the ViewModel class, so that the command has access to private members of its containing ViewModel and does not pollute the namespace. That nested class implements the ICommand interface, and a reference to the containing ViewModel object is injected into its constructor. However, creating a nested class that implements ICommand for each command exposed by a ViewModel can bloat the size of the ViewModel class. More code means a greater potential for bugs.
    In the demo application, the RelayCommand class solves this problem. RelayCommand allows you to inject the command’s logic via delegates passed into its constructor. This approach allows for terse, concise command implementation in ViewModel classes. RelayCommand is a simplified variation of the DelegateCommand found in the Microsoft Composite Application Library. The Relay­Command class is shown in Figure 3.
     Figure 3 The RelayCommand Class
    public class RelayCommand : ICommand
    {
        #region Fields
    
        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;        
    
        #endregion // Fields
    
        #region Constructors
    
        public RelayCommand(Action<object> execute)
        : this(execute, null)
        {
        }
    
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
    
            _execute = execute;
            _canExecute = canExecute;
        }
        #endregion // Constructors
    
        #region ICommand Members
    
        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }
    
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
    
        #endregion // ICommand Members
    }
    The CanExecuteChanged event, which is part of the ICommand interface implementation, has some interesting features. It delegates the event subscription to the CommandManager.RequerySuggested event. This ensures that the WPF commanding infrastructure asks all RelayCommand objects if they can execute whenever it asks the built-in commands. The following code from the CustomerViewModel class, which I will examine in-depth later, shows how to configure a RelayCommand with lambda expressions:
    RelayCommand _saveCommand;
    public ICommand SaveCommand
    {
        get
        {
            if (_saveCommand == null)
            {
                _saveCommand = new RelayCommand(param => this.Save(),
                    param => this.CanSave );
            }
            return _saveCommand;
        }
    }

     

    ViewModel Class Hierarchy
    Most ViewModel classes need the same features. They often need to implement the INotifyPropertyChanged interface, they usually need to have a user-friendly display name, and, in the case of workspaces, they need the ability to close (that is, be removed from the UI). This problem naturally lends itself to the creations of a ViewModel base class or two, so that new ViewModel classes can inherit all of the common functionality from a base class. The ViewModel classes form the inheritance hierarchy seen in Figure 4.
    Figure 4 Inheritance Hierarchy
    Having a base class for all of your ViewModels is by no means a requirement. If you prefer to gain features in your classes by composing many smaller classes together, instead of using inheritance, that is not a problem. Just like any other design pattern, MVVM is a set of guidelines, not rules.

     

    ViewModelBase Class
    ViewModelBase is the root class in the hierarchy, which is why it implements the commonly used INotifyPropertyChanged interface and has a DisplayName property. The INotifyPropertyChanged interface contains an event called PropertyChanged. Whenever a property on a ViewModel object has a new value, it can raise the PropertyChanged event to notify the WPF binding system of the new value. Upon receiving that notification, the binding system queries the property, and the bound property on some UI element receives the new value.
    In order for WPF to know which property on the ViewModel object has changed, the PropertyChangedEventArgs class exposes a PropertyName property of type String. You must be careful to pass the correct property name into that event argument; otherwise, WPF will end up querying the wrong property for a new value.
    One interesting aspect of ViewModelBase is that it provides the ability to verify that a property with a given name actually exists on the ViewModel object. This is very useful when refactoring, because changing a property’s name via the Visual Studio 2008 refactoring feature will not update strings in your source code that happen to contain that property’s name (nor should it). Raising the PropertyChanged event with an incorrect property name in the event argument can lead to subtle bugs that are difficult to track down, so this little feature can be a huge timesaver. The code from ViewModelBase that adds this useful support is shown in Figure 5.
     Figure 5 Verifying a Property
    // In ViewModelBase.cs
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected virtual void OnPropertyChanged(string propertyName)
    {
        this.VerifyPropertyName(propertyName);
    
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }
    
    [Conditional("DEBUG")]
    [DebuggerStepThrough]
    public void VerifyPropertyName(string propertyName)
    {
        // Verify that the property name matches a real,
        // public, instance property on this object.
        if (TypeDescriptor.GetProperties(this)[propertyName] == null)
        {
            string msg = "Invalid property name: " + propertyName;
    
            if (this.ThrowOnInvalidPropertyName)
                throw new Exception(msg);
            else
                Debug.Fail(msg);
        }
    }

     

    CommandViewModel Class
    The simplest concrete ViewModelBase subclass is CommandViewModel. It exposes a property called Command of type I­Command. MainWindowViewModel exposes a collection of these objects through its Commands property. The navigation area on the left-hand side of the main window displays a link for each CommandViewModel exposed by MainWindowView­Model, such as “View all customers” and “Create new customer.” When the user clicks on a link, thus executing one of those commands, a workspace opens in the TabControl on the main window. The Command­ViewModel class definition is shown here:
    public class CommandViewModel : ViewModelBase
    {
        public CommandViewModel(string displayName, ICommand command)
        {
            if (command == null)
                throw new ArgumentNullException("command");
    
            base.DisplayName = displayName;
            this.Command = command;
        }
    
        public ICommand Command { get; private set; }
    }
    In the MainWindowResources.xaml file there exists a Data­Template whose key is “CommandsTemplate”. MainWindow uses that template to render the collection of CommandViewModels mentioned earlier. The template simply renders each CommandViewModel object as a link in an ItemsControl. Each Hyperlink’s Command property is bound to the Command property of a Command­ViewModel. That XAML is shown in Figure 6.
     Figure 6 Render the List of Commands
    <!-- In MainWindowResources.xaml -->
    <!--
    This template explains how to render the list of commands on
    the left side in the main window (the 'Control Panel' area).
    -->
    <DataTemplate x:Key="CommandsTemplate">
      <ItemsControl ItemsSource="{Binding Path=Commands}">
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <TextBlock Margin="2,6">
              <Hyperlink Command="{Binding Path=Command}">
                <TextBlock Text="{Binding Path=DisplayName}" />
              </Hyperlink>
            </TextBlock>
          </DataTemplate>
        </ItemsControl.ItemTemplate>
      </ItemsControl>
    </DataTemplate>

     

    MainWindowViewModel Class
    As previously seen in the class diagram, the WorkspaceViewModel class derives from ViewModelBase and adds the ability to close. By “close,” I mean that something removes the workspace from the user interface at run time. Three classes derive from WorkspaceViewModel: MainWindowViewModel, AllCustomersViewModel, and CustomerViewModel. MainWindowViewModel’s request to close is handled by the App class, which creates the MainWindow and its ViewModel, as seen in Figure 7.
     Figure 7 Create the ViewModel
    // In App.xaml.cs
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
    
        MainWindow window = new MainWindow();
    
        // Create the ViewModel to which
        // the main window binds.
        string path = "Data/customers.xml";
        var viewModel = new MainWindowViewModel(path);
    
        // When the ViewModel asks to be closed,
        // close the window.
        viewModel.RequestClose += delegate
        {
            window.Close();
        };
    
        // Allow all controls in the window to
        // bind to the ViewModel by setting the
        // DataContext, which propagates down
        // the element tree.
        window.DataContext = viewModel;
    
        window.Show();
    }
    MainWindow contains a menu item whose Command property is bound to the MainWindowViewModel’s CloseCommand property. When the user clicks on that menu item, the App class responds by calling the window’s Close method, like so:
    <!-- In MainWindow.xaml -->
    <Menu>
      <MenuItem Header="_File">
        <MenuItem Header="_Exit" Command="{Binding Path=CloseCommand}" />
      </MenuItem>
      <MenuItem Header="_Edit" />
      <MenuItem Header="_Options" />
      <MenuItem Header="_Help" />
    </Menu>
    MainWindowViewModel contains an observable collection of WorkspaceViewModel objects, called Workspaces. The main window contains a TabControl whose ItemsSource property is bound to that collection. Each tab item has a Close button whose Command property is bound to the CloseCommand of its corresponding WorkspaceViewModel instance. An abridged version of the template that configures each tab item is shown in the code that follows. The code is found in MainWindowResources.xaml, and the template explains how to render a tab item with a Close button:
    <DataTemplate x:Key="ClosableTabItemTemplate">
      <DockPanel Width="120">
        <Button
          Command="{Binding Path=CloseCommand}"
          Content="X"
          DockPanel.Dock="Right"
          Width="16" Height="16"
          />
        <ContentPresenter Content="{Binding Path=DisplayName}" />
      </DockPanel>
    </DataTemplate>
    When the user clicks the Close button in a tab item, that Workspace­ViewModel’s CloseCommand executes, causing its Request­Close event to fire. MainWindowViewModel monitors the RequestClose event of its workspaces and removes the workspace from the Workspaces collection upon request. Since the Main­Window’s TabControl has its ItemsSource property bound to the observable collection of WorkspaceViewModels, removing an item from the collection causes the corresponding workspace to be removed from the TabControl. That logic from Main­WindowViewModel is shown in Figure 8.
     Figure 8 Removing Workspace from the UI
    // In MainWindowViewModel.cs
    
    ObservableCollection<WorkspaceViewModel> _workspaces;
    
    public ObservableCollection<WorkspaceViewModel> Workspaces
    {
        get
        {
            if (_workspaces == null)
            {
                _workspaces = new ObservableCollection<WorkspaceViewModel>();
                _workspaces.CollectionChanged += this.OnWorkspacesChanged;
            }
            return _workspaces;
        }
    }
    
    void OnWorkspacesChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null && e.NewItems.Count != 0)
            foreach (WorkspaceViewModel workspace in e.NewItems)
                workspace.RequestClose += this.OnWorkspaceRequestClose;
    
        if (e.OldItems != null && e.OldItems.Count != 0)
            foreach (WorkspaceViewModel workspace in e.OldItems)
                workspace.RequestClose -= this.OnWorkspaceRequestClose;
    }
    
    void OnWorkspaceRequestClose(object sender, EventArgs e)
    {
        this.Workspaces.Remove(sender as WorkspaceViewModel);
    }
    In the UnitTests project, the MainWindowViewModelTests.cs file contains a test method that verifies that this functionality is working properly. The ease with which you can create unit tests for ViewModel classes is a huge selling point of the MVVM pattern, because it allows for simple testing of application functionality without writing code that touches the UI. That test method is shown in Figure 9.
     Figure 9 The Test Method
    // In MainWindowViewModelTests.cs
    [TestMethod]
    public void TestCloseAllCustomersWorkspace()
    {
        // Create the MainWindowViewModel, but not the MainWindow.
        MainWindowViewModel target =
            new MainWindowViewModel(Constants.CUSTOMER_DATA_FILE);
    
        Assert.AreEqual(0, target.Workspaces.Count, "Workspaces isn't empty.");
    
        // Find the command that opens the "All Customers" workspace.
        CommandViewModel commandVM =
            target.Commands.First(cvm => cvm.DisplayName == "View all customers");
    
        // Open the "All Customers" workspace.
        commandVM.Command.Execute(null);
        Assert.AreEqual(1, target.Workspaces.Count, "Did not create viewmodel.");
    
        // Ensure the correct type of workspace was created.
        var allCustomersVM = target.Workspaces[0] as AllCustomersViewModel;
        Assert.IsNotNull(allCustomersVM, "Wrong viewmodel type created.");
    
        // Tell the "All Customers" workspace to close.
        allCustomersVM.CloseCommand.Execute(null);
        Assert.AreEqual(0, target.Workspaces.Count, "Did not close viewmodel.");
    }

     

    Applying a View to a ViewModel
    MainWindowViewModel indirectly adds and removes Workspace­ViewModel objects to and from the main window’s Tab­Control. By relying on data binding, the Content property of a TabItem receives a ViewModelBase-derived object to display. ViewModelBase is not a UI element, so it has no inherent support for rendering itself. By default, in WPF a non-visual object is rendered by displaying the results of a call to its ToString method in a TextBlock. That clearly is not what you need, unless your users have a burning desire to see the type name of our ViewModel classes!
    You can easily tell WPF how to render a ViewModel object by using typed DataTemplates. A typed DataTemplate does not have an x:Key value assigned to it, but it does have its DataType property set to an instance of the Type class. If WPF tries to render one of your ViewModel objects, it will check to see if the resource system has a typed DataTemplate in scope whose DataType is the same as (or a base class of) the type of your ViewModel object. If it finds one, it uses that template to render the ViewModel object referenced by the tab item’s Content property.
    The MainWindowResources.xaml file has a Resource­Dictionary. That dictionary is added to the main window’s resource hierarchy, which means that the resources it contains are in the window’s resource scope. When a tab item’s content is set to a ViewModel object, a typed DataTemplate from this dictionary supplies a view (that is, a user control) to render it, as shown in Figure 10.
     Figure 10 Supplying a View
    <!--
    This resource dictionary is used by the MainWindow.
    -->
    <ResourceDictionary
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:vm="clr-namespace:DemoApp.ViewModel"
      xmlns:vw="clr-namespace:DemoApp.View"
      >
    
      <!--
      This template applies an AllCustomersView to an instance
      of the AllCustomersViewModel class shown in the main window.
      -->
      <DataTemplate DataType="{x:Type vm:AllCustomersViewModel}">
        <vw:AllCustomersView />
      </DataTemplate>
    
      <!--
      This template applies a CustomerView to an instance
      of the CustomerViewModel class shown in the main window.
      -->
      <DataTemplate DataType="{x:Type vm:CustomerViewModel}">
        <vw:CustomerView />
      </DataTemplate>
    
     <!-- Other resources omitted for clarity... -->
    
    </ResourceDictionary>
    You do not need to write any code that determines which view to show for a ViewModel object. The WPF resource system does all of the heavy lifting for you, freeing you up to focus on more important things. In more complex scenarios, it is possible to programmatically select the view, but in most situations that is unnecessary.

     

    The Data Model and Repository
    You have seen how ViewModel objects are loaded, displayed, and closed by the application shell. Now that the general plumbing is in place, you can review implementation details more specific to the domain of the application. Before getting deep into the application’s two workspaces, “All Customers” and “New Customer,” let’s first examine the data model and data access classes. The design of those classes has almost nothing to do with the MVVM pattern, because you can create a ViewModel class to adapt just about any data object into something friendly to WPF.
    The sole model class in the demo program is Customer. That class has a handful of properties that represent information about a customer of a company, such as their first name, last name, and e-mail address. It provides validation messages by implementing the standard IDataErrorInfo interface, which existed for years before WPF hit the street. The Customer class has nothing in it that suggests it is being used in an MVVM architecture or even in a WPF application. The class could easily have come from a legacy business library.
    Data must come from and reside somewhere. In this application, an instance of the CustomerRepository class loads and stores all Customer objects. It happens to load the customer data from an XML file, but the type of external data source is irrelevant. The data could come from a database, a Web service, a named pipe, a file on disk, or even carrier pigeons: it simply does not matter. As long as you have a .NET object with some data in it, regardless of where it came from, the MVVM pattern can get that data on the screen.
    The CustomerRepository class exposes a few methods that allow you to get all the available Customer objects, add new a Customer to the repository, and check if a Customer is already in the repository. Since the application does not allow the user to delete a customer, the repository does not allow you to remove a customer. The CustomerAdded event fires when a new Customer enters the CustomerRepository, via the AddCustomer method.
    Clearly, this application’s data model is very small, compared to what real business applications require, but that is not important. What is important to understand is how the ViewModel classes make use of Customer and CustomerRepository. Note that Customer­ViewModel is a wrapper around a Customer object. It exposes the state of a Customer, and other state used by the Customer­View control, through a set of properties. CustomerViewModel does not duplicate the state of a Customer; it simply exposes it via delegation, like this:
    public string FirstName
    {
        get { return _customer.FirstName; }
        set
        {
            if (value == _customer.FirstName)
                return;
            _customer.FirstName = value;
            base.OnPropertyChanged("FirstName");
        }
    }
    When the user creates a new customer and clicks the Save button in the CustomerView control, the Customer­ViewModel associated with that view will add the new Customer object to the Customer­Repository. That causes the repository’s CustomerAdded event to fire, which lets the AllCustomers­ViewModel know that it should add a new Customer­ViewModel to its AllCustomers collection. In a sense, Customer­Repository acts as a synchronization mechanism between various ViewModels that deal with Customer objects. Perhaps one might think of this as using the Mediator design pattern. I will review more of how this works in the upcoming sections, but for now refer to the diagram in Figure 11 for a high-level understanding of how all the pieces fit together.
    Figure 11 Customer Relationships

     

    New Customer Data Entry Form
    When the user clicks the “Create new customer” link, MainWindowViewModel adds a new CustomerViewModel to its list of workspaces, and a CustomerView control displays it. After the user types valid values into the input fields, the Save button enters the enabled state so that the user can persist the new customer information. There is nothing out of the ordinary here, just a regular data entry form with input validation and a Save button.
    The Customer class has built-in validation support, available through its IDataErrorInfo interface implementation. That validation ensures the customer has a first name, a well-formed e-mail address, and, if the customer is a person, a last name. If the Customer’s IsCompany property returns true, the LastName property cannot have a value (the idea being that a company does not have a last name). This validation logic might make sense from the Customer object’s perspective, but it does not meet the needs of the user interface. The UI requires a user to select whether a new customer is a person or a company. The Customer Type selector initially has the value “(Not Specified)”. How can the UI tell the user that the customer type is unspecified if the IsCompany property of a Customer only allows for a true or false value?
    Assuming you have complete control over the entire software system, you could change the IsCompany property to be of type Nullable<bool>, which would allow for the “unselected” value. However, the real world is not always so simple. Suppose you cannot change the Customer class because it comes from a legacy library owned by a different team in your company. What if there is no easy way to persist that “unselected” value because of the existing database schema? What if other applications already use the Customer class and rely on the property being a normal Boolean value? Once again, having a ViewModel comes to the rescue.
    The test method in Figure 12 shows how this functionality works in CustomerViewModel. CustomerViewModel exposes a CustomerTypeOptions property so that the Customer Type selector has three strings to display. It also exposes a CustomerType property, which stores the selected String in the selector. When CustomerType is set, it maps the String value to a Boolean value for the underlying Customer object’s IsCompany property. Figure 13 shows the two properties.
     Figure 12 The Test Method
    // In CustomerViewModelTests.cs
    [TestMethod]
    public void TestCustomerType()
    {
        Customer cust = Customer.CreateNewCustomer();
        CustomerRepository repos = new CustomerRepository(
            Constants.CUSTOMER_DATA_FILE);
        CustomerViewModel target = new CustomerViewModel(cust, repos);
    
        target.CustomerType = "Company"
        Assert.IsTrue(cust.IsCompany, "Should be a company");
    
        target.CustomerType = "Person";
        Assert.IsFalse(cust.IsCompany, "Should be a person");
    
        target.CustomerType = "(Not Specified)";
        string error = (target as IDataErrorInfo)["CustomerType"];
        Assert.IsFalse(String.IsNullOrEmpty(error), "Error message should
            be returned");
    }
     Figure 13 CustomerType Properties
    // In CustomerViewModel.cs
    
    public string[] CustomerTypeOptions
    {
        get
        {
            if (_customerTypeOptions == null)
            {
                _customerTypeOptions = new string[]
                {
                    "(Not Specified)",
                    "Person",
                    "Company"
                };
            }
            return _customerTypeOptions;
        }
    }
    public string CustomerType
    {
        get { return _customerType; }
        set
        {
            if (value == _customerType ||
                String.IsNullOrEmpty(value))
                return;
    
            _customerType = value;
    
            if (_customerType == "Company")
            {
                _customer.IsCompany = true;
            }
            else if (_customerType == "Person")
            {
                _customer.IsCompany = false;
            }
    
            base.OnPropertyChanged("CustomerType");
            base.OnPropertyChanged("LastName");
        }
    }
    The CustomerView control contains a ComboBox that is bound to those properties, as seen here:
    <ComboBox
      ItemsSource="{Binding CustomerTypeOptions}"
      SelectedItem="{Binding CustomerType, ValidatesOnDataErrors=True}"
      />
    When the selected item in that ComboBox changes, the data source’s IDataErrorInfo interface is queried to see if the new value is valid. That occurs because the SelectedItem property binding has ValidatesOnDataErrors set to true. Since the data source is a Customer­ViewModel object, the binding system asks that Customer­ViewModel for a validation error on the CustomerType property. Most of the time, CustomerViewModel delegates all requests for validation errors to the Customer object it contains. However, since Customer has no notion of having an unselected state for the IsCompany property, the CustomerViewModel class must handle validating the new selected item in the ComboBox control. That code is seen in Figure 14.
     Figure 14 Validating a CustomerViewModel Object
    // In CustomerViewModel.cs
    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            string error = null;
    
            if (propertyName == "CustomerType")
            {
                // The IsCompany property of the Customer class
                // is Boolean, so it has no concept of being in
                // an "unselected" state. The CustomerViewModel
                // class handles this mapping and validation.
                error = this.ValidateCustomerType();
            }
            else
            {
                error = (_customer as IDataErrorInfo)[propertyName];
            }
    
            // Dirty the commands registered with CommandManager,
            // such as our Save command, so that they are queried
            // to see if they can execute now.
            CommandManager.InvalidateRequerySuggested();
    
            return error;
        }
    }
    
    string ValidateCustomerType()
    {
        if (this.CustomerType == "Company" ||
           this.CustomerType == "Person")
            return null;
    
        return "Customer type must be selected";
    }
    The key aspect of this code is that CustomerViewModel’s implementation of IDataErrorInfo can handle requests for ViewModel-specific property validation and delegate the other requests to the Customer object. This allows you to make use of validation logic in Model classes and have additional validation for properties that only make sense to ViewModel classes.
    The ability to save a CustomerViewModel is available to a view through the SaveCommand property. That command uses the RelayCommand class examined earlier to allow CustomerViewModel to decide if it can save itself and what to do when told to save its state. In this application, saving a new customer simply means adding it to a CustomerRepository. Deciding if the new customer is ready to be saved requires consent from two parties. The Customer object must be asked if it is valid or not, and the Customer­ViewModel must decide if it is valid. This two-part decision is necessary because of the ViewModel-specific properties and validation examined previously. The save logic for Customer­ViewModel is shown in Figure 15.
     Figure 15 The Save Logic for CustomerViewModel
    // In CustomerViewModel.cs
    public ICommand SaveCommand
    {
        get
        {
            if (_saveCommand == null)
            {
                _saveCommand = new RelayCommand(
                    param => this.Save(),
                    param => this.CanSave
                    );
            }
            return _saveCommand;
        }
    }
    
    public void Save()
    {
        if (!_customer.IsValid)
            throw new InvalidOperationException("...");
    
        if (this.IsNewCustomer)
            _customerRepository.AddCustomer(_customer);
    
        base.OnPropertyChanged("DisplayName");
    }
    
    bool IsNewCustomer
    {
        get
        {
            return !_customerRepository.ContainsCustomer(_customer);
        }
    }
    
    bool CanSave
    {
        get
        {
            return
                String.IsNullOrEmpty(this.ValidateCustomerType()) &&
                _customer.IsValid;
        }
    }
    The use of a ViewModel here makes it much easier to create a view that can display a Customer object and allow for things like an “unselected” state of a Boolean property. It also provides the ability to easily tell the customer to save its state. If the view were bound directly to a Customer object, the view would require a lot of code to make this work properly. In a well-designed MVVM architecture, the codebehind for most Views should be empty, or, at most, only contain code that manipulates the controls and resources contained within that view. Sometimes it is also necessary to write code in a View’s codebehind that interacts with a ViewModel object, such as hooking an event or calling a method that would otherwise be very difficult to invoke from the ViewModel itself.

     

    All Customers View
    The demo application also contains a workspace that displays all of the customers in a ListView. The customers in the list are grouped according to whether they are a company or a person. The user can select one or more customers at a time and view the sum of their total sales in the bottom right corner.
    The UI is the AllCustomersView control, which renders an AllCustomersViewModel object. Each ListView­Item represents a CustomerViewModel object in the AllCustomers collection exposed by the AllCustomerViewModel object. In the previous section, you saw how a CustomerViewModel can render as a data entry form, and now the exact same CustomerViewModel object is rendered as an item in a ListView. The CustomerViewModel class has no idea what visual elements display it, which is why this reuse is possible.
    AllCustomersView creates the groups seen in the ListView. It accomplishes this by binding the ListView’s ItemsSource to a Collection­ViewSource configured like Figure 16.
     Figure 16 CollectionViewSource
    <!-- In AllCustomersView.xaml -->
    <CollectionViewSource
      x:Key="CustomerGroups"
      Source="{Binding Path=AllCustomers}"
      >
      <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="IsCompany" />
      </CollectionViewSource.GroupDescriptions>
      <CollectionViewSource.SortDescriptions>
        <!--
        Sort descending by IsCompany so that the ' True' values appear first,
        which means that companies will always be listed before people.
        -->
        <scm:SortDescription PropertyName="IsCompany" Direction="Descending" />
        <scm:SortDescription PropertyName="DisplayName" Direction="Ascending" />
      </CollectionViewSource.SortDescriptions>
    </CollectionViewSource>
    The association between a ListViewItem and a CustomerViewModel object is established by the ListView’s ItemContainerStyle property. The Style assigned to that property is applied to each ListViewItem, which enables properties on a ListViewItem to be bound to properties on the CustomerViewModel. One important binding in that Style creates a link between the IsSelected property of a ListViewItem and the IsSelected property of a Customer­ViewModel, as seen here:
    <Style x:Key="CustomerItemStyle" TargetType="{x:Type ListViewItem}">
      <!--   Stretch the content of each cell so that we can
      right-align text in the Total Sales column.  -->
      <Setter Property="HorizontalContentAlignment" Value="Stretch" />
      <!--
      Bind the IsSelected property of a ListViewItem to the
      IsSelected property of a CustomerViewModel object.
      -->
      <Setter Property="IsSelected" Value="{Binding Path=IsSelected,
        Mode=TwoWay}" />
    </Style>
    When a CustomerViewModel is selected or unselected, that causes the sum of all selected customers’ total sales to change. The AllCustomersViewModel class is responsible for maintaining that value, so that the ContentPresenter beneath the ListView can display the correct number. Figure 17 shows how AllCustomersViewModel monitors each customer for being selected or unselected and notifies the view that it needs to update the display value.
     Figure 17 Monitoring for Selected or Unselected
    // In AllCustomersViewModel.cs
    public double TotalSelectedSales
    {
        get
        {
            return this.AllCustomers.Sum(
                custVM => custVM.IsSelected ? custVM.TotalSales : 0.0);
        }
    }
    
    void OnCustomerViewModelPropertyChanged(object sender,
        PropertyChangedEventArgs e)
    {
        string IsSelected = "IsSelected";
    
        // Make sure that the property name we're
        // referencing is valid.  This is a debugging
        // technique, and does not execute in a Release build.
        (sender as CustomerViewModel).VerifyPropertyName(IsSelected);
    
        // When a customer is selected or unselected, we must let the
        // world know that the TotalSelectedSales property has changed,
        // so that it will be queried again for a new value.
        if (e.PropertyName == IsSelected)
            this.OnPropertyChanged("TotalSelectedSales");
    }
    The UI binds to the TotalSelectedSales property and applies currency (monetary) formatting to the value. The ViewModel object could apply the currency formatting, instead of the view, by returning a String instead of a Double value from the TotalSelectedSales property. The ContentStringFormat property of ContentPresenter was added in the .NET Framework 3.5 SP1, so if you must target an older version of WPF, you will need to apply the currency formatting in code:
    <!-- In AllCustomersView.xaml -->
    <StackPanel Orientation="Horizontal">
      <TextBlock Text="Total selected sales: " />
      <ContentPresenter
        Content="{Binding Path=TotalSelectedSales}"
        ContentStringFormat="c"
      />
    </StackPanel>

     

    Wrapping Up
    WPF has a lot to offer application developers, and learning to leverage that power requires a mindset shift. The Model-View-ViewModel pattern is a simple and effective set of guidelines for designing and implementing a WPF application. It allows you to create a strong separation between data, behavior, and presentation, making it easier to control the chaos that is software development.
    Follow

    Get every new post delivered to your Inbox.