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

以初学者的身份用 C# 创建 NFO 查看器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (10投票s)

2012年12月10日

CPOL

25分钟阅读

viewsIcon

48367

downloadIcon

10924

一个小型应用程序,可以将 .nfo 文本文件转换为自定义表单,并以音乐和自动滚动文本的形式显示!


我有 4 个版本,所有版本都提供源代码和仅应用程序下载。

============================================

更新到 v1.4。外观已更改,新增改进和错误修复(请参阅底部历史记录以了解更改)。
希望您喜欢这个最终版本!

引言

通过几个月的 C# 业余经验(试错法)以及 NFO 查看器的一些新更新,我再次更新了这篇文章。我运行的是 Windows 7 Ultimate x64、.NET 4.5,并且使用 Microsoft Visual Studio 2010。

这个应用程序有一个自定义的“Form”(表单)、关闭应用程序和静音音乐的功能、自定义图标、点击后会跳转到网站的图片,以及最主要的 richTextBox(富文本框)。在这个框中,我将加载 NFO 文件(文本),它将以自定义颜色显示,自动向下滚动并循环,最后一点是:您无法与文本互动。它必须像电影的片尾字幕一样显示!
通过使用本文,您可以选取任何代码片段来创建自己的查看器,或者使用源代码来创建此处提供的自定义查看器。

背景

我有很多 NFO 文件,需要设置为“用记事本打开”才能显示它们。记事本默认无法正确显示所有符号,并且无法将所有内容放置在正确的位置。最终,我得到了一些可以立即运行并具有许多自定义功能的 NFO 查看器示例。我也想要这样的功能,但无法更改这些文件。所以我决定自己创建它们!

应用程序 + 选项

由于这是新的更新,一些按钮/功能有所不同。我将保留旧选项不变,以便人们仍然可以使用它们。自 1.3 以来的更改:不再有关闭按钮 (2)(改为按 Escape 键),测试速度按钮已移除 (7),更改了速度选项(1x +,1x - 而不是两个 2x)。

图片“Argaï NFO Viewer v1.1”
图片“Argaï NFO Viewer v1.3”
图片“Argaï NFO Viewer v1.4 Final”(下图完整图片)


上述应用程序包含以下部分

  1. 主框架(Windows Form,无边框);
    我使用了一张大图片,并将 Transparency(透明度)选项设置为 White(白色),以去除实际图片周围的所有内容。
    通过一小段代码,您可以使图片拖动整个应用程序。
  2. 一个 Button(按钮)(X) 用于在运行后关闭应用程序;
  3. 一个 checkBox(复选框),当它未选中时,将暂停(静音)音乐(mod 格式 [.it/.xm/.s3m/.mod]);
  4. (左侧) 一个可点击的 pictureBox(图片框)。它将打开用户的默认网页浏览器并跳转到我的链接;
  5. 此应用程序最重要的部分:richTextBox(富文本框)!
    它打开一个指定的 .nfo 文件,在显示之前进行转换,加载所有文本并使用我编辑的自动滚动代码以特定速度持续向上滚动文本。当到达末尾时,它将返回到开头并循环滚动。我不想使用滚动条,所以我决定“隐藏”它。我还禁用了所有可能的交互。
  6. 另一个被禁用的 pictureBox。这意味着您无法与任何事件交互,但您可以点击它并拖动应用程序,就像“表单”的图像一样。

    额外
  7. 按钮/KeyPress 速度选项事件:暂停、慢速、快速 {受限}
  8. 自定义 Icon(图标)。
  9. 作为 Button(按钮)的 PictureBox(图片框)(点击事件),用于打开“关于”窗口。
  10. 背景音乐(mod 格式)与应用程序一起运行。[需要外部播放器]
  11. 附加设置 [表单名称、进程名称/描述等]
  12. 工具提示 [鼠标悬停在活动表单控件上时显示文本信息的弹出窗口]

使用代码

注意由于我在 v1.4 中做了很多更改,并非所有代码都会放在本文中。如果您想查看某些特定代码,请使用提供的源代码

让我们尝试解释如何使用我想要的特定选项来制作这些单独的部分。您可以根据自己的喜好更改任何内容,但我想以这种方式创建它,并且在互联网上很难找到。首先,我的应用程序导入了下面列出的所有内容

using System; // Default, Used for handles.
using System.Diagnostics; // To use Process.
using System.Drawing; // Default, for point locations and colors.
using System.Windows.Forms; // Default, Formrelated code (events, properties, objects).
using IrrKlang; // I added this myself (music player).
using System.Reflection; // To work with Assemblies.
using System.Runtime.InteropServices; // This is for the dll-import.
using System.IO; // Work with files and streams.
using Un4seen.Bass; // This is for the new BASS ModPlayer.

1) 表单 [主框架]

Form 属性

  • Backgroundimage(背景图片):用于创建自定义图片表单。
  • BackgroundImageLayout(背景图片布局):None(无)(使图片中的白色区域足够大,不要重复图像)。
  • DoubleBuffered(双缓冲):True(真),这可以防止应用程序在屏幕外拖动时闪烁。
  • Icon(图标):为应用程序在运行时选择图标(.ico 格式),而不是 .exe 文件本身。如果你想让白色透明,请使用一些可以删除不需要的区域并将其保存为透明图像的软件(例如 Paint.net)。我使用 http://converticon.com/ 来创建我的图像图标。
  • Size(大小):您可以在此处手动设置大小,但您可以在设计视图中通过拖动边框来更改它。
  • StartPosition(起始位置):CenterScreen(屏幕居中),这将使应用程序在屏幕中心打开。
  • Text(文本):您可以在此处设置希望出现在运行中应用程序任务栏图标上方的文本。
  • TransparencyKey(透明色键):White(白色),取决于您想要从选定图片中去除的背景颜色。

我们希望能够移动我们的程序。
为此,为表单创建两个新事件并明智地命名它们,或者双击字段并使用默认名称。在我的应用程序中,我总是使用默认名称。
在“属性”窗口中,选择闪电图标(事件)。

  • MouseDownForm1_MouseDown
  • MouseMoveForm1_MouseMove

这会创建两个新方法,用于在图片(表单)上发生这些事件时执行某些操作。将以下代码添加到表单中的方法中

// As I use this in more methods, I placed it before public Form1()
public Point mouse_offset;

// These 2 methods will make it possible to drag your application around.
private void Form1_MouseDown(object sender,
System.Windows.Forms.MouseEventArgs e)
{
    mouse_offset = new Point(-e.X, -e.Y);
}

private void Form1_MouseMove(object sender,
           System.Windows.Forms.MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        Point mousePos = Control.MousePosition;
        mousePos.Offset(mouse_offset.X, mouse_offset.Y);
        Location = mousePos;
    }
} 

曾有一个“bug”,如果用户按下方向键,他们会进入richTextBox(焦点改变),并且可以使用方向键和PageUp/Pagedown键在其中导航。我搜索并找到了可用的代码来禁用此功能。我还在此处添加了Escape键功能,因为Escape是命令键。现在您可以在应用程序处于活动状态时按下Escape键关闭应用程序。
Form_Load( ) 事件中,添加

foreach (Control control in this.Controls)
{
    control.PreviewKeyDown += new PreviewKeyDownEventHandler(control_PreviewKeyDown);
}

在此事件之外,添加一个具有以下代码的方法

        void control_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {   // This blocks arrow/directional keys.
            if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down || e.KeyCode == Keys.Left || e.KeyCode == Keys.Right)
            { e.IsInputKey = true; }
            // Here I added the function to close app with Escape.
            if (e.KeyCode == Keys.Escape)
            { this.Close(); }
        }

添加此代码后,您将无法使用箭头键从 Form 切换到 richTextBox

我还添加了应用程序关闭时的音乐和应用程序的渐变效果
当应用程序收到需要关闭的消息时,会触发此效果。
以下代码处理外观效果,并同时使音乐渐弱。我使用计时器以不同的速度渐弱。您可以根据需要组合和更改速度,随心所欲。

        // CLOSE FORM [FADE APPLICATION/MUSIC]
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (this.Opacity > 0.01f)
            {
                e.Cancel = true;
                Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_GVOL_MUSIC, 7000);
                AppFadeTimer.Interval = 33;
                VolFadeTimer.Interval = 10;
                AppFadeTimer.Enabled = true;
                VolFadeTimer.Enabled = true;
            }
            else
            { AppFadeTimer.Enabled = false; VolFadeTimer.Enabled = false; }
        }
        private void AppFadeTimer_Tick(object sender, EventArgs e)
        {
            if (this.Opacity > 0.01)
            { this.Opacity = this.Opacity - 0.01f; }
            else
            {
                this.Close();
            }
        }
        private void VolFadeTimer_Tick(object sender, EventArgs e)
        { vol = vol - 26; Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_GVOL_MUSIC, vol);}

由于我制作了不同的应用程序设计和版本,这可能大相径庭,但它确实有效。

2) 按钮 [关闭应用程序]

Button 属性

  • Text(文本):将其更改为“关闭我”之类的文字。
  • Cursor(光标):我将其更改为 Hand(手形),这样可以更好地看出它是可点击的。
  • BackColor(背景色)和 ForeColor(前景色):更改按钮和文本的颜色样式。
  • Size(大小)和 Location(位置):在设计模式下拖动和调整大小,或输入数字。

我们希望能够通过一个X按钮关闭我们的程序
在设计模式下,从工具箱中添加一个 Button,将属性 Text 更改为“X”或“关闭”,并根据您的喜好调整大小。添加事件 Click:“button1_Click”。这将为该事件创建一个方法,以便在用户按下按钮时执行某些操作。

使用以下代码关闭应用程序

// This will close the application when the user presses the X button.
private void button1_Click(object sender, System.EventArgs e)
{
    this.Close();        
}

3) 复选框 [暂停音乐]

checkBox 属性

  • BackColor(背景色):我将此设置为Transparent(透明),以便只看到框,并且文本没有白色背景。
  • Checked(选中):我希望我的音乐默认开启,所以当应用程序启动时此项为True
  • CheckState(选中状态):我希望我的框中有一个复选标记,您也可以禁用该框(灰色),但这里是活动的:Checked
  • Cursor(光标):Hand(手形)。
  • ForeColor(前景色):在我的示例中为 LawnGreen(草绿色),或者将其更改为您想要的任何颜色。
  • Text(文本):输入将显示在 checkBox 旁边的文本,在我的例子中是:Music?(音乐?)
  • Size(大小)和 Location(位置):在设计模式下拖动和调整大小,或输入数字。

我们希望能够通过 checkBox 静音(暂停)音乐
在设计模式下,将一个 checkBox 添加到表单中。根据上面的示例或您自己的喜好更改属性。现在我们来设置 checkBox 的事件。我们模拟用户取消选中,以禁用音乐。添加事件 CheckChanged:checkBox2_CheckedChange。现在您有了此事件的方法。
添加代码以暂停或停止音乐。在我的例子中,我使用irrKlang modplayer BASS 用于播放 mod 音乐文件。要了解我是如何实现的,请参阅10) 音乐(.it 格式)要暂停此音乐,请在irrKlangBASS,在 checkBox2_CheckChange 方法中添加以下代码

// Old version irrKlang.
private void checkBox2_CheckedChange(object sender, EventArgs e)
{
    CheckState state = checkBox2.CheckState; // Get the current state of the check.
    if (state == CheckState.Checked) // If checked after change;
    {
        engine.SetAllSoundsPaused(false); // Do nothing (if already playing) or continue music.
    }
    else // If changed to 'Unchecked'
    {
        engine.SetAllSoundsPaused(true); // Pause the music.
    }
}

// New Version (Bass).
private void checkBox2_CheckedChange(object sender, EventArgs e)
{
    CheckState state = checkBox2.CheckState;
    if (state == CheckState.Checked)
    { Bass.BASS_Start(); }
    else
    { Bass.BASS_Pause(); }
}

4) 图片框 [可点击]

pictureBox 属性

  • BackgroundImage(背景图片):选择您要显示的图像。
  • BorderStyle(边框样式):我将其更改为 FixedSingle;将深色图像与黑色 richTextBox 背景分开。
  • Cursor(光标):我将其更改为 AppStarting(只是为了好玩,表示可以点击)。
  • Size(大小)和 Location(位置):在设计模式下拖动和调整大小,或输入数字。

我们希望能够通过点击图像访问网站。此外,不要选择 Internet Explorer,而是选择客户端系统的默认浏览器。在设计模式下,将一个 pictureBox 添加到表单中。根据上面的示例或您自己的喜好更改属性。现在我们从属性的事件窗口添加事件:Click,例如 pictureBox1_Click。

在我们刚刚创建的方法中添加

private void pictureBox1_Click(object sender, EventArgs e)
{
    Process myProcess = new Process();
    {
    myProcess.StartInfo.UseShellExecute = true; // This runs from command line (default browser).
    myProcess.StartInfo.FileName = "http://tehparadox.com/forum/f73/arga-prophecy-2001-dutch-2631589/";
    myProcess.Start(); // Use any valid link you want above.
    }
}

PictureBox 或其他控件上放置 ToolTip(工具提示)也很好,
这样人们就知道它会做什么或去哪里。
在上方和主表单构造函数中添加

        System.Windows.Forms.ToolTip ToolTip1 = new System.Windows.Forms.ToolTip();

        public Form1()
        {
            // Info ToolTips for Some Buttons.
            ToolTip1.InitialDelay = 1200;
            ToolTip1.SetToolTip(pictureBox1, "Go to: Dutch Argaï Project Forum!");
            ToolTip1.SetToolTip(buttonQ, "Info");
        }

5) 富文本框 [自动滚动锁定的 NFO 查看器]

richTextBox 属性

  • BackColor(背景色):在我的示例中设置为 MenuText(黑色)。
  • BorderStyle(边框样式):None(无),因为我想在我的滚动中裁剪掉任何边缘和滚动条。
  • Cursor(光标):默认是 IBeam(I 形光标),但我不想选择文本!设置为 Arrow(箭头)或 Default(默认)(指针)。
  • DetectUrls(检测URL):False(假),我只想显示文本,而且我的 ASCII 艺术中包含无效的“链接”......
  • Font(字体):如果你使用 ASCii 艺术,这个很重要,只有少数字体支持。我为我的应用程序选择了 Courier New
  • ForeColor(前景色):在我的示例中为 LawnGreen(草绿色),或者将其更改为您想要的任何颜色。
  • ReadOnly(只读):True(真),我不想以任何方式更改显示的文本。
  • ScrollBars(滚动条):我希望将其设置为 None(无),但为了让我的滚动功能正常工作,它需要滚动条,所以我将其设置为 Both(两者)。
  • Size(大小)和 Location(位置):在设计模式下拖动和调整大小,或输入数字。

我们希望能够在 richTextBox正确查看 .nfo 文件。在设计模式下,将一个 richTextBox 添加到表单中。根据上面的示例或您自己的喜好更改属性。如果您还没有 FormLoad 事件,请创建一个(例如 Form1_Load)。当 Form 和所有组件加载完毕后,我们想检查 .nfo 文件是否存在。如果存在,则将其转换为正确的格式并显示,否则在 richTextBox 中显示文本“NFO not found”。
最终代码(这只显示框中的文本,尚未滚动)

private void Form1_Load(object sender, EventArgs e)
{
    // You can set 'file_name' anything you like.
    string file_name = "C:\\Test\\DARTY.nfo"; // Set the filepath.

        if (System.IO.File.Exists(file_name) == true) // Check for existence.
        {
            // Encode the file and put the endresult in our richTextBox.
            System.Text.Encoding encoding = System.Text.Encoding.GetEncoding(437);
            System.IO.StreamReader file = new System.IO.StreamReader(file_name, encoding);
            richTextBox1.Text = file.ReadToEnd();
            file.Close();
        }
        else // If it doesn't exist/not found... 
        {
            // Put this text in the richTextbox instead.
            richTextBox1.Text = "File not Found! :( --- Where is DARTY.nfo??";
        }
} 

或者我们可以使用 .nfo 文件作为资源,在这种情况下会更好。
为此,打开项目属性并选择“资源”选项卡。添加一个现有文件(我们的 .nfo 文件)
并查看属性。我们需要将 FileType 更改为 Text,然后会出现另一个选项
Encoding(编码)。将其设置为 OEM United States - Codepage 437。如果这些选项不可用,请尝试将视图更改为“查看详细信息”而不是列表视图缩略图视图(就像在计算机上一样)。
设置完成后,我们就不再需要检查文件是否存在,也不再需要读取和编码文件了。
这将留下以下简短代码作为最终结果

string file_name = Properties.Resources.Eng_Dut_01; // Resource doesn't require file extension.
richTextBox1.Text = file_name; // Put the file in the box.

变量“file_name”将指向我们添加的资源,richTextBoxText 将填充资源数据。我们不再需要前一部分中的代码行来处理文件了!

我们希望能够禁用与 richTextBox 的所有交互,而无需禁用框本身。禁用 richTextBox 几乎是正确的,但它会阻止我们选择自定义颜色!
我创建了一个新的 Form,其大小和位置与 richTextBox 相同。不透明度为 1%,因此您看不到它,并且它也会随应用程序一起移动。这阻止了与我们的 richTextBox 的任何交互,正如我们所希望的那样!
如果您想查看代码,请使用我的源代码 (v1.3)。

在 v1.4 中,我移除了停留在所有其他 Windows 应用程序顶部的 Shield(Form),并用阻止鼠标点击代替。它不适用于点击事件,但我在线找到了非常有效的代码示例

        // DISABLE RIGHTCLICK + MIDDLEBUTTON ON RICHTEXTBOX.
        public bool PreFilterMessage(ref Message m)
        {
            // Filter out WM_NCRBUTTONDOWN/UP/DBLCLK
            if (m.Msg == 0xA4 || m.Msg == 0xA5 || m.Msg == 0xA6) return true;
            // Filter out WM_RBUTTONDOWN/UP/DBLCLK
            if (m.Msg == 0x204 || m.Msg == 0x205 || m.Msg == 0x206 || m.Msg == 0x207 || m.Msg == 0x208 || m.Msg == 0x209) return true;
            return false;
        }

请注意,这适用于整个活动 Form。但由于我的 Form 只接受左键点击,我不在乎阻止其他鼠标按钮。

我们希望能够自动滚动 richTextBox 并循环它,以像电影片尾字幕一样查看所有文本。
准备好,因为这段代码一点都不容易。你没有一个选项可以简单地以指定的速度自动滚动一个框。但是有一个代码选项可以逐行向上滚动,但这不容易阅读。这样想:我们需要一个来自 dll 的消息服务,来告诉 richTextBox 每隔几毫秒向上移动一个像素。因此,我们设置任务(向上移动),并必须创建一个计时器,每隔我们之前设置的几毫秒发送一次任务作为消息。

我没有理解所有的代码,但它奏效了!请参阅底部“参考文献”部分,了解我从中获取此项目的项目。我唯一更改的是滚动速度 + 调整选项,当它到达末尾时会返回到开头(循环),并且我在文本更新到新位置之前更改了颜色。这很难,因为我需要将其与不同的滚动速度同步。

导入 DLL,为代码创建所有引用

以下代码(除了第一行 'using')放置在 public partial classpublic Form1( ) 之间

// Paste this line on top of your code. 
using System.Runtime.InteropServices;
 
// The rest.
public partial class Form1 : Form
{
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);

    [DllImport("user32.dll")]
    static extern int SetScrollInfo(IntPtr hwnd, int fnBar, [In] ref SCROLLINFO lpsi, bool fRedraw);

    [DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage")]
    static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    struct SCROLLINFO // These are all required.
    {
        public uint cbSize;
        public uint fMask;
        public int nMin;
        public int nMax;
        public uint nPage;
        public int nPos;
        public int nTrackPos;
    }

    enum ScrollBarDirection
    {
        SB_HORZ = 0,
        SB_VERT = 1,
        SB_CTL = 2,
        SB_BOTH = 3
    }
 
    enum ScrollInfoMask
    {
        SIF_RANGE = 0x1,
        SIF_PAGE = 0x2,
        SIF_POS = 0x4,
        SIF_DISABLENOSCROLL = 0x8,
        SIF_TRACKPOS = 0x10,
        SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS
    }

    const int WM_VSCROLL = 277;
    const int SB_LINEUP = 0;
    const int SB_LINEDOWN = 1;
    const int SB_THUMBPOSITION = 4;
    const int SB_THUMBTRACK = 5;
    const int SB_TOP = 6;
    const int SB_BOTTOM = 7;
    const int SB_ENDSCROLL = 8;

    private Timer t = new Timer(); // Create an instance of our Timer.

设置速度并启用计时器以发送消息。

将此代码添加到 public Form1( )

public Form1()
{
    InitializeComponent();

    // Extra Code. Interval creates the speed, Event sends it.
    t.Interval = 40;
    t.Tick += new EventHandler(t_Tick);
    t.Enabled = true; // We want the Timer to run this instantly, so enable.
}

快完成了,自动滚动的最后一点代码!创建 t_Tick( )scroll( ) 这两个方法来运行进程。我添加了额外的代码来在滚动时更改文本。这在 t_Tick( ) 中,它通过获取计时器的间隔来检查当前的滚动速度,可以通过按 + 和 - 进行调整。
根据间隔,我执行正确的颜色更改速度代码。
我添加了一个方法 FastScrollpeed( )(最少的代码)作为示例,其余请参阅源代码。
根据间隔,颜色需要每个像素向上更新一次,或者每隔几个像素向上更新一次(在快速情况下)。这就是为什么我使用 int i 来检查像素号并在结束时重置以循环颜色模式。

       // Timer for scroll + change richTextBox1 color at each interval.
        int i = 1;
        string ScrollEnabled = "true"; // If user presses P (pausing scroll) then this is false. 
        void t_Tick(object sender, EventArgs e) // GetCurrentInterval and call the right color speed change.
        {
            if (t.Interval == 80)
            { SlowScrollSpeed(); }
            else if (t.Interval == 40)
            { CenterScrollSpeed(); }
            else if (t.Interval == 10)
            { FastScrollSpeed(); }

            if (ScrollEnabled == "true") { scroll(richTextBox1.Handle, 1); } else { scroll(richTextBox1.Handle, 0); }
        }

        void FastScrollSpeed() // Change color each 6 lines.
        {
            if (i >= 1 && i <= 6) { richTextBox1.ForeColor = Color.OrangeRed; i = i + 1; }
            else if (i >= 7 && i <= 12) { richTextBox1.ForeColor = Color.DarkOrange; i = i + 1; }
            else if (i >= 13 && i <= 18) { richTextBox1.ForeColor = Color.Yellow; i = i + 1; }
            else if (i >= 19 && i <= 24) { richTextBox1.ForeColor = Color.LawnGreen; i = i + 1; }
            else if (i >= 25 && i <= 30) { richTextBox1.ForeColor = Color.Turquoise; i = i + 1; }
            else if (i >= 31 && i <= 36) { richTextBox1.ForeColor = Color.DodgerBlue; i = i + 1; }
            else if (i >= 37 && i <= 42) { richTextBox1.ForeColor = Color.RoyalBlue; i = i + 1; }
            else if (i >= 43 && i <= 48) { richTextBox1.ForeColor = Color.DarkViolet; i = i + 1; }
            else if (i >= 49 && i <= 53) { richTextBox1.ForeColor = Color.HotPink; i = i + 1; }
            else if (i == 54) { richTextBox1.ForeColor = Color.HotPink; i = 1; }
        }

// Scrolls a textbox. handle: handle to our textbox. pixels: number of pixels to scroll.
void scroll(IntPtr handle, int pixels)
{
    IntPtr ptrLparam = new IntPtr(0);
    IntPtr ptrWparam;
    // Get current scroller posion

    SCROLLINFO si = new SCROLLINFO();
    si.cbSize = (uint)Marshal.SizeOf(si);
    si.fMask = (uint)ScrollInfoMask.SIF_ALL;
    GetScrollInfo(handle, (int)ScrollBarDirection.SB_VERT, ref si);

    // Increase posion by pixles
    if (si.nPos < (si.nMax - si.nPage))
        si.nPos += pixels;
    else // No more pixels left...
    {
        ptrWparam = new IntPtr(SB_ENDSCROLL); // End reached.
        t.Enabled = false; // Disable the Timer.
        SendMessage(handle, WM_VSCROLL, ptrWparam, ptrLparam);
    }

    // Reposition scroller
    SetScrollInfo(handle, (int)ScrollBarDirection.SB_VERT, ref si, true);

    ptrWparam = new IntPtr(SB_THUMBTRACK + 0x10000 * si.nPos);
    SendMessage(handle, WM_VSCROLL, ptrWparam, ptrLparam);

    // I added this to loop the scrolling.
    if (t.Enabled == false) // If Timer is disabled...
    {
        // Send scroller back to the top.
        SendMessage(handle, WM_VSCROLL, (IntPtr)SB_TOP, IntPtr.Zero);
        t.Enabled = true; // Enable timer again.
    }
} 

既然我们无法与 richTextBox 交互,那么如果我们可以通过拖动 richTextBox移动应用程序就太好了。我在线找到了一些代码,可以实现通过拖动其上的控件来移动整个 Form

        // These Events help to drag the Whole Form by dragging our richTextBox.
        private void richTextBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                dragging = true;
                pointClicked = new Point(e.X, e.Y);
            }
            else
            { dragging = false; }
            pictureBox1.Focus();
        }
        private void richTextBox1_MouseUp(object sender, MouseEventArgs e)
        {
            dragging = false;
            pictureBox1.Focus();
        }
        private void richTextBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (dragging)
            {
                Point pointMoveTo;
                pointMoveTo = this.PointToScreen(new Point(e.X, e.Y));
                pointMoveTo.Offset(-pointClicked.X, -pointClicked.Y);
                this.Location = pointMoveTo;
            }  
        }
// I've used Focus() method to prevent entering the richTextbox using certain ClickEvents.

应该是这样了,您应该会有一个自动滚动和循环文本,该文本是从 .nfo 文本文件(也在资源中)加载、转换并在 richTextBox 中显示的,可选的颜色更改代码。您还可以通过拖动此控件来移动 Form

6) 另一个图片框 [已禁用]

请参阅4) 图片框 [可点击],但不要添加任何(点击)事件并将属性 Enabled 设置为 False。这样就可以了,无需任何代码,您可以拖动任何禁用的对象。

7) 按钮/KeyPress 速度选项 [暂停,慢速,快速] {受限}

7.1) BUTTONS(按钮):添加您想要的按钮,将 Text 更改为 P、- 和 + 以实现相应功能。对于每个 Button:添加 Click 事件。在这些方法中,我们放置创建新功能的动作。我创建了这些:buttonp (PAUSE)、buttonmin (SLOWER) 和 buttonplus (FASTER)。
请注意 t.Interval,我们在 “设置速度并启用计时器以发送消息” 中设置了它
来自 5) RICHTEXTBOX。我用作默认速度的设置是 t.Interval = 40;

// The Interval needs to be smaller to send it's messages faster (faster speed).
private void buttonplus_Click(object sender, EventArgs e)
{
    if (t.Interval > 10) // Not highest Speed.
    {
        t.Interval = t.Interval - 15; // Increase Speed with 15ms.
        if (t.Interval <= 10) // When this reaches highest speed after calculation...
        { buttonplus.Enabled = false; } // Disable the speedbutton.
        buttonmin.Enabled = true; // Enable the opposite speedbutton.
    }
}

private void buttonmin_Click(object sender, EventArgs e)
{
    if (t.Interval < 70)
    {
        t.Interval = t.Interval + 15;
        if (t.Interval >= 70)
        { buttonmin.Enabled = false; }
        buttonplus.Enabled = true;
    }
}

private void buttonp_Click(object sender, EventArgs e)
{
    if (t.Enabled == true) // First check if timer is running.
    {
        t.Enabled = false; // If so, disable it.
    }
    else // (t.Enabled == false)
    {
        t.Enabled = true; // Otherwise do the opposite.
    }
}
//If you can count, you'll see you can push both the + & - 2x if you have the default speed[40].

7.2) KEYPRESS 事件

确定您希望能够在何处按下键盘键。我决定我的表单是最好的选择,但焦点并不在那里。我使用了以下步骤

  • 我将 Form 属性 KeyPreview 设置为 True
  • 我将所有 ButtonsTabIndex 更改为更高值;
  • 在 Form1.Designer.cs 中,我将 this.Focus(); 添加到 Form,以确保万无一失。

然后,是 KeyPress 事件的时间。转到“表单属性”并选择“事件”选项。添加 KeyPress。对于这一个方法,我们可以添加我们想要的任何键盘按键。

如果您想要带有按钮选项的旧代码,请查看 v1.1 源代码发布。
在 v1.4 中我使用了以下情况

        // KEYBOARD KEYS THAT PROVIDE FORM-FUNCTIONS FOR USERS.
        // Note you can't use 'command' keys, just input keys that actually print to screen.
        private void Form1_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 'm') // Toggle Music.
            {
                if (checkBox2.CheckState == CheckState.Checked)
                { checkBox2.CheckState = CheckState.Unchecked; }
                else // Unchecked state.
                { checkBox2.CheckState = CheckState.Checked; }
            }
            if (e.KeyChar == 'p') // Pause Scroll.
            {
                if (ScrollEnabled == "true")
                { ScrollEnabled = "false";}
                else { ScrollEnabled = "true"; }
            }
            if (e.KeyChar == '=' || e.KeyChar == '+') // Speed up Scroll, Values are fixed!
            {   if (t.Interval == 40)
                { t.Interval = t.Interval - 30; }
                else if (t.Interval == 80)
                { t.Interval = t.Interval - 40; } 
            }
            if (e.KeyChar == '-') // Slow down Scroll.
            {   if (t.Interval == 40) { t.Interval = t.Interval + 40; }
                else if (t.Interval == 10) { t.Interval = t.Interval + 30; }   }
        } 

8) 自定义图标(无代码)

请参阅 1) 表单 [主框架]Form 属性。在此部分,您可以为应用程序在运行状态下添加图标。

EXE 文件本身仍然有其默认的窗口图标。要将其更改为相同/不同的图标,请打开“解决方案资源管理器”。右键单击项目名称(几乎在顶部,以粗体显示)并打开“属性”。如果选项卡未设置为应用程序,请选择该选项卡。您会找到“图标和清单”选项,您可以在其中浏览相同的 .ico 文件。保存后,您的 .exe 文件也将包含您选择的图标。

9) “关于”窗口

当您点击相应的问号 PictureBox 时打开的新 Form
当您将鼠标悬停在框上时,问号会发光。我将这两张图片添加为资源。
下一段代码展示了问号如何工作

        // QUESTION-MARK BUTTON ['About' POPUP]
        void buttonQ_MouseEnter(object sender, EventArgs e)
        { this.buttonQ.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.QMarkNeonGlow)); }

        void buttonQ_MouseLeave(object sender, EventArgs e)
        { this.buttonQ.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.QMarkNeonLessGlow)); }

        private void buttonQ_Click(object sender, EventArgs e)
        {
            f2.TopMost = false; // DisAble SHIELD ON TOP
            Form f3 = new Form3(); // CREATE 'About' POPUP
            f3.ShowDialog(); // BLOCK INTERACTION WITH APPLICATION untill popup closed.
        }

通过使用 MouseEnterMouseLeave 事件,您可以在鼠标悬停在 PictureBox 上时更改图片的外观。我必须禁用护盾的 TopMost,否则它会阻止用户关闭弹出窗口。当用户点击 richTextBox 时,我确保护盾(f2)的 TopMost 被激活。这不是最好的解决方案,但我目前对此很满意。要查看完整的设置,源代码是您的朋友!

10) 音乐 (mod 格式)

我希望音乐文件小巧(至少与 mp3 相比),所以我选择了 mod 格式 [.it/.xm/.s3m/.mod]。这些文件也可能很大,但我只下载小于 1MB 的曲目,以保持我的应用程序体积小巧。没有简单的方法可以编写播放音乐的代码,除非你想使用 WMP 播放 .mp3/.wav 文件。我选择了irrKlang modplayer BASS 来播放我的 mod 音乐文件。[更多信息,请点击 mod 播放器链接] 独自找出如何实现这一点非常困难,但我成功了。以下是我为 irrKlang 所做的步骤,以供参考:

在解压的 irrKlang 文件夹中

  • 将“Include”文件夹复制到系统上的项目文件夹。
  • 将正确的 dotNET 版本 dll 复制到您的项目文件夹(在我的情况下:irrKlang-1.4.0\bin\dotnet-4\irrKlang.NET4.dll,如果您想播放 MP3 文件,还需要 ikpFlac.dllikpMP3.dll)。
  • 将此文件夹“irrKlang-1.4.0\lib\Win32-visualStudio”中的 .exp 和 .lib 文件复制到您的项目中。

在您的 Visual Studio 项目中,执行以下操作:在“解决方案资源管理器”中,右键单击“引用”并选择添加引用... 选择浏览选项卡,搜索并选择您的 .NET dll(在我的情况下是 irrKlang.NET4.dll)并添加它。

将此行添加到项目的顶部

using IrrKlang;

然后我们可以开始编写实际加载和播放 mod 音乐文件的代码了。请参阅 5) RICHTEXTBOX 的第一个要点(正确查看 .nfo 文件)。我们已经在 Form1_Load( ) 方法中读取和转换 .nfo 文件,并在 Form 加载时显示它。当应用程序启动时,我们也会对音乐做同样的事情。将以下行粘贴在 Form1_Load( ) 方法之前

IrrKlang.ISoundEngine engine = new IrrKlang.ISoundEngine();

我之所以在 Form 加载之前使用它,是因为我需要在更多方法中使用这个变量(也为了暂停它)!在 Form1_Load( ) 方法中,我们添加以下行

engine.Play2D("C:\\Test\\Xaser-Aeolus.it", true);

这意味着我们在计算机上播放一个文件,因此只要我无法将其包含在项目中,您就必须将其与应用程序一起发送,并确保它复制/解压到正确的文件夹。否则应用程序将运行,但没有音乐,并且可能没有来自同一文件夹的 NFO 文件。

我为 BASS 所做的步骤
我现在可以从资源播放我的音乐了,这更简单,并且始终有效。
下载 BASS 和 Bass.Net dll,并将文件添加到项目中,或者从我的源代码中复制。在解决方案资源管理器中,转到引用并添加 Bass.Net.dll 作为引用。除非您将它们添加到资源中,否则您必须将它们与最终的 .exe 文件一起放置。添加 dll 并创建引用后,在顶部添加以下行

using Un4seen.Bass;

我的项目中有个名为“Resources”的目录,右键单击 -> 添加了我的 mod 音乐文件。设置其属性:Build ActionEmbedded Resource
使用 Form1_Load( ) 中的以下代码,我可以在启动时使用 BASS 从资源播放该曲目

// LOAD MUSIC
BassNet.Registration("xxxxxxxx@xxxxxxxx.xx", "XXXXXXXXXXXXXXX"); // Source activated.
Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);
// "Projectname.(OptionalFolder.)ResourceFileName.Extension"
using (var manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("WindowsFormsApplication1.Resources.Saga Musix - Heaven.it"))
// use //string[] names = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames(); in debug to test the proper path to resource.
{
    if (manifestResourceStream != null)
    {
       int length = (int)manifestResourceStream.Length;
       byte[] buffer = new byte[length];
       manifestResourceStream.Read(buffer, 0, length);

       int load = Bass.BASS_MusicLoad(buffer, 0, length, BASSFlag.BASS_SAMPLE_LOOP, 0);

       Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_GVOL_MUSIC, vol);
       Bass.BASS_ChannelPlay(load, false);
    }
}

请参阅 3) 复选框 [暂停音乐],了解如何暂停正在播放的 mod 音乐并继续播放。

11) 附加设置

我在这里放置了为了让它在计算机其他地方看起来更好而更改的内容。

默认 .exe 文件名:我们进入解决方案资源管理器,右键单击我们的项目,然后选择属性。选择“应用程序”选项卡,并将程序集名称更改为您希望 .exe 使用的名称。
由于我处于 Beta 阶段,这并不重要,我将其更改为角色名称以查看差异(“Angel”)。
如果您设置此项,则在调试(导出)默认名称为“TestApplication.exe”的 .exe 文件后,无需手动更改 .exe 的名称。

进程描述:我注意到当我的程序运行时,任务管理器中的描述是WindowsFormsApplication1?这不是我想要的,我想要类似(Argaï) NFO Viewer!要更改进程描述,请转到解决方案资源管理器,在您的项目名称下您会看到“属性”目录。在该目录中,右键单击AssemblyInfo.cs并点击“查看代码”。[assembly: AssemblyTitle("WindowsFormsApplication1")],将Title更改为您想要的,当您的进程运行时,它将显示该Title作为描述!

任务栏和任务管理器外观:如前所述,更改 FormText 属性将创建“程序名称”,该名称在您将鼠标悬停在任务栏上运行的图标时出现。更改的程序集名称将显示在任务管理器中作为进程的 .exe 名称。

关注点

在短短几周内我创造了这个。基本的东西很容易做,但要让我的 richTextBox 自动滚动以及我想要的方式(循环)很难,即使是随之改变颜色。但更难的是在不禁用 richTextBox 本身的情况下禁用所有与它的交互。我通过试错法工作,并且仍在这样做。使用网上一些人的代码样本来实现这一点真的很难,因为我总是遇到“缺少引用”等错误......我很高兴现在这一切都正常工作了!

历史

首次上传后的更改

更新 1.1

一般更改

- 程序集名称已更改;
- 进程描述已更改 [请参阅 11) 附加设置];
- 控件的 Tabindex 已更改;
- 为 Form 添加了焦点(用于通用 KeyPress 选项);

功能

- 为自动滚动速度选项添加了 3 个 buttons [请参阅 7) 按钮/键盘按键];
- 添加了 3 个 KeyPress 选项,与按钮功能相同 [请参阅 7) 按钮/键盘按键];
我更喜欢按键,但必须先用按钮测试并修复焦点等...

更新 1.2

功能

- 将 .NFO 文本文件作为资源添加到项目中,这样人们就不再需要将其解压到特定位置。我仍然尝试将音乐和 dll 文件也作为资源添加。(请参阅 5) RICHTEXTBOX“使用 .nfo 文件作为资源” 部分);

错误修复

- 按下箭头键将不再允许您进入 richTextBox。(请参阅 1) 表单);

更新 1.3

一般更改

- 图标变得更圆(使用圆形而不是手动去除圆形周围的所有内容);
- 为了便于参考,更好地记录和整理了源代码;

功能

- 将 ModPlayer“irrKlang”更改为“BASS”,它支持以前无法工作的所有音轨,并且使用更轻量级的 DLL,这使得我的应用程序更容易在更多系统上运行(请参阅 10) 音乐(mod 格式)中的 “我为 BASS 所做的步骤”);
- 移除了关闭按钮并添加了更多 KeyPresses(按 Escape 键关闭应用程序)(请参阅 1) 表单中的 移动);
- 添加了关闭应用程序时的渐变效果,这也会使音乐随之渐弱,以创建平滑的关闭(请参阅 1) 表单中的 音乐和应用程序的渐变效果);
- 音乐现在是资源!不再需要全部放到 C:\Test 了,它在任何地方都能运行和工作(请参阅 10) 音乐(mod 格式)中的 “我为 BASS 所做的步骤”);
- 更改了默认启动位置,现在应用程序总是在屏幕中心启动(请参阅 1) 表单属性);
- 添加了一个问号按钮,可以打开一个简单的“关于窗口”。它显示应用程序的选项。当此窗口处于活动状态时,您无法与应用程序本身交互。如果您关闭应用程序,问号按钮将在渐变时禁用(请参阅 9) “关于”窗口);
- richTextBox 现在具有改变颜色的功能。即使您改变滚动速度或暂停滚动,它也保持相同的速度。我添加了一个粗略的强颜色模式(请参阅 5) RICHTEXTBOX创建 t_Tick( )scroll( ) 这两个方法);
- 最后,我将两个 DLL 都作为资源添加了。一个可以在内部读取,另一个必须写入磁盘。现在您可以在任何地方运行我的应用程序,而无需随附 DLL(请参阅我的源代码和本文底部“参考文献”中关于 DLL 的链接);

错误修复

- 添加了一个新的表单作为“防护罩”,防止与 richTextBox 交互。现在 v1.1/1.2 的点击 bug 已解决。然而,它会使应用程序失去焦点,因此在您再次点击应用程序之前 KeyPresses 不会起作用。即使我的应用程序在后台,防护罩也始终保持在您运行的任何其他应用程序之上!如果有其他解决方案,我会研究一下;
- 拖动屏幕外时应用程序闪烁的问题已解决!(请参阅 1) 表单属性);
- “跳行 Bug”已修复,现在文本在更新颜色时(用户暂停滚动条时)保持在相同位置,而不是跳到最后一行;

更新 1.4

一般更改

- 为了便于参考,更好地记录和整理了源代码;
- 更改了项目名称,所以我的所有不同设计现在也有不同的名称!

功能
- 添加了 KeyPress '+',因此数字键和字母键 '+' 都可用。(请参阅 7.2) KEYPRESS 事件
- 添加了 KeyPress 'M',以便在应用程序活动时通过键盘按键静音音乐。(同上
- 在图片上添加了工具提示,用于显示点击后将访问的网站信息,并在问号按钮上显示信息。(请参阅 4) 图片框 [可点击]PictureBox 上放置工具提示
- 添加了通过拖动 richTextBox 来移动 Form 的功能。(请参阅 5) RICHTEXTBOX通过拖动 richTextBox 移动应用程序);
- 更新了我的关于窗口[带有主窗体中使用的功能的新窗体,请参阅源代码获取代码]
+ 现在有一个带有文本的动画 GIF,自定义颜色边框,通过拖动移动的能力,自定义关闭按钮,具有与问号按钮相同的鼠标悬停发光效果。
+ 窗口不再置顶(已移除 ShowDialog()),可以打开一次,并且您仍然可以与主窗体交互。
+ 由于我在“关于”窗口打开时使应用程序可交互,
我添加了在激活主窗口的关闭按钮时关闭“关于”窗口的代码。
+ 由于它具有自定义边框,应用程序将不再“挂起”,就像您点击并按住正常的 Windows 窗体边框时那样。
+ 由于 ShowDialog 被禁用,CenterParent 作为 StartLocation 不再起作用。
因此我添加了代码来手动创建新的 CenterParent StartLocation 函数。
+ 关于窗口中新增了我的数字爱好论坛链接;
+ 关闭时“关于窗口”淡出。
错误修复
- 移除了阻挡层,并用阻挡鼠标点击和强制焦点代替。现在即使应用程序在后台,它也不会置顶!(请参阅 5) RICHTEXTBOX禁用与 richTextBox 的所有交互);


文件

添加了第一个结果 [页面顶部];
添加了源代码 [页面顶部];
更新 1.1
- 文章中的图片已更新;
- 添加了 v1.1 测试应用程序 [页面顶部];
- 添加了 v1.1 源代码 [页面顶部];
更新 1.3
- 文章中的图片已更新;
- 添加了 v1.3 测试应用程序 [页面顶部];
- 添加了 v1.3 源代码 [页面顶部];
更新 1.4
- 文章中的图片已更新;
- 添加了 v1.4 测试应用程序 [页面顶部];
- 添加了 v1.4 源代码 [页面顶部];
[更新图片后,我添加了指向旧图片的链接以供参考]

Bug

- Windows 8(总是?)包含 checkBox 和/或 Close Button 的空背景。
- 当应用程序(部分)渲染到屏幕外时,如果它不适合屏幕分辨率,则会被剪切。有没有办法/选项来解决这个问题?

参考文献

自定义 Form(无任何 Border 和默认 Buttons)的原始代码链接:
https://codeproject.org.cn/Articles/8056/Creating-Custom-Shaped-Windows-Forms-in-NET

我使用并修改的原始自动滚动代码链接
https://codeproject.org.cn/Articles/23363/Scrolling-a-Rich-Textbox-Automatically.
我关于“如何将 .nfo 用作资源”问题的链接
https://codeproject.org.cn/Questions/519140/Usingplusaplus-NFO-plustextplusfileplusasplusresou
我部分实现的“添加 DLL 作为资源”链接
https://codeproject.org.cn/Articles/528178/Load-DLL-From-Embedded-Resource

在 C# 中作为初学者创建 NFO 查看器 - CodeProject - 代码之家
© . All rights reserved.