Click here to Skip to main content
15,888,610 members
Articles / Web Development / HTML

Complete Sudoku Game in C#/WPF/Silverlight

Rate me:
Please Sign up or sign in to vote.
4.85/5 (35 votes)
1 Jan 2015GPL347 min read 54.4K   5.1K   47   5
This article is about converting a VB.NET WinForms application to C# using WPF and Silverlight.

Introduction

After writing a Sudoku game using VB.NET and WinForms, I decided to translate it to C#. To make things interesting, I decided to create the UI layer using WPF instead of WinForms. And since Silverlight is a subset of WPF, I wanted to see how easy it was to convert the finished WPF code to Silverlight as well.

This article covers the issues I encountered and the solutions and work-arounds that I came up with. For background information on the game and some of the decisions I made regarding game play as well as the programming concepts used like Singletons, Events, etc., please read the original article: Complete Sudoku Game for Windows using VB.Net 2013.

This article can be used as an introduction to programming in WPF and Silverlight. I will assume that the reader has some knowledge about C# and WinForms. For those who already know WPF and Silverlight, this article will be pretty boring. But feel free to read it, download the code and play with it. Any suggestions for improvement or alternative ways to do something are most welcome.

Background

Windows Presentation Foundation, or WPF, was first released with .NET 3.0 back in November of 2006. WPF is essentially a rendering engine with lots of powerful features to allow a developer to create visually stunning client applications for both standalone, browser-hosted, and phone applications. When building the WPF front-end, one uses XAML, or Extensible Application Mark-up Language, to describe the UI. The WPF engine will then interpret the XAML and output the UI to either a Windows application, browser, or Windows Store/Phone App depending on the project. Here is a simple diagram that shows what I mean.

Image 1

In reality, Silverlight and Windows Store App is a subset of WPF. So, the same XAML that works for a Windows Application may not work when outputting to a browser as a Silverlight app or to a Windows Phone.

When building the UI in WPF, instead of setting properties for the controls on the Property page of the designer window, one sets properties directly using the XAML code window. This is completely different from writing a WinForms application, whether it is in VB.NET or C#. If entering XAML code is intimidating, the IDE also has a Properties tool window where one can modify the properties as well. When starting out writing WPF applications, it might be better to use the Properties tool window so that one can get familiar with all the properties that are available.

There are lots of tutorials, sample code, and help on-line to get you started with WPF. Here is a link to Microsoft's own website on WPF: Introduction to WPF. In this article, I'll go over some of the more salient points of WPF as it relates to writing the game.

For this project, instead of using the MVC programming pattern like I did in the other project, I tried to adhere to the MVVM, or Model-View-View Model pattern, which is the recommended pattern to use when writing WPF applications. Here is a diagram taken from Microsoft's website that details the flow between the different parts of the MVVM pattern. Note the extra "Data Binding" link between the View and the View Model compared to that in the MVC pattern.

Image 2

In the MVC pattern, the business logic is generally kept in the Controller. In the MVVM pattern, the business logic is pushed down to the Model layer. Since the original code was written using the MVC pattern, I kept most of the business logic in the VM layer. There are lots of articles on-line that describe this programming pattern in greater detail, as well as the differences between the MVC and MVVM programming patterns.

The code was organized in the MVVM pattern as well. I created three folders and named them Model, View, and View Model and the code was split up accordingly.

Image 3

Programming in WPF and using the MVVM programming pattern lends itself well to separating the UI design from the business logic and allow groups of specialists to build the different parts of complex applications. Graphic designers can use tools like Expression Blend or even Visual Studio to build a visually rich UI without having to understand how to write a single line of C# or VB code. The developer can then concentrate on writing the business logic and data layers without having to think about how the data will be presented. Obviously, there should be an agreement as to what kind of data is required between the two teams.

I feel that one of the keys to understanding and mastering WPF is understanding Data Binding. WPF Data Binding allows UI elements, like text boxes, to be bound to a property in the VM layer. And if done right, when the property changes, the UI will automatically change as well. Here is a link to Microsoft's website that explains data binding as well as examples. Sure, one can address the controls directly in the code-behind like we did in the good old days of WinForms. But that goes counter to what WPF is all about and requires more code behind than data binding.

Using the Code

The programs were written using VS 2013 and the 4.5 .NET framework/Silverlight 5. They are code complete. I have included both the WPF and the Silverlight projects above. You should be able to download and compile both projects separately.

Normally, when one shares code between two or more projects, one would copy the file from one project to the other or add the file to the second project from the first project. This makes maintaining the code much harder since there are multiple copies of the same file.

Starting with VS 2010, one can add a link to the original code file by clicking the down arrow on the "Add Existing Item" dialog box.

Image 4

However, for these two projects, I did not use this feature. So each ZIP file contains all the necessary files and one can download whichever project that interests you without having to download the other.

Converting from VB.NET to C#

Syntactical differences aside, porting the code from VB.NET to C# was pretty straightforward since both run on top of the same CLR.

In addition to porting the code from VB.NET to C#, I also made some changes to the way arrays were addressed. In my VB code, arrays were addressed using the indices from 1 through 9. I did that because the valid answers for the game are the numbers from 1 through 9. However, in the C# code, since C is a zero based language, I decided to conform and changed everything to be zero based. Valid answers are still 1 through 9 though. That took a little while, but it was not hard to do.

Converting the UI from WinForms to WPF

After porting the code over to C#, the next step was to convert the UI from WinForms to WPF. That took a little more effort. The first decision to make was what kind of Panel object to use as the base. In WinForms, there is just a blank form and one starts putting controls on the form to build the UI. But in WPF, there are several different kinds of background or Panel objects to build the UI with and each one has different characteristics. So choosing the right one for the project is essential to the overall success. Here is a link to a website that describes the differences. The default is the Grid.

Since down the line, I might want to implement a fireworks display in the background, I decided to use the Canvas as my main window's background. The Canvas Panel allows me to draw directly on the control. I then added all the necessary controls to make it look like the original VB version.

Both WPF and WinForms have buttons, comboboxes, labels, and checkboxes. So that was easy to replicate.

The game grid on the other hand was a challenge since WPF does not have a similar TableLayoutPanel control. On the other hand, WPF has two kinds of grids: a DataGrid and a Grid grid. I felt that the DataGrid was more for displaying a scrolling list of data items in a tabular format. Since I am not interested in scrolling around and the data size is static, I opted to use a Grid control instead.

I added a Grid control to my form and then split it into 3 rows and 3 columns. Within each cell, I added another Grid control and also split it into 3 rows and 3 columns. I then added borders and rectangles as well as changed the background color to build the game grid. Since there is no line object, I used rectangles with a height (or width) of one pixel to represent the lines on the game grid.

The next issue was the status bar at the bottom. WPF also has a StatusBar control, but it works differently than the WinForms StatusBar. Instead of using the Items Collection Editor to add items to the Status Bar, one adds items directly in the XAML code.

Here is the final rendition of the main window in WPF:

Image 5

A couple of things that you will see that are different from the original VB version is that the Hint and Clear buttons are missing. I moved those buttons to the Input Pad dialog. The reason I did that was because Hint and Clear are more cell specific actions rather than game specific. And therefore, they really belong to the Input Pad window which contain cell specific actions. One could make the same argument for the "Enter Notes" checkbox as well. But there are times when one wants to enter notes for multiple cells and not just for a single cell. That is why I left it on the main window.

The next step was to wire up the event action for the buttons. In the XAML section for the button, I just specified the Click event as an XML property:

XML
<Button Content="Close"
        Style="{StaticResource ButtonBaseStyle}"
        Margin="430,411,0,0"
        Click="btnClose_Click"/>

And the code-behind looks like this, which looks similar to a WinForms event:

C#
private void btnClose_Click(object sender, RoutedEventArgs e)
{

}

I left the details of the code-behind blank for now. Once I am ready to work on the View Model, I will add code to wire them both together.

WPF and Data Binding

After wiring up the action or event code for each of the buttons, the next step was to wire up the UI to the data. Notice, I did not mention checkboxes at all. This is because we can use WPF data binding to set the checkboxes and do other really cool things. I will explain more later on.

I could have easily followed the WinForms conventions and just assigned the data to the control in the form's code-behind like so:

C#
this.ElapsedTime.Content = "00:00:00";

Since WPF has powerful tools to connect the UI to the data, I will use that instead.

It starts with the ViewModel class and adding the INotifyPropertyChanged interface, which is part of the System.ComponentModel namespace.

C#
using System.ComponentModel;

internal class ViewModelClass : INotifyPropertyChanged

The other related declarations are as follows. I declare the event and then write code to raise the event.

C#
public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

And this is how it is used:

C#
public string GameCountVeryEasy
{
    get
    {
        return GetGameCount(DifficultyLevels.VeryEasy);
    }
    private set
    {
        OnPropertyChanged();
    }
}

The set accessor has only one line of code since we do not need to save the actual value in the ViewModel class. When we need it, the get accessor makes a call to GetGameCount where it is stored.

Then in the XAML, here is how it is called:

XML
<Label Content="{Binding Path=GameCountVeryEasy, Mode=OneWay}"/>

Easy, right? Actually, I am missing a few more things. First, in the XAML header declaration, I need to specify the namespace where the property GameCountVeryEasy is located. To do this, I need to add the following line to the <Window tag up top:

XML
xmlns:srcVM="clr-namespace:SudokuWPF.ViewModel"

Then, in the form's code-behind, I need to add the following line:

C#
this.DataContext = _viewModel;

Normally, it is added when the View Model class is initialized, either in the constructor of the form, right after InitializeComponent(), or somewhere close to that. This tells the UI which instance of the View Model class has the actual data. Interestingly enough, I did not have to specify in the XAML code, the actual name of the class where this property is found. That is what the DataContext is for. It tells WPF where to find the properties that are bound to the controls on the UI.

Let me explain in more details some of the more interesting points in the code above. First, the [CallerMemberName] attribute in the OnPropertyChanged method's parameter.

C#
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")

This attribute allows the OnPropertyChanged method to obtain the method or property name of the caller to the method. This is new for .NET 4.5 and is part of the System.Runtime.CompilerServices namespace. Here is a link to Microsoft's website that describes this attribute in more details.

Another concept new to C# is the ability to create optional arguments. This is something that the VB world has had for years. So for those who are already familiar with VB's optional arguments, the syntax above is already familiar. The only difference is that there is no Optional qualifier. Simply put, when we add = "" to the end of a parameter declaration, it turns that parameter into an optional parameter. The blank string simply means that is the default value if the parameter were omitted from the call. Any valid string can be used as the default. For more information about optional parameters, here is the link to Microsoft's website.

Normally, when one calls OnPropertyChanged, one has to pass the name of the property so that the corresponding WPF element that is bound to that property will know that it changed. For example:

C#
OnPropertyChanged("GameCountVeryEasy");

By using the [CallerMemberName] attribute and optional parameters, if the call came from the actual property GameCountVeryEasy, we can omit the propertyName parameter from the call since [CallerMemberName] will fill it in for us automatically.

In C#, namespaces are declared with the using keyword at the beginning of the code page. XAML uses xmlns:. In the following XAML namespace declaration:

XML
xmlns:srcVM="clr-namespace:SudokuWPF.ViewModel"

Here is what it all means. xmlns: is just the XML attribute that allows us to specify a namespace. srcVM is a shortcut name that can be used in the XAML code if we need to refer to a class, property, or method in that namespace. I could have said viewModel, vm, or any valid name for that matter. And finally, the content of the string is the name of the actual namespace. The XAML editor automatically adds "clr-namespace:" when the final double quotation is entered. When you enter the first quotation, intellisense populates a dropdown list with all the valid namespaces in the current project.

Here is another huge difference between WinForms and WPF. In WPF, it is not necessary to name all the elements on a form. So our label control just has a Content property. The binding code is what ties this element to the data source and that is how we will update that element from code.

XML
<Label Content="{Binding Path=GameCountVeryEasy, Mode=OneWay}"/>

Generally speaking, if there is no need to address the control from code, then there is no need to give it a name. The data binding is sufficient. Binding is a key word that tells the XAML interpreter that binding code follows. Path= specifies the property to use specified by the DataContext. Mode=OneWay specifies that the binding goes from the View Model to the View, or in other words, to a read-only property. If it is not specified, then the binding defaults to TwoWay. Here is a link to Microsoft's website that goes over data binding in WPF.

So, with all the wiring above, when we want to update the label with a new value for GameCountVeryEasy, all we need to do is make the following call in the ViewModel class.

C#
GameCountVeryEasy = "4";

What happens next is, in the property's set accessor, the OnPropertyChanged method is called and the PropertyChanged event is raised. The WPF engine, which is listening to all PropertyChanged events, is triggered. It then looks for the corresponding data binding. Once it is found, WPF then calls the property's get accessor and assigns the label the string value of 4.

The property to bind to can be located in pretty much any class in the project. It does not have to be limited to ViewModel class. In fact, I bind the game cells to properties that are found in the Model class as well. All you need is to add the INotifyPropertyChanged interface and the related code. Then just bind to it in your XAML.

It may not seem impressive since the following is the WinForms equivalent. In a WinForm's code-behind, we would write the following code:

C#
private delegate void SetGameCountCallback(Int32 value);

internal void SetLabel(Int32 value)
{
    if (label1.InvokeRequired)
    {
        SetGameCountCallback callback = new SetGameCountCallback(SetLabel);
        this.Invoke(callback, new object[] { value });
    }
    else
        label1.Text = value.ToString();
}

And the code in the set accessor of the GameCountVeryEasy property would simply be:

C#
Form1.SetLabel(value);

And we would not need a get accessor since we already assigned the value to the label on the form.

On the surface, WinForms looks much simpler than the WPF equivalent, but WPF data binding can do so much more than simply setting the contents of a label. Also, we would have to replicate this code for each and every control that we want to update. Naturally, we can write a generic version of this method where we pass in the control that needs to be updated.

In WinForms, we need to call the InvokeRequired method of the label to make sure that we are making a thread-safe call. In WPF, we do not need to check if the call is thread safe or not. WPF takes care of all that for us. So all we need to do is just raise the PropertyChanged event and WPF takes care of the rest. I should also mention that the OnPropertyChanged method can be called from anywhere in the class. So long as the correct property name is passed to the method, it will get updated. I used this technique in my project and I will talk about it later on.

Data Binding and Checkboxes

Here is the real magic of WPF data binding. For the check boxes, we also create properties in the ViewModel class to store the checkbox state. For example, for the EnterNotes checkbox, we have the following property definition in the ViewModel class:

C#
public bool IsEnterNotes
{
    get
    {
        return _isEnterNotes;
    }
    set
    {
        _isEnterNotes = value;
        OnPropertyChanged();
    }
}

The property is pretty self explanatory. We either return or save the state of the check box. When we save the state, we also call the OnPropertyChanged method to raise the PropertyChanged event.

The check box XAML declaration looks like this:

XML
<CheckBox Style="{StaticResource CheckboxBaseStyle}"
          IsChecked="{Binding Path=IsEnterNotes}"
          Content="Enter Notes"
          Margin="23,420,0,0" />

The IsChecked property is bound to the IsEnterNotes property of the ViewModel. And since we want to know when the user clicks the check box, we want the binding to go both ways. So the binding mode uses the default TwoWay. Because the IsChecked property of the check box is bound, we do not need to wire up the Click event in the code-behind. That is one less thing to worry about. However, any Click event code-behind we used to have in the WinForms version will go into the set accessor of the IsEnterNotes property instead.

WPF Styles

One of the powerful features in WPF is the ability to create styles for a group of controls like buttons. So we can create a Style like this:

XML
<Style x:Key="ButtonBaseStyle"
       TargetType="Button">
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="VerticalAlignment" Value="Top" />
    <Setter Property="Width" Value="140" />
    <Setter Property="Height" Value="30" />
</Style>

And apply it to all the buttons so that they all look the same:

XML
<Button Content="New Game"
        Style="{StaticResource ButtonBaseStyle}"
        Margin="430,70,0,0"
        Click="btnNew_Click"/>
<Button Content="About Sudoku WPF"
        Style="{StaticResource ButtonBaseStyle}"
        Margin="430,376,0,0"
        Click="btnAbout_Click"/>
<Button Content="Close"
        Style="{StaticResource ButtonBaseStyle}"
        Margin="430,411,0,0"
        Click="btnClose_Click"/>

The <Style></Style> declaration can be put in several places in the project. It can be located in a separate resource file so that the user can select different themes. It can be put in the container's resource section, or in my case, in the resource section just above the container. So, right above my <Canvas></Canvas> tags, I added the <Window.Resources></Windows.Resources> tags and inserted my styles there. If the style is put in the <Application.Resources></Application.Resources> section or a separate resources file, the scope expands to the entire application. But since I put it in the <Window.Resources></Windows.Resources> section of the Window that I am using it, the scope is limited to that window.

Notice again that none of these buttons are named.

The nice thing about being able to create styles is that I can use the same style for all the buttons on the window. If I need to change something, I can just change the style and all the buttons will reflect the changes. In WinForms, if I need to change something, I need to select all the buttons and then change the property.

If you notice in my Style XAML, I added an x:key property to the Style. If I omitted that property, this style will apply to all buttons and I can remove the following line from the button declaration.

XML
Style="{StaticResource ButtonBaseStyle}"

However, since I need to apply a different style to both the Reset and the Start buttons, I added the x:Key property. The reason is, both these buttons have some special states depending on where in the game play the user is in. For example, the Start button, if the user just loaded a new game, the button will say "Start Game". And when the game is in progress, it will change to "Pause", etc. Same with the Reset button. If a new game is loaded or when the game is paused, the Reset button will be disabled, and so on.

To code these states, we can add code to the form's code-behind. But then, that would bypass another great feature of WPF.

WPF Style Triggers

In the previous section, I mentioned that the Start and Reset buttons change state depending on where in the game play the user is in, and that we can manage the state of the buttons using WPF. To do this, we use Style Triggers. Here is an example:

XML
<Style x:Key="EnableGameButtonStyle"
       TargetType="Button"
       BasedOn="{StaticResource ButtonBaseStyle}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=IsEnableGameControls, Mode=OneWay}" Value="True">
           <Setter Property="IsEnabled" Value="True" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=IsEnableGameControls, Mode=OneWay}" Value="False">
            <Setter Property="IsEnabled" Value="False" />
        </DataTrigger>
    </Style.Triggers>
</Style>

I added the following boolean property to the View Model class:

C#
public bool IsEnableGameControls
{
    get
    {
        return _isEnableGameControls;
    }
    private set
    {
        _isEnableGameControls = value;
        OnPropertyChanged();
    }
}

Notice in the set accessor, I call OnPropertyChanged.

Then, I create a new Style which is based on the ButtonBaseStyle from above, since I want these buttons to maintain the same look. I then added two triggers based on possible values of this property: True and False. Basically, if the property is true, then the button is enabled. If it is false, then disable the button.

Let us go through this style line by line. I added an x:Key property to the style so that I can reference it from the Reset button. Since this style will be used for buttons, I added the TargetType property. I then added the BasedOn property because I want the Reset button to look like all the other buttons on the form. I then added the Triggers. The syntax is pretty straight forward. I need to bind the trigger to a property in the View Model class and then set the value for the trigger to look for. And within that trigger tag, I set the property, or properties of the button that I want to change when the value of the property is either True or False. In the case of the Reset button, all I want to do is either enable or disable the button.

In the binding declaration, I added the Mode=OneWay property because the update only goes from the View Model to the UI. The UI never updates that particular property. And so the property is declared as read-only in the View Model class.

And to use the Style, the Reset button declaration looks like this:

XML
<Button Content="Reset Game"
        Style="{StaticResource EnableGameButtonStyle}"
        Margin="430,190,0,0"
        Click="btnReset_Click"/>

The CheckBoxes below the game grid also uses the IsEnableGameControls property and the style and trigger syntax is silimar. Similarly, I added another property called IsShowGameGrid that controls whether the game grid is displayed or not.

To enable or disable the game controls, I just call the IsEnableGameControls property in the ViewModel:

C#
IsEnableGameControls = true;

And all controls that are bound to that property will be affected. To implement the same thing in WinForms, I would have to code something like the following. I would also have to wrap it with an InvokeRequired to make sure the call is threadsafe.

C#
private void EnableGameButtons(bool bEnable)
{
    this.EnterNotesCheckbox.IsEnabled = bEnable;
    this.ShowNotesCheckbox.IsEnabled = bEnable;
    this.ShowSolutionCheckbox.IsEnabled = bEnable;
    this.ResetButton.IsEnabled = bEnable;
}

Since I am just changing the IsEnabled property of the affected controls and both the IsEnabled and IsEnableGameControls are boolean properties, I can just bind the IsEnabled property of each control directly to the IsEnableGameControls property of the ViewModel and it will work the same way as using the Style Triggers. The declaration would look like this:

XML
IsEnabled="{Binding Path=IsEnableGameControls, Mode=OneWay}"

The Style declaration for the Start button is more complicated since there are multiple states. But the syntax is similar.

XML
<Style x:Key="StartButtonStyle"
       TargetType="Button"
       BasedOn="{StaticResource ButtonBaseStyle}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=StartButtonState, Mode=OneWay}" Value="Start">
            <Setter Property="Content" Value="Start Game"/>
            <Setter Property="IsEnabled" Value="True"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=StartButtonState, Mode=OneWay}" Value="Pause">
            <Setter Property="Content" Value="Pause Game"/>
            <Setter Property="IsEnabled" Value="True"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=StartButtonState, Mode=OneWay}" Value="Resume">
            <Setter Property="Content" Value="Resume Game"/>
            <Setter Property="IsEnabled" Value="True"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=StartButtonState, Mode=OneWay}" Value="Disable">
            <Setter Property="Content" Value="Start Game"/>
            <Setter Property="IsEnabled" Value="False"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

Each trigger is bound to the StartButtonState property in the View Model class. Since the StartButtonState property has more states than a simple boolean, we include a trigger for each state. And within each trigger tag, we set the properties that we want to change for each state. I did not have to include the following...

XML
<Setter Property="IsEnabled" Value="True"/>

...in the first three trigger tags since the default value of this property is already True and I am not changing anything. But I added them for readability.

Likewise, we can create properties in the ViewModel that return a content string and the enabled state for each StartButtonState and bind the Content and the IsEnabled properties of the Start button to those properties. We can then bypass the use of Style Triggers.

Here is the modified StartButtonState property in the ViewModel as well as the StartButtonContent and the IsEnableStartButton properties:

C#
private StartButtonStateEnum StartButtonState
{
    get
    {
        return _startButtonState;
    }
    set
    {
        _startButtonState = value;
        OnPropertyChanged("StartButtonContent");
        OnPropertyChanged("IsEnableStartButton");
    }
}

public string StartButtonContent
{
    get
    {
        switch (StartButtonState)
        {
            case StartButtonStateEnum.Pause:
                return "Pause Game";

            case StartButtonStateEnum.Resume:
                return "Resume Game";

            default:
                return "Start Game";
        }
    }
}

public bool IsEnableStartButton
{
    get
    {
        return (StartButtonState != StartButtonStateEnum.Disable);
    }
}

And the Start Button XAML code looks like this:

XML
<Button Style="{StaticResource ButtonBaseStyle}"
        Content="{Binding Path=StartButtonContent, Mode=OneWay}"
        IsEnabled="{Binding Path=IsEnableStartButton, Mode=OneWay}"
        Margin="430,105,0,0"
        Click="btnStart_Click"/>

We change the Style to point to the ButtonBaseStyle and add the Content and the IsEnabled properties and bind them to the corresponding properties in the ViewModel class.

The big thing here is in the set accessor of the StartButtonState property, we call the OnPropertyChanged method on both the StartButtonContent and the IsEnableStartButton properties.

Personally, both methods work and I do not think that one is better than the other. There is less code when using Style Triggers though. However, that puts some of the business logic in the UI rather than in the ViewModel. So, if we were to strictly adhere to the MVVM programming pattern, then the second method would probably be the preferred method to use. That way, the UI layer does not contain any business logic.

WPF and the Combo Box

The next order of business is how to populate the ComboBox with the contents of an Enum. If one searches in Google, there are many examples on how to do this. Let us use this example from Microsoft's own website. First, we need to declare the namespace where the enum resides. In this project, the Enum resides in the following namespace:

XML
xmlns:srcME="clr-namespace:SudokuWPF.Model.Enums"

Then, in the Windows Resource section, we declare the following:

XML
<ObjectDataProvider x:Key="GameLevels"
                    ObjectType="{x:Type sys:Enum}"
                    MethodName="GetValues">
    <ObjectDataProvider.MethodParameters>
        <x:Type TypeName="srcME:DifficultyLevels" />
    </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

This XAML object is actually creating a wrapper around the GetValues method of the Enum class. And in the ComboBox declaration, we point the ItemsSource property to this object like so:

XML
<ComboBox HorizontalAlignment="Left"
          VerticalAlignment="Top"
          Width="140"
          ItemsSource="{Binding Source={StaticResource GameLevels}}"
          SelectedItem="{Binding GameLevel}"
          IsEditable="False"
          IsReadOnly="True"
          Canvas.Left="430"
          Canvas.Top="32"/>

Pretty straightforward, right? Well, not exactly. One of our enums is "VeryEasy" and that is exactly how it will appear in the dropdown instead of "Very Easy", with a space between the two words. To get around this, there are many examples on-line on how to fix this as well as how to create tooltips and how to localize the enum values. But that is a bit much for this project so let us just stick to this method since it is easy to read and the missing space is not really noticeable.

The SelectedItem property is bound to another property in the ViewModel class. In this case, we use the default TwoWay mode since we want the changes to go both ways. Meaning, if the user changes the selection, we want the View Model to be aware of it and act on it. And when the game first loads, we want to be able to set the level to the last selected level.

C#
public DifficultyLevels GameLevel
{
    get
    {
        return _gameLevel;
    }
    set
    {
        bool bLoadNewGame = (_gameLevel != value);
        _gameLevel = value;
        Properties.Settings.Default.Level = _gameLevel.GetHashCode();
        if (bLoadNewGame)
            LoadNewGame();
        OnPropertyChanged();
    }
}

Because we bound the SelectedItem property, we do not need to add the SelectionChanged property to the combo box and a corresponding event handler in the code behind. Instead, the usual SelectionChanged event code is found in the set accessor of this property.

One thing you will notice when you look at the code, all the bound properties are declared public instead of my preferred access modifier, internal. This is because WPF requires that level of access. If it was declared internal, then the XAML data binding will fail. For this property, even the DifficultyLevels enum has to be declared public.

Game Cells

Now that we have more or less dealt with the periphery controls, how do we manage the state of the game cells? Do we use the Style triggers? Or is there a more powerful WPF tool that we can use? As it turns out, there is. WPF has a DataTemplate class. The reason we use this instead of the simpler Style Triggers is because each game cell can display two primary kinds of data. A single value that denotes the answer or the user's answer. And another state where it displays the notes. However, we could still probably use Style Triggers to achieve the same effects.

First, each game cell is declared as a ContentControl control. A ContentControl is the base class for pretty much all the controls in WPF. It represents a control with a single piece of content of any type. With the ContentControl, we can build our own custom control to represent the game cell.

In the Windows.Resources section, we create the DataTemplate for the game cell. The base for the game cell is a 3 x 3 Grid so that we can display the notes for the cell. And in each grid, we put in a TextBlock element to display the actual note. If we are displaying anything other than notes, we use another TextBlock that spans the entire grid area.

Highlighting Cells

Each cell of the grid has an IsMouseDirectlyOver property that we can use to add a style trigger to highlight it whenever the user mouses over a cell. The following is the style trigger I used. I excluded the rest of the style since it is not all that interesting.

XML
<Style.Triggers>
    <Trigger Property="IsMouseDirectlyOver" Value="True">
        <Setter Property="Background" Value="LightBlue" />
    </Trigger>
</Style.Triggers>

With this trigger, whenever the user moves the mouse over a game cell, the background will change to LightBlue. This works whether the background is AliceBlue or White.

WPF and DataTemplates

So, how to do we deal with the different game cell states? In the original VB version, I used two properties to control the cell state:

  • CellState
  • IsCorrect

While there is such a thing as a MultiDataTrigger, I decided that it was easier to combine these two properties and modified the CellStateEnum accordingly. In addition, I needed to change the way the Notes worked. I added another class to represent the state of the Notes for each cell so that each note cell can be bound to a property that displays notes.

Look up the CellDataTemplate in the MainWindow XAML code to see what I did.

I also needed to add the INotifyPropertyChanged interface to the CellClass and the NoteState since both classes have properties that are bound to controls in the CellDataTemplate.

In the NoteState class, I have the following property declaration:

C#
public bool State
{
    get
    {
        return _state;
    }
    set
    {
        _state = value;
        OnPropertyChanged("Value");
    }
}

What this code does is, in the set accessor, when the state of the note is changed, we raise a PropertyChanged event for the Value property. This is because the Value property is bound to a control on the MainWindow and not the State property. This is an example of raising the PropertyChanged event on a related property. Also in the ViewModel class, we have the following function:

C#
private void UpdateAllCells()
{
    if (IsValidGame())
        foreach (CellClass item in _model.CellList)
            OnPropertyChanged(item.CellName);
}

We call this method whenever we need to update all the cells in the game grid. This is another example where we raise the PropertyChanged event outside of the actual property that is bound to a WPF control.

Input Pad

For the Input Pad, which is used by the user to enter values into blank cells, the WPF version is pretty straight forward. This is how it looks in the IDE designer window:

Image 6

As I mentioned earlier, I moved the Hint and Clear buttons from the main form onto the Input Pad form. The base Panel I used for this form is the StackPanel. I then added a 3 x 3 grid for the number pad and then two buttons: one for Hint and another for Clear.

To create spaces between the three elements, we add the Margin property to the number grid and the bottom Clear button..

When the user clicks a cell on the game grid, we want to position this window right next to where the user just clicked. To do this, we use the following lines of code in the cell's click event.

C#
System.Drawing.Point point = System.Windows.Forms.Control.MousePosition;
inputPad.Left = point.X + 20;
inputPad.Top = point.Y - (inputPad.Height / 2);

Basically, we query the system for the absolute location of the mouse. Then, we adjust the Left and Top properties of the Input window and position it 20 pixels to the right of the mouse click. The complete code to create and display the window follows:

C#
inputPad = new InputPad();
inputPad.Owner = this;
System.Drawing.Point point = System.Windows.Forms.Control.MousePosition;
inputPad.Left = point.X + 20;
inputPad.Top = point.Y - (inputPad.Height / 2);
inputPad.ShowDialog();

Like the WinForms equivalent, we need set several properties on the Window:

XML
ResizeMode="NoResize"
ShowInTaskbar="False"
SizeToContent="Height"
WindowStartupLocation="Manual"
WindowStyle="ToolWindow"

In order to position this window where we want it, like the WinForms version, we need to set the WindowStartupLocation property to Manual. Then, we can control the Top and Left properties of the Window. Otherwise, Windows will ignore our attempts to reposition the window and just put it wherever it wants to.

The code behind for this window is pretty straightforward as well. Once the user clicks something, we save the button clicked as a state and then close the window.

The About and Game Complete windows are similar except that we do not need to save any state. We just open the child window as a modal dialog and then when the user clicks "OK", just close it and return to the main window.

Wrapping Up WPF

At the beginning, I left all the click events in the code behind for the MainWindow blank. But now that the ViewModelClass is more or less complete, the next step is to wire them up together. Basically, the click event in the MainWindow just calls a function in the ViewModelClass, similar to how the WinForms version did. So, the code-behind for "New Game" button looks like this:

C#
private void btnNew_Click(object sender, RoutedEventArgs e)
{
    if (ViewModel != null)
        ViewModel.NewClicked();
}

To wrap up the WPF version, I cleaned up the game play code in the ViewModelClass and then set the Application startup code. The Startup object in a WPF application is the App.XAML object. When you open it, there is a StartupUri property in the <Application/> tag. By default, it points to the MainWindow.XAML. But since we want to instantiate the ViewModelClass outside of the View, we repoint it to the ApplicationStartup method of the Application class instead. In addition, we rename it from StartupURI to Startup. Then we open the code-behind and add the following method to the code-behind of the App.xaml class.

C#
public void ApplicationStartup(object sender, StartupEventArgs args)
{
    MainWindow mainWindow = new MainWindow();
    mainWindow.ViewModel = ViewModelClass.GetInstance(mainWindow);
    mainWindow.Show();
}

So basically, we instantiate the main window, set the ViewModel property for the main window, and then display the window.

If you look at the code-behind for the MainWindow, it is pretty sparse compared to the code-behind for a WinForms application.

Here is how the WPF application looks like when it is running:

Image 7

If you have played with the VB version of this game, you will notice that in this version, I have implemented the mirror image where the missing cells are mirrored on the vertical axis. In the next version of the game, I will add another dimension where the game generator randomly picks the mirror to be either vertical, horizontal, or diagonal.

WPF and Silverlight

Now that we have completed the port from VB.NET/WinForms to C#/WPF, we shall now attempt to port the code to Silverlight. I created a new C# Silverlight Application project, added all the subfolders (View, Model, and ViewModel), then copied all the files, except for the WPF UI files, from one project to the other.

I thought it would be a pretty straight port, but there were several bumps along the way. The first difference is that a Silverlight Application has two projects instead of one. These are:

  • SilverlightApplication1
  • SilverlightApplication1.Web

The second project is just a wrapper for the main project so that we can run it in a browser for testing/debugging. So we do not have to concern ourselves with the second project at all.

The next difference is that instead of a <Windows/> tag in the MainWindow, we have a <UserControl/> in a MainPage. The following sections detail the other bumps that I encountered as I tried to port the code from WPF to Silverlight.

Application Settings

The first bump was the application settings does not port over to Silverlight. In WPF, the application settings are found in a tab in the Application Property window.

Image 8

Addressing the properties from code is similar to VB.NET Windows application.

C#
Properties.Settings.Default.Level = (int)GameLevel;
Properties.Settings.Default.Save();

But in Silverlight, the Properties namespace does not exist. There is however, the IsolatedStorageSettings class. To use it, one needs to get an instance of the class before using it like so:

C#
private IsolatedStorageSettings _appSettings = IsolatedStorageSettings.ApplicationSettings;

Before using any of the settings, we first check if the setting already exists or not:

C#
if (!_appSettings.Contains("Level"))
    _appSettings.Add("Level", "");

So, if it does not exist, we add it. Then to retrieve data, we use the following code:

C#
_level = (Int32)_appSettings["Level"];

To save the settings, we use the following code:

C#
_appSettings["Level"] = _level.ToString();
_appSettings.Save();

Since all the settings are saved as a string, we need to convert the integer value to and from a string when we use it. Also, to commit the data to disk, we need to call the Save() method.

Since one needs an instance of this class, as well as a lot of support code, I thought it best to consolidate all the settings into a single class and wrap it as a Singleton object.

Timer Class

The next bump was the Timer class. Apparently, System.Timers is not part of the Silverlight application namespace. Fortunately, there is another Timer in the System.Threading namespace. There are several differences between the two classes and I had to rewrite parts of the Timer class to accommodate those differences.

The first difference is how one instantiates the Timer class. The next difference is, there is no Enabled property to start or stop the timer once it is instantiated. There is, however, a Change method that one can use to pause/restart/stop the timer. Also, once the timer is instantiated and running, there is no way to know if it is running or not, so I created my own TimerEnabled property to keep track of the state of the timer.

The UI XAML Code

After fixing the above issues, I copied the WPF XAML code from one project to the other. Instead of copying the actual files, I opened the original WPF code, copied the XAML code inside the <Window></Window> tag for the MainWindow and pasted it to the MainPage.XAML window. I did this for the other three child windows as well.

When I pasted the code into the MainPage.XAML window, there are several squiggly lines indicating that there are errors. Among the errors, are:

  • DataTriggers
  • StatusBar
  • Label
  • ObjectDataProvider

These are pretty serious issues since I used the DataTrigger class to help control and manage the game controls and the game grid. So I had to look for work-arounds.

The StatusBar And Labels

This was an easy enough fix. Since Silverlight did not have a StatusBar and the Label controls, I just used the <Textblock/> control instead. The issue was that the Labels were put inside the StatusBar control.

Since Silverlight projects are UserControl as opposed to a Window, I switched the background panel object from a Canvas to a Grid. Part of the reason was since this UserControl runs inside a browser window and does not have any defined border, it was easier to position all the controls a grid. So I added some rows and columns and then put each control into a specific grid column and row and aligned all the controls that way rather than using absolute coordinates. Here is how the UI looks in the designer window.

Image 9

Here is another view with the actual grid rows and columns showing.

Image 10

As you can see, I created a grid with four columns and ten rows. I then assigned each control to a particular row and column and lined them up that way. The game grid itself spans three columns and eight rows. I then centered the game grid within that. The status bar TextBlock is in row ten and spans the first three columns. I added a gray border object to simulate a status bar at the bottom.

For cells that have more than one control, I used a StackPanel object as the base and then aligned the controls within that StackPanel. For example, here is the code for the ComboBox and the label above it:

XML
<StackPanel Grid.Column="3"
            Grid.Row="0">
    <TextBlock Text="Difficulty Level:"
               Height="20"
               Margin="0,12,0,0"
               HorizontalAlignment="Left"
               VerticalAlignment="Bottom" />
    <ComboBox Name="GameLevel"
              HorizontalAlignment="Left"
              VerticalAlignment="Top"
              ItemsSource="{Binding}"
              SelectedIndex="0"
              Width="140"
              SelectionChanged="DifficultyLevel_SelectionChanged"/>
</StackPanel>

I define a StackPanel object and assign it to column three and row zero. I then define a TextBlock object and then the ComboBox object inside that StackPanel.

In the lower right corner of the MainPage, I also used a StackPanel where the elements are oriented horizontally as opposed to vertically, which is the default. Here is an example where I define a style that is local to that StackPanel object.

XML
<StackPanel.Resources>
    <Style TargetType="TextBlock">
        <Setter Property="FontSize" Value="12" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="Margin" Value="5" />
    </Style>
</StackPanel.Resources>

Since all I have in that StackPanel are TextBlock objects, I do not bother with naming the style since I want this Style to apply to all the TextBlock objects within that StackPanel. In other words, the scope of this Style is limited to the StackPanel object where it was defined.

The ComboBox ItemsSource

If you notice the XAML for the ComboBox above, the ItemsSource property is bound to something different than the WPF version. This is because the ObjectDataProvider class is not available in Silverlight. As a work-around, I had to create an ObservableCollection of strings in the code-behind and then bind it to the ItemsSource property of the ComboBox. Here is the code:

C#
private ObservableCollection<string> _levels;

private void InitComboBox()
{
    _levels = new ObservableCollection<string>();
    foreach (string item in Enum.GetNames(typeof(DifficultyLevels)))
        _levels.Add(item);
    this.GameLevel.DataContext = _levels;
}

First, I create a module level variable, then in the InitComboBox method, which is called from the MainPage_Loaded event, I populate the collection with the names from the Enum. Finally, since the DataContext for this combo box is different from the MainPage, I bind the DataContext of the combo box directly to the collection. This is one of the cases where I needed to give the ComboBox control a name. That way, I could access the control from the code-behind.

Since I bind the DataContext of the combo box directly to the ObservableCollection, the ItemsSource binding definition looks like this:

XML
ItemsSource="{Binding}"

We do not have to specify a path or property in the binding declaration.

I could have also created an ObservableCollection property in the ViewModel class and bound the combo box's ItemsSource property to that property rather than in the code behind.

Another difference that you might notice is that the ComboBox's SelectedIndex property is not bound to a property in the ViewModel class and that I also defined a SelectionChanged click event. There is a reason for this and I will explain later on when I continue with the differences between WPF and Silverlight.

Silverlight and DataTriggers

I was kind of shocked that Silverlight does not have DataTriggers. So how was I going to use all the fancy triggers that I wrote for the WPF project? Well, Silverlight has something called DataTemplates. For highlighting the cells as the user mouses over them, I used the VisualStateManager class.

I had to essentially redo the model and created an abstract BaseCell class. I then added an AnswerCell, BlankCell, HintCell, etc. that all inherit from the BaseCell class for each state in the CellStateEnum. Since the BaseCell is complete, the derived cells do not need to have any extra properties or methods.

Here is the BaseCell abstract class which has a single public property based on the CellClass:

C#
public abstract class BaseCell
{
    public CellClass Cell { get; set; }
}

Then each of the other state classes derive from this abstract class. They all look the same so I will just include the definition for the AnswerCell class.

C#
public class AnswerCell : BaseCell
{ }

As you can see, each class inherits from the abstract class and the class definition is blank.

Then, for each of these state classes, I created a data template. So, for the Answer class, the corresponding DataTemplate looks like this. But first, I needed to create the base cell style.

XML
<Style x:Key="CellOutputStyle"
       TargetType="TextBlock">
    <Setter Property="Height" Value="39"/>
    <Setter Property="Width" Value="34" />
    <Setter Property="TextAlignment" Value="Center" />
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="VerticalAlignment" Value="Top" />
    <Setter Property="Foreground" Value="Black" />
    <Setter Property="FontSize" Value="24" />
</Style>

Then, the DataTemplate for the AnswerCell looks like this:

XML
<DataTemplate DataType="model:AnswerCell">
    <Grid>
        <TextBlock Style="{StaticResource CellOutputStyle}"
                   Text="{Binding Path=Cell.Answer}"/>
    </Grid>
</DataTemplate>

The Text property of TextBlock is bound to underlying cell's Answer property.

I then create a DataTemplate for each and every cell state data type. Basically, each DataTemplate starts out with a base Grid and inside the grid, I add a TextBlock control to display data. I also set the Foreground color property to depending on whether it is displaying the answer (black, default), a hint (purple), or the user's correct (green) or incorrect answer (red).

I also need to add the Model.Structures namespace to the top of the XAML code so that it knows where these classes reside.

XML
xmlns:model="clr-namespace:Sudoku_Silverlight.Model.Structures"

Then the Content property of each cell in the game grid is bound to a Cell property in the ViewModel that returns the BaseCell class. So, in this example, the Content property of the first cell (row zero, column zero) is bound to the Cell00 property of the ViewModel class.

XML
<Button Style="{StaticResource CellStyleBlue}"
        Content="{Binding Path=Cell00, Mode=OneWay}"
        Grid.Column="0"
        Grid.Row="0"
        Click="A0_CellClick" />

And the definition for Cell00 in the ViewModel class looks like this:

C#
public BaseCell Cell00
{
    get
    {
        return GetCell(0, 0);
    }
}

I then add an extra data layer between the model and the ViewModel which is basically a list of BaseCell objects.

C#
private List<BaseCell> Cells { get; set; }

Then when I need to display the different states, I simply replace the cell object at a particular index in the list with the corresponding class. For example, if I want to display the answer in the first cell, I simply assign the first element in the list to an AnswerCell class like so.

C#
Cells[0] = new AnswerCell() { Cell = item };

where item is the actual CellClass object from the Model for the first cell. Then I raise the PropertyChanged event for that cell so the UI will update the cell contents with the DataTemplate for the AnswerCell.

C#
OnPropertyChanged(item.CellName);

If you notice, in the XAML code for the Cell, there is no pointer to the DataTemplate that we created earlier. But because the cell is bound to the abstract BaseCell and that in turns points to an AnswerCell class, Silverlight uses the AnswerCell DataTemplate to format that cell. Pretty cool, right?

If I were to do this properly, I would have modified the Model layer to output this list instead of creating it in the ViewModel layer. Also, I would use indexers so that I do not have to define a property for each cell. But this works so let us leave it the way it is.

Highlighting Cells in Silverlight

The final trigger I needed to translate was highlighting the cell as the user mouses over each cell. We do this by using the VisualStateManager class. Here is the XAML code to do this:

XML
<Style x:Key="CellStyleBlue"
       TargetType="ContentControl"
       BasedOn="{StaticResource BaseCellStyle}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ContentControl">
                <Grid HorizontalAlignment="Center"
                      VerticalAlignment="Center"
                      Margin="3">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualStateGroup.Transitions>

                                <!--Take one half second
                                    to transition to the MouseOver state.-->
                                <VisualTransition To="MouseOver"
                                                  GeneratedDuration="0:0:0.5"/>
                            </VisualStateGroup.Transitions>

                            <VisualState x:Name="Normal" />

                            <!--Change the SolidColorBrush (ButtonBrush)
                                to CadetBlue when the
                                mouse is over the button.-->
                            <VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="ButtonBrush"
                                                    Storyboard.TargetProperty="Color"
                                                    To="CadetBlue" />
                                </Storyboard>
                            </VisualState>

                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Grid.Background>
                        <SolidColorBrush x:Name="ButtonBrush" Color="AliceBlue" />
                    </Grid.Background>
                    <ContentPresenter
                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

This looks pretty complicated, but it is actually pretty simple. We create a Style and within that Style, we add a Template property to define the visual states that we care about. Basically, we care about the Normal and MouseOver states. When the user mouses over a cell, we change the Background property of the Grid object from AliceBlue to CadetBlue. In addition, take 0.5 seconds to make the transition.

Even though we are not doing anything in the Normal state, if we remove that line, the cell will remain highlighted even after the mouse leaves the cell.

The interesting thing here is that even though the Styles all target the ContentControl, I actually apply the style to a Button control. When I change it to a ContentControl, the highlighting does not work, but everything else does. I am not sure why the highlighting does not work properly. Maybe after writing this article, I will spend some time to try and figure it out.

One of the differences between the highlighting in the WPF application versus the Silverlight application is that the highlight appears for all the cells in the Silverlight version regardless of the underlying data. In the WPF version, if the underlying data is the Answer, the highlighting is disabled. This is because in Silverlight, the IsEnabled property is missing for the TextBlock control.

Invalid Cross-thread Access Error

After making the necessary changes to get the Silverlight project compiled, I tried to run it. It did not get far before it spit an "Invalid Cross-thread access error" in one of the OnPropertyChanged events. Apparently, Silverlight does not handle multi-threaded applications as well as WPF does. I searched Google and the solution was to use the Dispatcher.CheckAccess() method to check for cross-thread access. Here is how it is used:

C#
private void SetTextBlockContent(TextBlock target, string value)
{
    if (target.Dispatcher.CheckAccess())
        target.Text = value;
    else
        target.Dispatcher.BeginInvoke(() => { target.Text = value; });
}

This is the same problem with WinForms applications where background threads are not allowed to access the UI controls directly. Instead, one needs to check InvokeRequired. Here is the WinForms equivalent:

C#
delegate void SetTextCallback(string text);

private void SetText(string text)
{
    if (this.textBox1.InvokeRequired)
    {
        SetTextCallback d = new SetTextCallback(SetText);
        this.Invoke(d, new object[] { text });
    }
    else
        this.textBox1.Text = text;
}

I could have spent more time trying to figure out which ones need the Dispatcher and which ones do not. Instead, I just decided to use the Dispatcher pretty much every thing. Since using the Dispatcher is just like using InvokeRequired in WinForms, I decided to use custom events to get the ViewModel to talk to the View and vice versa. While I still raise the PropertyChanged event for some things and I still set the DataContext property of the MainPage to the ViewModel, I fire off a custom event for pretty much any kind of communication between the two just to get things working properly.

Earlier, I mentioned that the combo box SelectedIndex property does not point to a property in the ViewModel. And that I also defined an event handler for the SelectionChanged event. The reason I did this was because of this Dispatcher issue. So, when the user changes the selection in the combo box, it fires an event with the new selection and the ViewModel class is listening for the change.

After making all these changes, the code-behind of the Silverlight project looks like a cross between WPF and WinForms.

Invalid XML

Another issue came up when I was entering XAML code for the MainPage. The XAML editor window would display a squiggly line under the new code I was entering. When I mouse over the squiggly line, the editor would tell me that the underlying code was "Invalid XML." It would complain for something as simple as the following:

XML
<DataTemplate DataType="model:AnswerCell">

</DataTemplate>

After searching Google, the answer had nothing to do with the XAML code I was entering, but rather, it was the name of the application. When I created the project, I named it "Sudoku Silverlight" with a space in between. Apparently, this causes problems all over the place and this is one of the symptoms. After going to the project properties page and removing the space, the "Invalid XML" error went away. That was a really weird error.

So bottom line, be careful when naming your project and do not use spaces in the name.

Child Windows

Because Silverlight works inside a browser, opening a child window is different from a WPF or Windows application in general. For one thing, it does not have access to the mouse position. Second, it does not have any control over where the window is placed. Silverlight will always center it within the browser window. Another thing that is missing in Silverlight is the ShowDialog method. It only has Show. Since Show is non-modal, it will return after opening the child window and does not wait around for the child window to close.

With those differences in mind, I had to tackle the child windows, especially the Input Pad window, differently than I would in a WinForms or WPF application. Fortunately, there is a Closed event that one can wire up an event handler to.

Here is the code that opens the Input Pad window:

C#
private void UIShowNumberPad(CellIndex callingCell)
{
    InputPad inputPad = new InputPad(callingCell);
    inputPad.Closed += NumberPadClosed;
    inputPad.Show();
}

I create an instance of the child window, wire up the event handler, then open it. Here is the corresponding code that handles the Closed event.

C#
private void NumberPadClosed(object sender, EventArgs e)
{
    InputPad inputPad = (InputPad)sender;
    if (inputPad.InputPadState != InputPadStateEnum.InvalidState)
        RaiseEvent(inputPad.CallingCell, inputPad.InputPadState);
}

When the user closes the Input Pad window by either clicking the "X" or a button on the window, I raise an event with some data so that the ViewModel knows which cell was last clicked as well as which button on the Input Pad the user clicked.

Message Box In Silverlight

Unlike a MessageBox in a WinForms application, the MessageBox in Silverlight has several limitations, I decided not to use it because I could not control the text in the buttons. By default, it has an "OK" button. There is a parameter in the method call where I can also add a "Cancel" button as well. But I cannot change the button text to say "Yes" and "No", so I decided to create my own Message Box.

Image 11

It was not all that difficult. I used a Grid as the base and added three columns and two rows. For the second row, where the buttons are located, the Height was set to "Auto".

XML
<Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition Height="Auto" />
</Grid.RowDefinitions>

Instead of splitting the height evenly between the two rows, Auto sets the row height to whatever the height is of the controls on that row. And the rest of it is given to the first row. As for how this window works, it is similar to the way the Input Pad window works. I wire up a Closed event handler and it returns information whether the user clicked Yes or No.

Wrapping Up The Silverlight Project

In the end, the code-behind for the MainPage looks like a cross between WPF and WinForms. After publishing this article, I might spend some time to see if I can reduce the number of Dispatcher calls as well as the number of custom events that are raised between the View and the ViewModel.

After working through the differences between WPF and Silverlight and cleaning up the game play code, the final step was to wrap up the application start/stop code. Since the project runs in a browser, we cannot close the application from within. So there is no need for a close button. We do, however, know when the application closes because there is an Exit event in the Application class.

I open the App.xaml.cs file and wire up the Application_Exit handler in such a way that when the user closes the tab or browser where this application is running, it will call a method to stop all the background threads as well as save the current settings to the IsolatedStorageSettings.

Here is how the Silverlight application looks like when running:

Image 12

It looks pretty close to the WPF version. The main difference is the lack of a title bar and line separators between the numbers in the lower right corner. I also added a border element around the user control so that when it runs in a browser, the user knows where the bounds of the game window is.

Conclusion

This was a fun project to try to convert a VB.NET WinForms application to C#/WPF and Silverlight. I learned a lot about both about WPF and Silverlight. Even though Silverlight is a subset of WPF, there are enough differences between the two that sharing XAML code would be a difficult task. The biggest difference for me is the lack of triggers in Silverlight.

However, if a project really needed to share the same code base between the two, with careful planning and lots of compiler directives, it can be done. There are several articles available on-line that talk about code re-use and code sharing between WPF, Silverlight, and Windows Phone/Store App. Basically, take the lowest common denominator and write code to use that subset. If done right, the code will be portable across all three platforms.

For this project, had I started out with Silverlight, it might have been much easier to port the code to WPF than the other way around. Maybe I will tackle that question at a later date. That is, try to port the Silverlight project to WPF and see just how much is actually re-usable.

I hope that by detailing all the issues I went through, it will help someone else out with their project.

One of the things that you will notice is that I created a property for each cell in the game grid as well as a matching click event handler. That comes out to 81 properties and 81 click events. I am sure there is a way to simplify that by passing parameters from the WPF XAML with the cell's row and column. It will be another thing that I will look into.

Feel free to contact me with questions or issues about anything in this article or if you would like me to go into more details about a particular issue or topic that I covered above or failed to cover.

History

  • 2015-01-01: First release of the code/article

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer (Senior)
United States United States
Senior software developer with over 30 years of experience.

Started programming on the Apple II platform back in the 80's. Over the years, I have programmed in various languages, including, but not limited to VB 3.0 through VB 6.0, Forth, and C.

Currently programming professionally in .Net using VB and C#.

Wrote many Windows applications, services, and dlls as well as ASP.Net web-services. Also wrote WPF applications as well as WCF services using both VB.Net and C#.

Dabbling with Flutter, PHP, and Laravel.

Comments and Discussions

 
QuestionGreat WPF Project Pin
RedSin14-Jun-15 11:02
RedSin14-Jun-15 11:02 
GeneralMy vote of 5 Pin
raja mohan7-Jan-15 2:52
raja mohan7-Jan-15 2:52 
GeneralThis is a great game! Thanks Pin
dataporter4-Jan-15 17:10
dataporter4-Jan-15 17:10 
GeneralMy vote of 5 Pin
Klaus Luedenscheidt1-Jan-15 20:16
Klaus Luedenscheidt1-Jan-15 20:16 
GeneralRe: My vote of 5 Pin
Hung, Han2-Jan-15 4:55
Hung, Han2-Jan-15 4:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.