65.9K
CodeProject 正在变化。 阅读更多。
Home

将简单的“计算机仿真”应用程序从 WINFORM 迁移到 WPF

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.52/5 (8投票s)

2008年11月5日

CPOL

6分钟阅读

viewsIcon

38941

downloadIcon

700

展示了将一个简单应用程序从WINFORM移植到WPF的一些步骤、陷阱和差异

WinForm

Sample Image - maximum width is 600 pixels

WPF

Sample Image - maximum width is 600 pixels

目录

引言

本文不是关于应用程序本身,而是关于将应用程序从WinForm移植到WPF。将一个简单应用程序从WinForm移植到WPF有多难?代码会发生多大变化?

我对WPF完全陌生,所以我可能得出了错误的结论,或者没有发现已有的功能。欢迎评论和提示。

我花了两天时间编写原始代码。我花了大约一周时间进行移植。

背景

我刚完成一个模拟非常简单计算机的程序。我打算使用这个应用程序来教授一些计算的基本概念。现在我必须编写文档。我读了Code Project上的一篇WPF示例文章,所以我决定移植我的代码,并发现其中的差异。文档总是可以等等。

关于应用程序的说明

该程序模拟了一台非常简单的十进制数字计算机。如果你想使用它,请查看“如何操作”选项卡。它需要更多的文档。

移植代码

自定义控件

该控件必须移动到一个类库中,并且不会像在WinForm中那样出现在工具箱中。由于我的控件派生自TextBox,所以我首先会放置一个TextBox,然后在XAML文件中更改其名称。

所有的DesignerAttributes都被移除,事件变成了重写方法。重写方法与事件不同,所以你必须寻找正确的方法。在Winform中,使用PreFilterProperties移除属性是不可用的。你可以在XAML中访问属性,可能是通过反射。

移除属性似乎不存在。所以以下WinForm代码没有等效项

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

你的类派生自Window而不是Form。所以你处于一个不同的世界。你突然需要更多的程序集。

布局新外观的提示 (XAML)

首先,只尝试一些简单的布局,移动它们,向网格添加列和行,添加一些网格和一些分隔线。几个小时内,甚至不要看你的项目。

划分网格后,放置几个按钮。查看生成的XAML。删除margin、width、height,然后查看结果。大多数情况下,删除这些以及列跨度和行跨度可以改善布局。

UniformGrid

三个UniformGrid用于行标题、行号和内存。

UniformGrid非常有效地取代了TableLayout。具有100个相同元素的内存必须在代码中完成。使用划分的网格来定位元素效果非常好,特别是放置列标题和行号。

XAML可能令人不知所措。

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

要在某一列的左侧显示x0,使用样式可能会有所帮助,但我还没有弄清楚它是如何工作的。我的下一篇阅读是关于XAML的。

数据网格

字段信息再次使用反射找到。行数据驻留在派生自ObservableCollection的类中。你必须将System.Collections.ObjectModel添加到你的程序集中。总的来说,DataGrid非常好用。有很多关于它的文章,所以我不会详细介绍。

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

LineOfCode是行的名称。

行选择

选择行的代码显示了一些有趣的差异。

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];
}

取消选择是受欢迎的。另请注意,行选择是通过将SourceProgram.Items[row]存储到选定项中完成的。这是因为项是单行数据的类。这种设计简化了代码。

更新显示

从文件读取源代码后,必须使DataGrid无效。我找不到任何方法可以做到这一点,除了以下这个丑陋的代码。我欢迎任何提示。

/// <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

使用WinForm,你可以使用Application.DoEvents()

使用WPF,你必须创建一个虚拟线程来拉取消息。

/// <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();
    ...        

背景颜色

BackColor颜色变为Background。这是一个更好的名称,但它需要一个Brush,并且枚举现在来自Colors而不是Color

在DataGridView中编辑和验证单元格

原始版本 (CommittingEdit)

事件发生了显著变化。在某些情况下,它似乎更简单,在其他情况下则不那么简单。我认为他们试图做出普遍改进,但你不能满足所有人,包括你的父亲。

例如,比较SourceProgram_CommittingEdit(object sender, DataGridEndingEditEventArgs e)SourceProgram_CellEndEdit(object sender, DataGridViewCellEventArgs e)中的代码,后者在对某行代码进行更改时发生。你会注意到惊人的差异。

版本 1 (SelectedCellsChanged)

SourceProgram_CommittingEdit已被移除。它没有真正正常工作,希望在不久的将来会重新安装。

如何在没有CommittingEdit的情况下支持字段编辑?这是一个权宜之计,直到CommittingEdit得到支持。

添加了两个变量来记住上一个单元格。

LineOfCode previousLineOfCode = null;
int previousIndex = 0;        

执行字段验证的函数CheckProgram将由各种事件调用。

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

调用CheckProgram的事件

  • SourceProgram_SelectedCellsChanged
  • SourceProgram_PreviewLostKeyboardFocus
  • SourceProgram_LostFocus

后两个似乎并非总是有效。我认为如果你真的需要字段编辑完美工作,你将不得不在所有Getfocus事件中调用CheckProgram

这是如何工作的?

最重要的事件是SelectedCellChanged。当这种情况发生时,上一个单元格已被编辑,应进行检查。问题是当前单元格不是关注的单元格。因此,代码只是检查了记住的上一个单元格。

你必须非常小心,如果在CheckProgram中更改当前单元格,将调用SelectedCellsChanged,并且你将陷入无限递归。因此,如果你想强制用户转到另一个单元格,最好添加一些逻辑来检测这种情况。

文件对话框

不再有像saveFileDialog_FileOk这样的事件,对于其他事件,你必须调用对话框。从某种意义上说,它更原始,但不是大问题。唯一的问题是你的代码将需要进行大量更改。

RTF

WPF尚不支持浏览器。所以我选择RTF作为文档。这可能不是一个好主意。读取RTF文件是不同的。

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);
    } 

多行工具提示 (关于工具提示的提示 :)

你不能使用\n,所以要么你搜索所有控件中的"\\n"并将它们替换为"\n",要么你可以使用以下方法

<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>

这也似乎表明工具提示可以是任何东西。有人想要动画GIF吗???

关注点

值得吗?这做起来很有趣。如果演示是你的产品的重要组成部分,并且你想要一个全新的、真正漂亮的外观,这就是方法。

我还没有真正利用这个平台。我将回去,阅读更多关于它的内容,并尝试改善用户界面的外观。这只是第一次快速移植。

历史

  • 2008年11月2日:初始版本
  • 2008年11月6日:使用WPFTool版本1
© . All rights reserved.