Monday, April 20, 2015

Putting a DatePicker in a datagrid

This is such a common requirement I'm surprised Microsoft hasn't added a DataGridDatePickerColumn but they haven't so every time we want a DatePicker in a DataGrid we have to go through the convolutions below...

<DataGrid ItemsSource="{Binding Path=Events}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Event Date" Width="200">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=EventDate,StringFormat=MM/dd/yyyy}"></TextBlock>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <DatePicker SelectedDate="{Binding Path=EventDate}"></DatePicker>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

Wednesday, April 15, 2015

Cannot find governing FrameworkElement or FrameworkContentElement for target element

I wrote this blog entry because I am fed up with having to remember the solution from scratch every time I get this error message. I happens whenever I try to set the ItemSource for a DataGridComboBoxColumn. Because data grid columns are not in the Visual Tree they don't inherit the data context from the page.

So the XAML below will always fail and you will see "Cannot find governing FrameworkElement or FrameworkContentElement for target element" in the output window. The problem is that the ItemsSource has no DataContext to work in.

<DataGridComboBoxColumn Header="Owner" Width="100" ItemsSource="{Binding Path=Owners}" DisplayMemberPath="OwnerName" SelectedValuePath="OwnerID" SelectedValueBinding="{Binding Path=OwnerID}"></DataGridComboBoxColumn>


There are many solutions out there on the web, but the easiest and most logical for me is to create a CollectionViewSource resource and reference that. To implement the XAML above, simply add the resource...

<Page.Resources>
    <CollectionViewSource x:Key="Owners" Source="{Binding Owners}"/>
</Page.Resources>

... and change the ItemsSource...

ItemsSource="{Binding Source={StaticResource Owners}}"

Sunday, April 12, 2015

MVVM Part 7

Making the DataGrid editable

To make the DataGrid editable we take off the IsReadOnly attribute, set CanUserAddRows and CanUserDeleteRows true, and set the EmployeeID read only. The new DataGrid definition looks like this...

<DataGrid Name="dg" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Employees}" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True">
    <DataGrid.Columns>
        <DataGridTextColumn Header="ID" Binding="{Binding Path=EmployeeID}" IsReadOnly="True"/>
        <DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName}"></DataGridTextColumn>
        <DataGridTextColumn Header="Last Name" Binding="{Binding Path=LastName}"></DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

If you run the application now you can modify Billy Bee and click [Save]. The changes are saved to the database. Try adding a new name in the blank row and clicking [Save]. Hmm - the new employee isn't saved to the database.

It turns out that we have to work harder to implement adding and removing rows. We need to provide an event handler for the ObservableCollection's CollectionChanged event to add new rows and remove deleted rows from the Model's employee table.

    1. At the end of the constructor add the following line of code...
        AddHandler Employees.CollectionChanged, AddressOf EmployeesChanged

    2. The event handler looks like this. Add it somewhere in the ViewModel class.
    Protected Sub EmployeesChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
        Select Case e.Action
            Case NotifyCollectionChangedAction.Add
                For Each Emp As Employee In e.NewItems
                    Model.Employees.Add(Emp)
                Next
            Case NotifyCollectionChangedAction.Remove
                For Each Emp As Employee In e.OldItems
                    Model.Employees.Remove(Emp)
                Next
        End Select
    End Sub


MVVM Part 6

Binding a DataGrid

Let's add a new requirement. We also need to display a list of employees in a data grid. Now some of you are probably thinking that I am very skilled at creating badly designed screens but I've learned from the absolute masters of bad screen design - my business analysis team.

    1. We will start by adding a new table to our database with the following SQL script.
USE [MVVM_Walkthrough]
GO

CREATE TABLE [dbo].[Employee](
[EmployeeID] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [varchar](50) NULL,
[LastName] [varchar](50) NULL,
 CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED 
(
[EmployeeID] ASC
) ON [PRIMARY]

    2. Let's add a row to the table
INSERT INTO Employee VALUES ('Billy', 'Bee')

    3. In the solution explorer double-click on the MVVM_Walkthrough_Model.edmx
    4. In the edmx client area right-click and select "Update Model from Database"
    5. Check the Employee table and click [Finish]
    6. Now we have updated our Model we need to update the ViewModel. Add the following code to the constructor.
     Dim emp = (From a In Model.Employees Select a).ToList()
  Me.Employees = New ObservableCollection(Of Employee)(emp)

     7. Now we need a private member and a public property like this...
    Private _Employees As ObservableCollection(Of Employee)
    Public Property Employees As ObservableCollection(Of Employee)
        Get
            Return _Employees
        End Get
        Set(value As ObservableCollection(Of Employee))
            If Not value.Equals(_Employees) Then
                _Employees = value
                NotifyPropertyChanged("Employees")
            End If
        End Set
    End Property

    8. Build the application. Now we can update the view. Add another RowDefinition (there are now four) and make the height of the third row Height="200". Move the button down to Grid.Row="3".
    9. Add a DataGrid like this...
   <DataGrid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Employees}" AutoGenerateColumns="False" IsReadOnly="True">
       <DataGrid.Columns>
           <DataGridTextColumn Header="ID" Binding="{Binding Path=EmployeeID}"/>
           <DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName}"></DataGridTextColumn>
           <DataGridTextColumn Header="Last Name" Binding="{Binding Path=LastName}"></DataGridTextColumn>
       </DataGrid.Columns>
   </DataGrid>

Nothing surprising here. If we run the application now we can see Billy Bee in the data grid.


In the next blog entry we will discover what it would take to make the data grid editable.

MVVM Part 5

Binding a ComboBox

We have a new requirement. We want to add a configuration entry called DefaultColor. It can take one of three values - "Black", "Blue" or "Red". When the user selects a new DefaultColor we will save it in the Config table and change the color of the labels.

Note: To keep this as simple as possible I have not implemented an ideal database design.

    1. Create a new table in the MVVM_Walkthrough database and populate it with the following SQL script.
USE [MVVM_Walkthrough]
GO

CREATE TABLE [dbo].[Colors](
[ColorName] [varchar](50) NOT NULL,
 CONSTRAINT [PK_Colors] PRIMARY KEY CLUSTERED 
(
[Color] ASC
) ON [PRIMARY]
GO

    2. Now populate the new table
INSERT INTO Colors VALUES ('Black')
INSERT INTO Colors VALUES ('Blue')
INSERT INTO Colors VALUES ('Red')

    3. We always work from the Model to the ViewModel to the View. In the solution explorer double-click on MVVM_Walkthrough_Model.edmx
    4. Right click in the Model's client area and select "Update Model from Database"
    5. In the Update Wizard open the tables and select the Colors table. Click [Finish]
    6. You now have a new table called Color in your Model.


    7. Now we can update the ViewModel. We need to create a new config type property that will hold a default color that was selected from the list of Colors. Add the following code in the MainWindowViewModel constructor. This will initialize a property called DefaultColor.

        Dim dc = (From a In Model.Configs Select a Where a.[Key] = "DefaultColor").ToList()
        If dc.Count > 0 Then
            Me.DefaultColor = dc(0)
        Else
            DefaultColor = New Config()
            DefaultColor.Key = "DefaultColor"
            DefaultColor.Value = "Black"
            Model.Configs.Add(DefaultColor)
        End If

    8. Now we need to define this new property. Like the ApplicationName property there is a private member and a public property. The definition looks like this...

    Private _DefaultColor As Config
    Public Property DefaultColor As Config
        Get
            Return _DefaultColor
        End Get
        Set(value As Config)
            If Not value.Equals(_DefaultColor) Then
                _DefaultColor = value
                NotifyPropertyChanged("DefaultColor")
            End If
        End Set
    End Property

    9. Now we have a way to capture and store the default color in the Config table. We can now create and populate a property that will hold a list of valid colors. Add the following code to the MainWindowViewModel constructor. Unlike the other properties we have created, this one will not be initialized if it is missing.

    Dim col = (From a In Model.Colors Select a).ToList
    Me.Colors = New ObservableCollection(Of Color)(col)

   10. Now add the definition of the new Colors property. We use an observable collection instead of a list so that if any colors are added or removed in code all the bound controls will be updated too. We don't need this feature in this walk through but it is a good habit to get into. If you didn't realize it yet, we need to import System.Collections.ObjectModel.

    Private _Colors As ObservableCollection(Of Color)
    Public Property Colors As ObservableCollection(Of Color)
        Get
            Return _Colors
        End Get
        Set(value As ObservableCollection(Of Color))
            If Not value.Equals(_Colors) Then
                _Colors = value
                NotifyPropertyChanged("Colors")
            End If
        End Set
    End Property

   11. Now our ViewModel has a default color config property and a list of valid colors. We can enhance the View to utilize these new properties. In the MainWindow.xaml add another RowDefinition and move the button down to Grid.Row="2"

   12. Build the application so the new properties become available and add the following XAML to create another label and a combobox. The combobox itemssource is bound to the Colors collection and the SelectedValue is bound to the DefaultColor property.

   <Label Grid.Row="1" Grid.Column="0" Content="{Binding Path=DefaultColor.Key}"/>
   <ComboBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding Path=Colors}" SelectedValuePath="ColorName" DisplayMemberPath="ColorName" SelectedValue="{Binding DefaultColor.Value}"/>

   13. If we run the application now we see it works correctly and saves the new default color to the database. But there is one more requirement we have not fulfilled - we need the foreground color of the labels to change when the default color is changed. We can bind them to DefaultColor property with a style so we only have to do it once. Add the following XAML.

    <Window.Resources>
        <Style TargetType="Label">
            <Setter Property="Foreground" Value="{Binding DefaultColor.Value}"/>
        </Style>
    </Window.Resources>




In the next section we will look at binding a datagrid.

MVVM Part 4

Adding missing rows

In the previous examples we were only modifying an existing row. If the row was missing the example would have failed. Let's see what it would take to detect a missing row and add it in code.

The existing code would have failed on the line below because c has no rows.
        Me.ApplicationName = c(0)

To solve this problem simply replace this line with the following...
   If c.Count > 0 Then
       Me.ApplicationName = c(0)
   Else
       ApplicationName = New Config()
       ApplicationName.Key = "ApplicationName"
       ApplicationName.Value = "MVVM_Walkthrough"
       Model.Configs.Add(ApplicationName)
   End If

All we are doing here is detecting the missing row, creating a new row and assigning it to the ViewModel's ApplicationName property, populating it, and adding back into the Config collection as a new row. When the user clicks [Save] it will be added instead of updated.

Now that we have that little Entity Framework nugget out of the way, let's see how we would bind a combobox.

MVVM Part 3

Saving Data

It turns out that by combining Framework Entity and MVVM it is very easy to save the data. But there is a small wrinkle...

Although it is perfectly possible to directly wire event handlers to handle events, we know that RoutedCommands are the preferred method for connecting UI events with event handlers. Unfortunately RoutedCommands can only execute code that is the code behind and we would prefer that the event handler be in the ViewModel which already has a reference to the Model. We need to introduce the concept of a RelayCommand.

A RelayCommand implements ICommand but can execute code in the class that defines it. So instead of defining a RoutedCommand in the XAML, we define it in the ViewModel and bind to it. The RelayCommand class is not currently part of WPF so we need to add it to the project ourselves. We only have to do this once. Let's do it now.

    1. Add a new class and call it RelayCommand.
    2. Replace the default code with this... (credit http://www.dotmaniac.net/)

Public Class RelayCommand
    Implements ICommand

    Private ReadOnly _CanExecute As Func(Of Boolean)
    Private ReadOnly _Execute As Action
    Public Sub New(ByVal execute As Action)
        Me.New(execute, Nothing)
    End Sub

    Public Sub New(ByVal execute As Action, ByVal canExecute As Func(Of Boolean))
        If execute Is Nothing Then
            Throw New ArgumentNullException("execute")
        End If
        _Execute = execute
        _CanExecute = canExecute
    End Sub

    Public Custom Event CanExecuteChanged As EventHandler Implements System.Windows.Input.ICommand.CanExecuteChanged

        AddHandler(ByVal value As EventHandler)
            If _CanExecute IsNot Nothing Then
                AddHandler CommandManager.RequerySuggested, value
            End If
        End AddHandler
        RemoveHandler(ByVal value As EventHandler)

            If _CanExecute IsNot Nothing Then
                RemoveHandler CommandManager.RequerySuggested, value
            End If
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            'This is the RaiseEvent block
            CommandManager.InvalidateRequerySuggested()
        End RaiseEvent
    End Event

    Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute
        If _CanExecute Is Nothing Then
            Return True
        Else
            Return _CanExecute.Invoke
        End If
    End Function

    Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute
        _Execute.Invoke()
    End Sub
End Class

    3. Add a public property to the ViewModel like this...

    Public Property SaveCommand As RelayCommand

    4. Now let's add a [Save] button to our XAML. Add another RowDefinition and add this button. Note that its Command is bound to our new RelayCommand.

        <Button Grid.Row="1" Grid.Column="1" Content="Save" Command="{Binding Path=SaveCommand}"/>

    5. Right now our SaveCommand does nothing. We need to hook it up to an eventhandler in the ViewModel's constructor. Add the following line of code at the end of the constructor...

   SaveCommand = New RelayCommand(New Action(AddressOf DoSave))

    6. Lastly we write the event handler
    Public Sub DoSave()
        Model.SaveChanges()
    End Sub

    7. You can now run the application, change the application name, click [Save], kill the application, and rerun it to see the changed application name. Other than the one-time creation of the RelayCommand class, saving the data is very easy.

In the next post in the series we will look at detecting and adding missing data in the ViewModel.

MVVM Part 2

In Part 2 of our MVVM walkthrough we will define a simple ViewModel. Our Config table only contains one row so our aim in this blog entry is to display the value from that row in a textbox.

Normally at this point we would add a textbox to MainWindow.xaml and start putting code in MainWindow.xaml.vb, but this is MVVM so we will start by creating our ViewModel. There is nearly always one ViewModel per page so lets start by adding a new class to the project called MainWindowViewModel. In a larger project you might want to group all your ViewModels into a folder called, perhaps, ViewModels. We won't need to do that here.

Create the ViewModel

    1. Add a new class called MainWindowViewModel
    2. Add "Imports System.ComponentModel"
    3. Add "Implements INotifyPropertyChanged". This will add a PropertyChanged event definition.
    4. After the PropertyChanged event, add the following method. We will discuss this later.

        Public Sub NotifyPropertyChanged(PropertyName As String)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
        End Sub

    5. Add a private variable called Model as MVVM_WalkthroughEntities
    6. In the constructor add the following code...

        Dim an = (From a In Model.Configs Select a Where a.[Key] = "ApplicationName").ToList()
        Me.ApplicationName = an(0)

    7. Add a private member called _ApplicationName and a public property called ApplicationName. The only unusual thing here is that the property setter calls NotifyPropertyChanged.

    Private _ApplicationName As Config
    Public Property ApplicationName As Config
        Get
            Return _ApplicationName
        End Get
        Set(value As Config)
            If Not value.Equals(_ApplicationName) Then
                _ApplicationName = value
                NotifyPropertyChanged("ApplicationName")
            End If
        End Set
    End Property

It's about time we looked at INotifyPropertyChanged. Although it's very important in WPF, I find a lot of programmers don't understand it.

If your ViewModel does not implement INotifyPropertyChanged then when its properties are changed, the View will not update. When you call NotifyPropertyChanged you send a message to any view that is bound to it saying "Go refresh yourself". The important thing is that the ViewModel does not need to know what View is bound to it. Remember, in WPF the view contains a reference to the ViewModel - not the other way around.

So what have we achieved here? We have a single property called Config which represents a single row from the Config table. It has two useful properties called Key and Value which contain the data from the row and that the View can bind to. We populated the Config object in the ViewModel constructor using some LINQ.

It's time to create the View.

    1. Before you can reference the ViewModel you must build the project. Do it now.
    2. Open MainWindow.xaml in XAML mode.
    3. Add a namespace definition for the ModelView
            xmlns:vm="clr-namespace:MVVM_Walkthrough"

    4. Set the datacontext of the view to the ViewModel we created above. Note the editor may incorrectly flag this as an error but you will still be able to build and run.
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>

    5. Now we will be able to bind to every public property in MainWindowViewModel, such as the Config property. Add the following XAML inside the <Grid> tags.
    <Grid.RowDefinitions>
            <RowDefinition Height="auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"></ColumnDefinition>
            <ColumnDefinition Width="auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Content="{Binding Path=ApplicationName.Key}"></Label>

        <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=ApplicationName.Value}" Width="200"></TextBox>

The completed application.

If you build and run the application now you will see that the label and textbox are populated as expected. This may seem like a lot of work to achieve something trivial, but as you application becomes more realistically complicated the value of separation between View and ViewModel becomes more apparent.

Unfortunately, as the number of properties increases, so does the tedium of coding each one but there are toolkits such as MVVM Light that alleviate that.

In Part Three we will look at what it would take to save the Application Name back to the database. 

MVVM Part 1

In the previous blog entry we created a simple database and a WPF project skeleton. Now we will start looking at a trivial MVVM design to display a single row in the database.

I mentioned before that I would use Framework Entity to provide our Model. Here are the steps to do that.

    1. Right click on the project, select Add -> Add New Item
    2. Select Data in the left panel, then select ADO.Net Entity Data Model in the right panel.
    3. Rename model1 to MVVM_Walkthrough_Model and click [Add]
    4. In the Entity Data Model Wizard select EF Designer from Database and click [Next>]
    5. Click [New Connection]
    6. Select SQL Server or SQL Server Express and click [Continue]
    7. Type localhost as the Server Name (or where ever your database is)
    8. Under Select or Enter a database name, select MVVM_Walkthrough and click [OK]
    9. Back at the wizard, click [Next>]
   10. Chose an Entity Framework version (any of them will work in the walkthrough)
   11. Under "Chose your Database Objects and Settings", select the Config table as shown below.
   12. Click [Finish]



You have now created an EDMX file which will provide the model of your config table. You can reference this model as MVVM_WalkthroughEntities and we will do so very soon.

Now we have defined our Model. We will define the ViewModel in part 2.

Introduction to MVVM for WPF using VB

If you search Amazon.com for books on WPF and MVVM you will find the pickings rather slim. If you add in a filter for Visual Basic you are left with nothing. Although many good people have published blogs and walkthroughs describing MVVM, they tend to be complex and/or full of errors and ambiguities and they are almost all in C#. I decided to buck the trend and write a series of blog entries for VB coders that takes gentle steps.

We've all been coding using techniques that are very much like MVVM for years. We understand the need for data abstraction layers and separation of the UI from the data model. The only big conceptual change we need to make is that in WPF and MVVM the UI controls the linkage to the data model rather than the other way around.

For example, we are used to writing code like this to set the content of a textbox to a value...
FirstNameTextBox.Text = "John"

With WPF we bind the text property to a FirstName property in the data context and write
Employee.FirstName = "John"

That, in my opinion, is the biggest paradigm change from Model-View-Controller to Model-View-ViewModel -- The ViewModel is now the owner of the data, not the View.

Now the big difference between regular WPF and WPF with MVVM is that the ViewModel is not in the code behind, but it is in a custom class built specifically for the view. The code behind is often completely empty. At first I wondered why not just put all the ViewModel code in the code behind. The reason is that the code behind is too tightly bound to the UI and we want looser coupling. One immediate advantage we get is the ability to unit test the ViewModel separately from the View.

This walkthrough uses Visual Studio 2013 but should work with versions as far back as 2010.

You can use many data sources for the model. I'm going to use SQL Server because most professional applications will use a database server of some kind. You can use SQL Server Express if you want. I'm going to use Framework Entity as my model because it does so much of the work for me.

    1. Create a new database in SQL Server called MVVM_Walkthrough
    2. Create a new table called Config using the following SQL script

USE [MVVM_Walkthrough]
GO
CREATE TABLE [dbo].[Config](
[Key] [varchar](50) NOT NULL,
[Value] [varchar](1000) NULL,
 CONSTRAINT [PK_Config] PRIMARY KEY CLUSTERED 
(
[Key] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

    3. Insert a single row into Config
INSERT INTO Config VALUES ('ApplicationName','MVVM Walkthrough')

    4. Start a new Visual Studio Project (File -> New -> Project)
    5. In the left panel chose Visual Basic, then Windows
    6. In the right panel chose WPF Application. 
    7. Rename the project to MVVM_Walkthrough and click [OK]

Continue this walkthrough in MVVM Part 1