Wpf 简明教程

WPF - Debugging

这是一种识别并修复代码中的错误或缺陷而且它们不会按照您预期的那样工作的系统机制。调试子系统紧密耦合的复杂应用程序不容易,因为在一个子系统中修复错误可能会在另一个子系统中创建错误。

It is a systematic mechanism of identifying and fixing the bugs or defects in a piece of code which are not behaving the same as you are expecting. Debugging a complex application where the subsystems are tightly coupled are not that easy, because fixing bugs in one subsystem can create bugs in another subsystem.

Debugging in C

在 WPF 应用程序中,程序员处理两种语言例如 C# 和 XAML。如果您熟悉在任何过程语言(例如 C# 或 C/C++)中的调试并且您还知道中断点的用法,那么您可以轻松调试应用程序的 C# 部分。

In WPF applications, programmers deal with two languages such as C# and XAML. If you are familiar with debugging in any procedural language such as C# or C/C++ and you also know the usage of break points, then you can debug the C# part of your application easily.

我们通过一个简单的示例来演示如何调试 C# 代码。创建新的 WPF 项目,项目名称为 WPFDebuggingDemo 。从工具箱中拖拽四个标签、三个文本框,以及一个按钮。查看以下 XAML 代码。

Let’s take a simple example to demonstrate how to debug a C# code. Create a new WPF project with the name WPFDebuggingDemo. Drag four labels, three textboxes, and one button from the toolbox. Take a look at the following XAML code.

<Window x:Class = "WPFDebuggingDemo.Window1"
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
   Title = "Window1" Height = "400" Width = "604">

   <Grid>
      <TextBox Height = "23" Margin = "0,44,169,0" Name = "textBox1"
         VerticalAlignment = "Top" HorizontalAlignment = "Right" Width = "120" />

      <TextBox Height = "23" Margin = "0,99,169,0" Name = "textBox2"
         VerticalAlignment = "Top" HorizontalAlignment = "Right" Width = "120" />

      <TextBox HorizontalAlignment = "Right" Margin = "0,153,169,0"
         Name = "textBox3" Width = "120" Height = "23" VerticalAlignment = "Top" />

      <Label Height = "28" Margin = "117,42,0,0" Name = "label1"
         VerticalAlignment = "Top" HorizontalAlignment = "Left" Width = "120">
         Item 1</Label>

      <Label Height = "28" HorizontalAlignment = "Left"
         Margin = "117,99,0,0" Name = "label2" VerticalAlignment = "Top" Width = "120">
         Item 2</Label>

      <Label HorizontalAlignment = "Left" Margin = "117,153,0,181"
         Name = "label3" Width = "120">Item 3</Label>

      <Button Height = "23" HorizontalAlignment = "Right" Margin = "0,0,214,127"
         Name = "button1" VerticalAlignment = "Bottom" Width = "75"
         Click = "button1_Click">Total</Button>

      <Label Height = "28" HorizontalAlignment = "Right"
         Margin = "0,0,169,66" Name = "label4" VerticalAlignment = "Bottom" Width = "120"/>

   </Grid>

</Window>

以下是实现了按钮点击事件的 C# 代码。

Given below is the C# code in which a button click event is implemented.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPFDebuggingDemo {
   /// <summary>
      /// Interaction logic for Window1.xaml
   /// </summary>

   public partial class Window1 : Window {

      public Window1() {
         InitializeComponent();
      }

      private void button1_Click(object sender, RoutedEventArgs e) {

         if (textBox1.Text.Length > 0 && textBox2.Text.Length > 0 && textBox2.Text.Length > 0) {
            double total = Convert.ToDouble(textBox1.Text) +
            Convert.ToDouble(textBox2.Text) + Convert.ToDouble(textBox3.Text);
            label4.Content = total.ToString();
         }
         else {
            MessageBox.Show("Enter the value in all field.");
         }
      }
   }
}

当您编译并执行以上代码时,它将生成以下窗口。现在在文本框中输入值并按下 Total 按钮。您将在所有文本框中输入值的总和后获得总值。

When you compile and execute the above code, it will produce the following window. Now enter values in the textboxes and press the Total button. You will get the total value after summation of all the values entered in the textboxes.

debugging

如果您尝试输入除实际值之外的值,则上述应用程序将崩溃。为了找到并解决问题(为什么它崩溃),您可以在按钮点击事件中插入中断点。

If you try to enter values other than real values, then the above application will crash. To find and resolve the issue (why it is crashing), you can insert break points in the button click event.

我们如下一文所示在项 1 中写入“abc”。

Let’s write "abc" in item 1 as shown below.

write in item1

单击 Total 按钮后,您将看到程序在断点处停止。

Upon clicking the Total button, you will see that the program stops at the break point.

program is crashing

现在将光标移动到 textbox1.Text,您将看到程序正尝试将 abc 值与其他值相加,这就是程序崩溃的原因。

Now move the cursor towards the textbox1.Text and you will see that the program is trying to add abc value with the other values which is why the program is crashing.

Debugging in XAML

如果您希望在 XAML 中进行同样的调试,您会惊讶地知道至今仍无法像调试任何其他过程语言代码一样调试 XAML 代码。当您在 XAML 代码中听到术语“调试”时,它意味着尝试并找到错误。

If you are expecting the same kind of debugging in XAML, then you will be surprised to know that it is not possible yet to debug the XAML code like debugging any other procedural language code. When you hear the term debugging in XAML code, it means try and find an error.

  1. In data binding, your data doesn’t show up on screen and you don’t know why

  2. Or an issue is related to complex layouts.

  3. Or an alignment issue or issues in margin color, overlays, etc. with some extensive templates like ListBox and combo box.

Debugging an XAML program is something you typically do to check if your bindings work; and if it is not working, then to check what’s wrong. Unfortunately setting breakpoints in XAML bindings isn’t possible except in Silverlight, but we can use the Output window to check for data binding errors. Let’s take a look at the following XAML code to find the error in data binding.

<Window x:Class = "DataBindingOneWay.MainWindow"
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
   Title = "MainWindow" Height = "350" Width = "604">

   <Grid>
      <StackPanel Name = "Display">
         <StackPanel Orientation = "Horizontal" Margin = "50, 50, 0, 0">
            <TextBlock Text = "Name: " Margin = "10" Width = "100"/>
            <TextBlock Margin = "10" Width = "100" Text = "{Binding FirstName}"/>
         </StackPanel>

         <StackPanel Orientation = "Horizontal" Margin = "50,0,50,0">
            <TextBlock Text = "Title: " Margin = "10" Width = "100"/>
            <TextBlock Margin = "10" Width = "100" Text = "{Binding Title}" />
         </StackPanel>

      </StackPanel>
   </Grid>

</Window>

Text properties of two text blocks are set to “Name” and “Title” statically, while other two text blocks Text properties are bind to “FirstName” and “Title” but class variables are Name and Title in Employee class which is shown below.

We have intentionally written an incorrect variable name so as to understand where can we find this type of a mistake when the desired output is not shown.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataBindingOneWay {

   public class Employee {
      public string Name { get; set; }
      public string Title { get; set; }

      public static Employee GetEmployee() {

         var emp = new Employee() {
            Name = "Ali Ahmed", Title = "Developer"
         };

         return emp;
      }
   }
}

这是用 c# 代码实现 MainWindow 类的方法。

Here is the implementation of MainWindow class in C# code.

using System;
using System.Windows;
using System.Windows.Controls;

namespace DataBindingOneWay {
   /// <summary>
      /// Interaction logic for MainWindow.xaml
   /// </summary>

   public partial class MainWindow : Window {

      public MainWindow() {
         InitializeComponent();
         DataContext = Employee.GetEmployee();
      }
   }
}

让我们运行此应用程序,您会立即看到在我们的 MainWindow 中,我们已经成功绑定到那个 Employee 对象的 Title,但是 name 没有绑定。

Let’s run this application and you can see immediately in our MainWindow that we have successfully bound to the Title of that Employee object but the name is not bound.

debugging in xaml

为了检查 name 发生了什么,让我们来看看生成了很多日志的输出窗口。

To check what happened with the name, let’s look into the output window where a lot of log is generated.

找到错误很容易,只需搜索错误,您就会找到以下错误,“BindingExpression 路径错误:在 'object' 'Employe' 上未找到 'FirstName' 属性”。

The easy to find an error is just to search for the error and you will find the following error which says “BindingExpression path error: 'FirstName' property not found on 'object' ''Employe”

System.Windows.Data Error: 40 : BindingExpression path error: 'FirstName'
   property not found on 'object' ''Employee' (HashCode=11611730)'.
   BindingExpression:Path = FirstName; DataItem = 'Employee' (HashCode = 11611730);
   target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

这清楚地表明,FirstName 不是 Employee 类的一个成员,所以它有助于修复您应用程序中的此类问题。

Which clearly indicate that FirstName is not a member of Employee class, so it helps to fix this type of issues in your application.

当您将 FirstName 再次更改为 Name 时,您会看到所需的输出。

When you change the FirstName to Name again, then you will see the desired output.

UI Debugging Tools for XAML

引入了 UI 调试工具,在 Visual Studio 2015 中用 XAML 在运行时检查 XAML 代码。在这些工具的帮助下,XAML 代码以运行的 WPF 应用程序的视觉树的形式呈现,还有树中的不同 UI 元素属性。要启用这些工具,请按照以下步骤操作。

UI debugging tools were introduced for XAML with Visual Studio 2015 to inspect the XAML code at runtime. With the help of these tools, XAML code is presented in the form of a visual tree of your running WPF application and also the different UI element properties in the tree. To enable these tools, follow the steps given below.

  1. Go to the Tools menu and select Options from the Tools menu.

  2. It will open the following dialog box.

debugging tools
  1. Go to the General Options under Debugging item on the left side.

  2. Tick the highlighted option, i.e., “Enable UI Debugging Tools for XAML” and click the OK button.

现在运行任何 XAML 应用程序或使用以下 XAML 代码。

Now run any XAML application or use the following XAML code.

<Window x:Class = "XAMLTestBinding.MainWindow"
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
   Title = "MainWindow" Height = "350" Width = "604">

   <StackPanel>
      <ComboBox Name = "comboBox"  Margin = "50" Width = "100">
         <ComboBoxItem Content = "Green" />
         <ComboBoxItem  Content = "Yellow" IsSelected = "True" />
         <ComboBoxItem Content = "Orange" />
      </ComboBox>

      <TextBox  Name = "textBox" Margin = "50" Width = "100" Height = "23"
         VerticalAlignment = "Top" Text  =
         "{Binding ElementName = comboBox, Path = SelectedItem.Content, Mode = TwoWay, UpdateSourceTrigger = PropertyChanged}"
         Background = "{Binding ElementName = comboBox, Path = SelectedItem.Content}">
      </TextBox>

   </StackPanel>

</Window>

当您执行应用程序时,它将显示 Live Visual Tree,其中所有元素都显示在树中。

When you execute the application, it will show the Live Visual Tree where all the elements are shown in a tree.

live visual tree

此 Live Visual Tree 显示了完整的布局结构,以了解 UI 元素位于何处。但此选项仅在 Visual Studio 2015 中可用。如果您使用的是较旧版本的 Visual Studio,则无法使用此工具,但是,还有另一个可以与 Visual Studio 集成的工具,如 Visual Studio 的 XAML Spy。您可以从 xamlspy 下载它。

This Live Visual Tree shows the complete layout structure to understand where the UI elements are located. But this option is only available in Visual Studio 2015. If you are using an older option of Visual Studio, then you can’t use this tool, however there is another tool which can be integrated with Visual Studio such as XAML Spy for Visual Studio. You can download it from xamlspy