Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Porting a Simple Application Computer Emulation from WINFORM to WPF

0.00/5 (No votes)
7 Nov 2008 1  
Shows some of the steps, pitfalls and differences in porting a simple application from WINFORM to WPF

WinForm

Sample Image - maximum width is 600 pixels

WPF

Sample Image - maximum width is 600 pixels

Contents

Introduction

The article is not about the application but about the port of the application from WinForm to WPF. How hard is it to port a simple application from WinForm to WPF? How much is the code changing?

I am completely new to WPF, so I may have reached the wrong conclusions or did not find features that exist. I welcome comments and hints.

It took me two days to write the original code. I spent about a week porting it.

Background

I just finished writing a simple program to simulate a very simple computer. I intend to use this application to teach some basic concepts of computing. I now had to write the documentation. I read an article from The Code Project with an example of WPF so I decided to port my code and discover what and where the differences were. Documentation can always wait.

Note about the App

The program simulates a very simple decimal digital computer. Look at the How To tab if you want to use it. It is in need of much more documentation.

Porting the Code

Custom Control

The control has to be moved to a class library and will not appear in the toolbox like it does in WinForm. Since I derived mine from a TextBox, I would first drop a TextBox and then change the name in the XAML file.

All the DesignerAttributes are removed and the events become override methods. The override methods are different from the events so you have to chase for the right one. Removing properties using PreFilterProperties in Winform is not available. You have access to the properties in XAML probably through reflection.

Removing properties does not seem to exist. So the following WinForm code has no equivalent:

public class DesignMemoryLocation : 
	System.Windows.Forms.Design.ParentControlDesigner
{
    protected override void PreFilterProperties
		(System.Collections.IDictionary properties)
    {
        properties.Remove("Multiline");
        properties.Remove("Text");
        properties.Remove("MaxLength");
    }
}

Application

Your class is derived from Window instead of Form. So you are in a different world. You suddenly need a lot more assemblies.

Hint on Laying Out your New Look (XAML)

At first, just try a few simple layouts, move them around, add columns and rows to the grid, add some grids and some dividers. For a couple of hours, don't even look at your project.

After dividing your grid, drop a few buttons. Look at the XAML generated. Delete the margin, width, height and then look at the result. Most of the time, deleting these and the column span and row span improves the layout.

UniformGrid

Three UniformGrids are used for the row headings, row numbers and the memory.

UniformGrid replaces TableLayout very effectively. The memory with a 100 identical elements has to be done in code. Using a divided grid to position the elements works really well, particularly to place the column headings and row numbers.

The XAML can be overwhelming.

<TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom">x0</TextBlock>

To display x0 on the left of one column, using a style may help but I have not yet figured out how it works. My next reading is about XAML.

Datagrid

Information about the fields is found again using reflection.  The row data resides in a class derived from ObservableCollection. You have to add  System.Collections.ObjectModel to your assembly. Overall the DataGrid is really great to work with. Many articles are being written about it, so I will not go into more detail.

using System.Collections.ObjectModel;
    public class Program : ObservableCollection<LineOfCode>

LineOfCode is the name of the row.

Row selection

The code for selecting a row shows some interesting differences.

Winform:

void SelectRow(int row)
{
    for (int r = 0; r < SourceProgram.Rows.Count; r++)
        SourceProgram.Rows[r].Selected = r == row;
    SourceProgram.FirstDisplayedCell = SourceProgram[0, row];
}

WPF:

void SelectRow(int row)
{
    SourceProgram.UnselectAllCells();
    SourceProgram.SelectedItem = SourceProgram.Items[row];
}

The unselect is welcome. Also notice that the row selection is done by storing  SourceProgram.Items[row] into the selected item. This is because item is a class for one row of data. This design simplifies the code.

Updating the Display

After the source code is read from a file, the DataGrid has to be invalidated. I could not find any way to do this except the following code which is ugly. I will welcome any hint.

/// <summary>
/// Force the display to be updated
/// there must be a better way to do this !!!
/// </summary>
private void UpdateDisplay()
{
    // there must be a better way to do this !!!
    SourceProgram.SelectedItem = SourceProgram.Items[99];
    SourceProgram.ScrollIntoView(SourceProgram.SelectedItem);
    DoEvents();
    SourceProgram.SelectedItem = SourceProgram.Items[0];
    SourceProgram.ScrollIntoView(SourceProgram.SelectedItem);
    DoEvents();
}

DoEvents

With WinForm, you use Application.DoEvents().

With WPF, you have to create a dummy thread to pull the messages.

/// <summary>
/// Allows the completion of any UI processing
/// </summary>
private void DoEvents()
{
    Application.Current.Dispatcher.Invoke
	(DispatcherPriority.Background, new ThreadStart(delegate { }));
}

private bool Step()
{
    DoEvents();
    ...        

Background Colour

BackColor color becomes Background. This is a better name but it expects a Brush and the enum is now from Colors instead of Color.

Editing and Validating Cells in DataGridView

Original Version (CommittingEdit)

The events have changed significantly. In some cases it seems simpler, in others not as much. I think they try to make general improvements but you cannot satisfy every one and your father.

For example, compare SourceProgram_CommittingEdit(object sender, DataGridEndingEditEventArgs e)to the code in SourceProgram_CellEndEdit(object sender, DataGridViewCellEventArgs e) which occurs whenever a change is made to one of the lines of code. You will notice a surprising number of differences.

Version 1 (SelectedCellsChanged)

SourceProgram_CommittingEdit was removed. It did not work really right and hopefully it will be re-installed in the near future.

How to support field editing without CommittingEdit? This is a step gap solution until CommittingEdit is supported.

Two variables are added to remember the previous cell.

LineOfCode previousLineOfCode = null;
int previousIndex = 0;        

The function CheckProgram which does the field verification will be invoked by various events.

private void CheckProgram(DataGrid program)
{
    if (!(program.CurrentItem is LineOfCode))
        return;
    LineOfCode checkLine = previousLineOfCode;
    int checkColumnIndex = previousIndex;
    previousLineOfCode = (LineOfCode)program.CurrentItem;
    previousIndex = program.CurrentColumn.DisplayIndex;
    if (checkLine == null)
        return;
    switch (checkColumnIndex)
    ...// validate the field

Events which invoke CheckProgram:

  • SourceProgram_SelectedCellsChanged
  • SourceProgram_PreviewLostKeyboardFocus
  • SourceProgram_LostFocus

The last two do not always seem to be working. I think that if you really need field editing to work perfectly, you would have to invoke CheckProgram in all the Getfocus events.

How does this work?

The most important event is SelectedCellChanged. When this happens, the previous has been edited and should be checked. The problem is that the current cell is not the one that is of interest. So the code simply checks the previous cell which was remembered.

You have to be very careful if you change the current cell within CheckProgram, the SelectedCellsChanged will be invoked and you will be within an infinite recursion. So you better add some logic to detect this case if you want to force the user to another cell.

File Dialog

There are no more events like saveFileDialog_FileOk, and for others you have to invoke the dialog. In one way, it is more primitive but not a big deal. The only problem is that a lot of changes will have to be done to your code.

RTF

WPF does not support a browser yet. So I choose RTF for the documentation. This may be a bad idea. Reading an RTF file is different.

Winform:

if (File.Exists("howto.rtf")) HowTo.LoadFile("howto.rtf"); 

WPF:

if (File.Exists("howtox.rtf"))
    using (FileStream fs = File.Open("howto.rtf", FileMode.Open))
    {
        TextRange documentTextRange = new TextRange(
        HowTo.Document.ContentStart,
        HowTo.Document.ContentEnd);
        documentTextRange.Load(fs, DataFormats.Rtf);
    } 

Multiline Tooltip (Tip on Tooltips :)

You cannot use \n so either you search all the controls for "\\n" and replace them with "\n" or you can use the following:

<Button Grid.Column="1"  Grid.Row="2"  Margin="10" Name="StopButton" 
	Opacity="0.65" Grid.ColumnSpan="3" Click="StopButton_Click" >
    <Button.ToolTip>
        <TextBlock>
            Depress to stop a running program.
            <LineBreak/>
            Highlighted when a halt instruction is executed.
        </TextBlock>
    </Button.ToolTip>
    Stop
</Button>

This also seems to demonstrate that tooltip could be anything. Animated GIF anyone???

Points of Interest

Is it worth it? It is very interesting to do. If presentation is a significant part of your product and you would like to have a new really neat look, this is the way to go.

I have not truly taken advantage of this platform. I will go back, read more about it and try to improve the look of the user interface. This is a first quick port.

History

  • 2nd November, 2008: Initial version
  • 6th November, 2008: Use version 1 of the WPFTool

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here