WinForm
WPF
Contents
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.
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.
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.
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");
}
}
Your class is derived from Window instead of Form. So you are in a different world. You suddenly need a lot more assemblies.
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.
Three UniformGrid
s 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.
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.
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.
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.
private void UpdateDisplay()
{
SourceProgram.SelectedItem = SourceProgram.Items[99];
SourceProgram.ScrollIntoView(SourceProgram.SelectedItem);
DoEvents();
SourceProgram.SelectedItem = SourceProgram.Items[0];
SourceProgram.ScrollIntoView(SourceProgram.SelectedItem);
DoEvents();
}
With WinForm, you use Application.DoEvents()
.
With WPF, you have to create a dummy thread to pull the messages.
private void DoEvents()
{
Application.Current.Dispatcher.Invoke
(DispatcherPriority.Background, new ThreadStart(delegate { }));
}
private bool Step()
{
DoEvents();
...
BackColor
color becomes Background
. This is a better name but it expects a Brush
and the enum
is now from Colors
instead of Color
.
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.
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)
...
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.
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.
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);
}
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???
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.
- 2nd November, 2008: Initial version
- 6th November, 2008: Use version 1 of the
WPFTool