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

如何添加动画 3D WPF 启动屏幕

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (21投票s)

2010年10月26日

CPOL

9分钟阅读

viewsIcon

86978

downloadIcon

14172

解释了如何使用 WPF 和 XAML 为 WinForms 应用程序添加动画 3D 启动屏幕。

引言

本文基于 Visual Studio 2010。构建的目标 .NET Framework 是 4.0,因此您的计算机应具备构建和运行项目所需的所有软件。

Microsoft WPF 提供了 3D 内容,您可以将其添加到桌面应用程序或基于浏览器的应用程序中。但是,3D 内容有什么用呢?它到底有什么实际用途?如果您正在制作游戏或 3D 建模应用程序,答案是显而易见的。但对于大多数构建普通应用程序的人来说,很难找到一个可以融入 3D 内容的位置。

3D 模型的一种用途可能是作为启动屏。本文将介绍如何为 WinForms 应用程序添加一个启动屏。启动屏会逐渐淡入,并包含动画 3D 内容。然后它会淡出,显示您的应用程序。

这可能看起来是一个没有人真正需要的奇特例子。但它是为外观普通的程序增加一些闪光点和视觉趣味的方法。对于那些想动手进行一些 3D 编程的人来说,这是一个简单的入门方法。此外,本文还教授了一些基础知识。它将展示如何将 WPF 控件集成到普通的 Windows Forms 应用程序中。它展示了如何将 XAML 代码作为嵌入资源包含在您的应用程序中。它还包含一些其他简单但有用的技术,例如如何获取屏幕工作区以及如何使用计时器实现一些简单的透明度动画。

首先,打开 Visual Studio 2010 并启动一个 WinForms 项目。

从文件菜单中选择“新建”和“项目”。对于模板,在左侧树状列表中选择“Windows”下的“Visual C#”,然后在右侧列表中选择“Windows Forms Application”,输入您想要的**项目名称 (SplashScreenInWPF)** 和位置,然后单击“确定”。

由于您在 Windows Forms 项目中使用 WPF 控件,因此需要为项目添加一些引用。右键单击解决方案资源管理器中的项目下的“引用”,然后添加以下引用:

  • PresentationCore
  • PresentationFramework
  • System.XAML
  • WindowsBase
  • WindowsFormsIntegration

您将得到一个包含一个名为 Form1 的窗体。右键单击解决方案资源管理器中的 Form1.cs,然后选择“查看代码”。

您需要在源文件顶部添加一些 using 语句:

using System.IO;

这样可以允许您使用 StreamReader 从嵌入的资源中读取 XAML。

using System.Windows.Markup;

这样可以允许您处理 XAML 内容。

using System.Windows.Media.Media3D;

这样可以允许您在应用程序中包含 3D 模型。

using System.Windows.Controls;
	using System.Windows.Forms.Integration;

这样可以允许您在 Winforms 应用程序中托管 WPF 控件。

现在,在设计器中打开 Form1 ,右键单击窗体以打开窗体属性页,并进行以下更改:

FormBorderStyle 设置为“None”,因为您不希望在启动屏上显示任何边框、栏或菜单。

ShowInTaskBar 设置为“False”。您不希望在任务栏中显示启动屏。

在“视图”菜单上单击“工具箱”以显示工具箱窗口。在“所有 Windows Forms”类别下,在工具箱中找到一个 Timer ,并将其拖到 Form1 的空白区域。这会将一个 Timer 添加到窗体中,以控制您的淡入和淡出行为。

双击窗体的空白区域以添加一个 Load 事件。代码如下:

private void Form1_Load(object sender, EventArgs e)
{
}

双击计时器以添加一个 Tick 事件。代码如下:

private void timer1_Tick(object sender, EventArgs e)
{
}

您需要为项目添加一个文本文件,其中包含描述您希望在启动屏中显示的 3D 内容和动画的 XAML。生成精确的 XAML 代码超出了本文的范围,但稍后我将解释如何创建此项目的动画 3D 内容,并告诉您在哪里可以获取一些有用的工具。现在,我在本文的随附文件中包含了一个名为 splashXAML.txt 的文件。将文件复制到您的项目目录并将其添加到项目中。右键单击解决方案资源管理器中的项目名称,然后选择“添加”,然后选择“现有项”,然后浏览 splashXAML.txt 文件并双击以将其添加到项目中。

右键单击解决方案资源管理器中的 splashXAML.txt 文件,然后选择“属性”以打开文件的属性页。将文件的“生成操作”设置为“嵌入的资源”。

Form1 类中添加一个函数,该函数可以从您的资源中读取 XAML。这是您需要的代码:

private string getSplashXAML()
{
    string contents;

    System.IO.Stream stream = this.GetType().Assembly.GetManifestResourceStream
    				("SplashScreenInWPF." + "splashXAML.txt");

    using (StreamReader reader = new StreamReader(stream))
    {
        contents = reader.ReadToEnd();
    }

return contents;
}

确保 GetManifestResourceStream string 构造正确。第一部分必须与您项目的命名空间完全匹配,后跟一个点。第二部分是您添加到项目中的资源的精确文件名。

您将希望将启动屏的大小设置为屏幕工作区。因此,添加一个函数来完成此操作。这是您需要的代码:

public static System.Drawing.Rectangle GetScreenWorkingArea()
{
    System.Drawing.Rectangle rs = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
    Point p = new Point(rs.Width / 2, rs.Height / 2);
    rs = System.Windows.Forms.Screen.GetWorkingArea(p);
    return rs;
}

您需要在 Form1 类顶部添加一些变量来控制您的动画:

int timerInterval = 4;

bool startedApp = false;
bool endedSplash = false;

private double fadeInTime = 500;
private double holdTime = 2000;
private double fadeOutTime = 2500;
private double startAppTime = 800;

double elapsedTicks = 0;

您可以更改一些值来控制淡入和淡出的速度。

您还需要为托管 3D 内容的控件添加一些变量:

Viewport3D viewport1 = null;
ContentControl contentControl = null;

您将需要一个函数来在启动屏完成其工作后将其隐藏。这是相关代码:

private void hideSplash()
{
    timer1.Stop();

    // free resources by letting the viewport with its models 
    // and animations to get garbage collected
    contentControl.Content = null;
    viewport1 = null;

    TopMost = false;
    Visible = false;
}

此函数还会删除对 ViewPort3D 的引用,以便垃圾回收器可以释放它们。这一点很重要,因为您不希望 3D 模型的网格和动画计时器占用您应用程序可以使用的内存和 CPU 周期。

现在是时候为您的应用程序添加一个窗体了。Form1 是您的启动屏。您添加的新窗体将是您应用程序的实际用户界面。右键单击解决方案资源管理器中的项目名称,然后从菜单中选择“添加”,然后选择“Windows 窗体”。将新窗体命名为“FormAppWindow.cs”,然后单击“添加”按钮将其添加。

FormAppWindow.cs 的构造函数添加一个参数,该参数接受一个 Form1 作为参数,并使用它来设置您应用程序窗体中的一个局部变量。代码如下:

namespace SplashScreenInWPF
{
    public partial class FormAppWindow : Form
    {
         Form1 parentForm;

         public FormAppWindow(Form1 ParentForm)
         {
             InitializeComponent();
             parentForm = ParentForm;
         }
    }
}

在设计器中打开 FormAppWindow ,然后双击窗体中的空白区域以添加一个 Load 事件。在 load 事件中,调用您添加到启动屏的 static 函数,该函数获取屏幕工作区的矩形,并使用它来设置应用程序启动时的边界。代码现在应如下所示:

namespace SplashScreenInWPF
{
    public partial class FormAppWindow : Form
    {
        Form1 parentForm;

        public FormAppWindow(Form1 ParentForm)
        {
            InitializeComponent();
            parentForm = ParentForm;
        }

        private void FormAppWindow_Load(object sender, EventArgs e)
        {
            System.Drawing.Rectangle rs = Form1.GetScreenWorkingArea();
            SetBounds(0, 0, rs.Width, rs.Height);
        }
    }
}

您需要确保在关闭应用程序窗体时关闭应用程序窗体的父级,即启动屏。因此,右键单击应用程序窗体以获取属性窗口。单击属性窗口中的闪电图标,然后双击 FormClosing 事件以添加处理程序。然后添加一些代码来关闭父窗口。FormAppWindow 的代码现在将如下所示:

namespace SplashScreenInWPF
{
    public partial class FormAppWindow : Form
    {
        Form1 parentForm;

        public FormAppWindow(Form1 ParentForm)
        {
            InitializeComponent();
            parentForm = ParentForm;
        }

        private void FormAppWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            parentForm.Close();
        }

        private void FormAppWindow_Load(object sender, EventArgs e)
        {
            System.Drawing.Rectangle rs = Form1.GetScreenWorkingArea();
            SetBounds(0, 0, rs.Width, rs.Height);
        }
    }
}

您可以使用工具箱将其他控件添加到 FormAppWindow 中,使其看起来已准备好执行某些工作。添加什么控件并不重要,只需为其提供一些视觉内容即可查看和把玩。我在应用程序窗口中包含了一个菜单栏和一个文本框,该文本框可以显示创建启动屏 3D 动画的 XAML 字符串。如果您构建了本文随附的项目,您可以看到我添加的菜单和控件。我将不描述如何添加这些部分,因为它们很容易弄清楚。

现在我们准备添加使启动屏发挥作用的代码了。返回 Form1.cs 文件,并将此代码添加到加载事件中。完成后,它应如以下代码所示。您可以阅读下面的代码中的注释来理解它在做什么。基本上,它会创建托管 3D 内容所需的控件,从嵌入的资源中读取 XAML,并使用它来创建一个 ViewPort3D 来显示您的 3D 内容。

private void Form1_Load(object sender, EventArgs e)
{
    try
    {
        // get the XAML for your 3D content
        string xaml = getSplashXAML();
        
        // set the window to TopMost because it is your splash screen
        TopMost = true;
        
        CenterToScreen();
        
        Visible = true;
        
        // set the opacity to 0 because we are going to fade the splash screen in
        Opacity = 0;
        
        // set the splash screen size to the screen working area
        System.Drawing.Rectangle rs = GetScreenWorkingArea();
        SetBounds(0, 0, rs.Width, rs.Height);
        
        // add an element host control to the form to let it host WPF controls
        ElementHost elementHost = new ElementHost();
        Controls.Add(elementHost);
        
        // set the size of the elementHost
        elementHost.SetBounds(ClientRectangle.Left, ClientRectangle.Top, 
				ClientRectangle.Width, ClientRectangle.Height);
        
        // add a Grid control to host your content and make is a child of the ElementHost
        Grid grid = new Grid();
        
        grid.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
        grid.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
        
        elementHost.Child = grid;
        
        // add a ContentControl to your Grid that will host the 3D content
        contentControl = new ContentControl();
        
        grid.Children.Add(contentControl);
        
        // call the function you added earlier to get the XAML 
        // for your animated 3D content
        string content = getSplashXAML();
        
        // create a stream from the content so XamlReader can use it
        byte[] byteArray = Encoding.ASCII.GetBytes(content);
        MemoryStream stream = new MemoryStream(byteArray);
        
        // use XamlReader to create a Viewport3D object from the XAML code
        object o = XamlReader.Load(stream);
        
        contentControl.Content = o;
        
        // set your viewport to the one you read in from the resource
        if (o is Viewport3D)
        {
            viewport1 = (Viewport3D)o;
        }
        
        // start a timer that will fade in your content while it animates, 
        // then fade it out
        timer1.Interval = timerInterval;
        timer1.Start();
    }
    catch (Exception ex)
    {
        string s = ex.Message;
        MessageBox.Show(s);
    }
}

现在我们几乎完成了。只需在 Tick 事件中添加一些代码即可实现淡入淡出并显示您的应用程序。请参阅下面的代码中的注释,了解其工作步骤:

private void timer1_Tick(object sender, EventArgs e)
{
    // increment the elapsed ticks
    elapsedTicks += timerInterval;
    
    if (elapsedTicks < fadeInTime)
    {
        // for a period of time, we are fading the splash in by incrementing its opacity
        double op = 1;
        op *= elapsedTicks;
        op /= fadeInTime;
        Opacity = op;
    }
    else
    {
        // we are finished fading in
        if (elapsedTicks < holdTime)
        {
            // hold splash steady at full opacity for a specified time
            if (Opacity < 1)
            {
                Opacity = 1;
            }
            
            // start the application window when it is time
            // give its constructor this window as a parent
            if (!startedApp)
            {
                if (elapsedTicks >= startAppTime)
                {
                    startedApp = true;
                    
                    FormAppWindow app = new FormAppWindow(this);
                    app.ShowDialog();
                }
            }
        }
        else
        {
            // now we are fading out to reveal the application behind the splash
            Opacity -= ((double)1 / ((fadeOutTime - holdTime) / timerInterval));
            
            if (Opacity <= 0 && !endedSplash)
            {
                // when we are faded out all the way, hide the splash screen
                endedSplash = true;
                
                Visible = false;
                
                hideSplash();
            }
        }
    }
}

当您运行程序时,如果一切都正确完成,您将看到启动屏逐渐淡入,直到您可以看到动画 3D 模型。3D 模型中的对象在淡入时会处于运动状态。相机还将进行一次环绕,直到停在模型前面。在启动屏完全不透明之后,应用程序主屏幕将启动,然后启动屏将淡出,显示应用程序。

最后一个问题是:如何创建用于 3D 动画的 XAML 代码?即使是一个简单的场景,例如我们在这里包含的场景,也需要数千个 3D 坐标来表示数百个三角形。Visual Studio 不包含任何帮助您构建或动画 3D 三角形网格的工具。Blend 也没有帮助。我编写了一个名为 Calder4D 的程序,该程序允许您直接建模 3D 形状并对其进行动画处理,然后生成使其正常工作的 XAML 代码。本文包含的 XAML 代码文件就是使用 Calder4D 创建的,并且它与用于 Calder4D 启动屏的 XAML 相同。您无需 Calder3D 即可构建此示例,但它可以作为创建 XAML 中动画 3D 模型的工具。

如果您想尝试 Calder4D 并创建一些自己的动画 3D 场景,这是一个链接,您可以从中了解更多信息。

关于 XAML 文件的一个说明:如果您正在创建自己的 XAML 文件作为本文内容的用途,它必须有一个有效的 Viewport3D 作为其根对象,因为 XamlReader 期望找到的就是这种对象。此外,您必须在 Viewport3D 对象属性中添加以下行:

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

这些是 WPF 和 XAML 命名空间声明。您可以查看本文项目随附的 XAML 文件,以确切了解这些行在 XAML 文件中的位置。

历史

  • 2010 年 10 月 25 日:初始版本
© . All rights reserved.