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

一个更好的类向导

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.44/5 (11投票s)

2004年10月14日

6分钟阅读

viewsIcon

54327

downloadIcon

472

如何解决“添加类”向导中烦人的路径问题。

引言

当你想使用通用类向导在 C++ 项目中添加一个类时,你经常会因为无法提供类文件创建的“默认”路径而感到恼火。大多数项目都使用分离的源文件,“cpp”和“h”文件不保存在与“sln”和“vcproj”文件相同的目录中。当你想要在一个目录中添加多个类时,你会浪费很多时间,因为你必须点击“.h”和“.cpp”的浏览按钮并选择一个目录。向导总是从存储项目配置文件所在的目录开始。每次你想添加一个类时,这两个浏览步骤都必须执行,而且 IDE 不会保存向导中使用的最后一个路径。对于下一个类,你必须再次浏览你的目录结构。如果你有很多目录和深层嵌套的文件夹,这很快就会变成一场噩梦。

Visual Studio .NET 向导

幸运的是,有一种简单的方法可以修复标准的向导。向导实际上由一个 JavaScript 文件组成,该文件与一个 HTML 文件和 Visual Studio .NET IDE 进行通信。这个文件是你将在 IDE 中看到的对话框的模板。HTML 还包含一些 JavaScript 函数来处理字段和按钮。你可以轻松地在向导中添加一些字段,并通过 JavaScript 方法扩展它。所有向导都存储在 Visual Studio .NET 文件夹中,你将在“Microsoft Visual Studio .NET/VC7/VCWizards”目录中找到它们。“添加类”向导可在“ClassWiz”文件夹中找到。这个文件夹包含名为“ATL”、“Generic”和“MFC”的子目录,我们将修改“Generic”文件夹。“Generic”文件夹包含三个子目录:“HTML”、“Images”和“Scripts”。你将在“default.htm” (/HTML/1033) 中找到对话框模板,相应的 JavaScript 是“default.js” (/sScripts/1033)。

improvedaddclasswizard5.png

HTML 模板

在浏览器中打开“default.htm”文件,你就会进入一个熟悉的界面。以下是你所能看到的(在 Firefox 中,背景将是紫色的)。

improvedaddclasswizard2.png

现在,如果你查看“default.htm”的源代码,你会发现第一部分定义了 HTML 表单,后面是一个 JavaScript 部分。“<HEAD>”部分位于文件开头,其中包含向导中使用的字段的定义。你可以添加一个类型为“SYMBOL”的元素。我们将添加一个“PATH”文本字段,默认值为一个空字符串。该字段将接收使用 GetDirectoryViaBrowseDlg 函数创建的对话框返回的路径。稍后将详细介绍。

<SYMBOL NAME="HEADER_FILE_VALID" TYPE=bool VALUE=false></SYMBOL>
<SYMBOL NAME="IMPL_FILE_VALID" TYPE=bool VALUE=false></SYMBOL>
<SYMBOL NAME="PATH" TYPE="text" VALUE=""></SYMBOL>

这些符号用于 JavaScript 访问字段值。下一步是在表单中添加字段。如果你有一些 HTML 知识,这很容易。表单布局基于一个表格,你会找到一些“TD”和“TR”标签。我们将把字段添加到基类字段的正下方。我们还必须在字段后面添加一个按钮,点击此按钮将打开浏览目录对话框,此行为通过 onClick 属性定义。每个控件都必须有一个唯一的 ID,你也可以定义访问字段的键盘快捷键(ACCESSKEY 属性)。名为 sideBtn666ThreeColumn 的文本字段会将它的值发送到我们上面定义的 PATH 符号。请注意,我们定义了一个名为 OnBrowsePath() 的新函数,当按下按钮时将调用它,我们必须实现这个函数。

<SPAN CLASS="itemText" TITLE="">
    <LABEL FOR="BASE_CLASS_NAME" ID="BASE_CLASS_NAME_LABEL" 
      TITLE="Enter the name for the base class from 
                            which the class is to be derived.">
      <U>B</U>ase class:
    </LABEL>
    <BR>
    <INPUT CLASS="sideBtnThreeColumn" ID="BASE_CLASS_NAME" 
      TYPE="text" ACCESSKEY="b" 
      TITLE="Enter the name for the base class from 
                           which the class is to be derived." 
      TABINDEX="6">
</SPAN>

<!-- new code --> 
<SPAN CLASS="itemText" TITLE="">
    <P>
    <LABEL FOR="PATH" ID="PATH_LABEL" 
      TITLE="Enter the path where the class files will be stored">
      <U>P</U>ath</LABEL>
    <BR>
    <INPUT CLASS="sideBtn666ThreeColumn" ID="PATH" TYPE="text" ACCESSKEY="p" 
      TITLE="Enter the path where class will be stored" TABINDEX="6">
    <BUTTON CLASS="buttonClass666Custom" ID="BrowsePathBtn" TYPE="BUTTON" 
      TITLE="Browse for the path." onClick="OnBrowsePath();" 
      TABINDEX="6">...</BUTTON>
</SPAN>
<!-- end of new code  -->

我们还删除了“.h 文件”和“.cpp 文件”字段后面的两个按钮。我总是把“.h”和“.cpp”放在同一个目录中。如果你觉得这样烦人,你可以改变它,这是复制路径字段和相应代码的问题。

在浏览器中,现在应该看起来像这样

improvedaddclasswizard3.png

default.htm 的 JavaScript 部分

我们添加了 OnBrowsePath() 函数,它与 defaut.htm 中已有的文件浏览函数非常相似。它基本上调用 GetDirectoryViaBrowseDlg 函数,并将默认路径作为第二个参数。此路径将从 cookie 读取,我将在下一节中讨论这个问题。OnBrowsePath() 返回一个包含目录完整路径的字符串。此路径将附加到 .h.cpp 文件(在 default.js 中)。

// new function added to the wizard, to allow path definition

function OnBrowsePath()
{
    var strFile;
    try
    {
        L_Title1_Text = "VS Wizards Select Path to store class files";
        strPath = window.external.GetDirectoryViaBrowseDlg (L_Title1_Text, 
                                                      cookieData.thePath);
    }
    catch(e)
    {
        if (e.number != OLE_E_PROMPTSAVECANCELLED)
        {
            var L_ErrMsg1_Text = "Error in OnBrowsePath()";
            if (e.description.length != 0)
            {
                L_ErrMsg1_Text += ": ";
                L_ErrMsg1_Text += e.description;
            }
            window.external.ReportError(L_ErrMsg1_Text);
        }
        return;
    }

    // store path in the textfield

    PATH.value = strPath;
}

我没有添加任何代码来验证目录,我期望 GetDirectoryViaBrowseDlg 返回的目录是有效的。要创建一个更健壮的向导,你可以在 Validate(obj) 函数中出现的 switch 语句中添加一个 case。

为下次保存路径

我不是 JavaScript 专家,尝试了各种解决方案。MSDN 上有一个教程 (Scripting) Working with files,但它使用了 ActiveX 控件,并且每次调用向导时 Visual Studio .NET 都会打开一个授权窗口。出于安全原因,JavaScript 不能写入文件,你必须使用 ActiveX 控件或 applet 来 hack。一个更好、更干净的方法是使用 cookies。我找到了一些关于 cookies 的 JavaScript 代码,它直接取自 O'Reilly 的 JavaScript “权威指南”:cookies 代码。我只是把他们的代码复制粘贴到 default.htm 中。

我们首先定义一个全局变量 cookieData,它在 InitDocument(document) 的开头实例化。我们为第一次使用向导提供了一个默认位置,cookie(“last_path”)还需要一个关于其过期时间的指示。

// COOKIE CODE FROM OREILLY

//    ............

//


// cookie for whole document

var cookieData;

function InitDocument(document)
{
    setDirection();
    CLASS_NAME.focus();

    // create a cookie for 10 years, should be enough :)

    cookieData = new Cookie(document, "last_path", 24*365*10);

    // if cookie doesn't exist or is invalid, create it.

    if (!cookieData.load() || !cookieData.name) {
        cookieData.name = "last_path";
    }

    // store a default value for first session

    if (cookieData.thePath==null)
    { cookieData.thePath="C:\\"; cookieData.store();}


    if (window.external.FindSymbol("DOCUMENT_FIRST_LOAD"))
    {
        var L_WizardDialogTitle_Text = "Generic C++ Class Wizard";
        window.external.AddSymbol("WIZARD_DIALOG_TITLE", 
                                        L_WizardDialogTitle_Text);
        window.external.SetDefaults(document);
    }
    window.external.Load(document);

    // store the cookie value into the textfield in the form
    PATH.value = cookieData.thePath;
}

当我们完成时,我们不应该忘记将路径存储在 cookie 中。在 OnFinish(document) 中调用 cookieDatastore() 方法就可以了。

function OnFinish(document)
{

    if (!ValidateInput())
        return;

    cookieData.thePath = PATH.value;
    cookieData.store();

    // ....

}

default.js 文件

我们仍然需要修改充当 IDE 和模板之间桥梁的脚本,即“default.js”。需要在开头添加一个变量:var strPath = wizard.FindSymbol("PATH");,它的作用是从模板获取 PATH 的值。现在很简单,我们只需要将路径附加到 .cpp.h 文件。

var strProjectName = wizard.FindSymbol("PROJECT_NAME");
var strClassName = wizard.FindSymbol("CLASS_NAME");
var strHeader = wizard.FindSymbol("HEADER_FILE");
var strImpl = wizard.FindSymbol("IMPL_FILE");
var strBaseName = wizard.FindSymbol("BASE_CLASS_NAME");
var strPath = wizard.FindSymbol("PATH");

// ... 


var newclass = oCM.AddClass(strClassName, strPath+"/"+strHeader, 
                 vsCMAddPositionEnd, "", "", vsCMAccessDefault);

// ...


newclass.AddFunction(strClassName, vsCMFunctionConstructor, "", 
     vsCMAddPositionEnd, vsCMAccessPublic, strPath+"/"+strImpl);
var oDestructor = newclass.AddFunction("~"+strClassName, 
     vsCMFunctionDestructor, "", vsCMAddPositionEnd, 
     vsCMAccessPublic, strPath+"/"+strImpl);

安装

打开 zip 文件并将 default.jsdefault.htm 复制到正确的位置(我包含了完整的路径以确保无误,你可能需要将它们移动到正确的地方)。我建议你备份当前的向导,以防出现问题。你无需重启 Visual Studio .NET 即可设置新向导。如果你像在进行这些修复之前那样,在 C++ 项目中调用“添加类”向导,你应该会看到新版本。

结论

improvedaddclasswizard4.png

我在 Visual Studio .NET 2003 中开发和测试了该向导。我猜它在早期版本的 Visual Studio .NET 中也能工作,微软的原始版本是在 2001 年编写的。如果你能改进它,请随时在论坛上发布你的修改 :) 如前所述,我根本不是 JavaScript 专家,这是我第二次接触 JavaScript 代码,也是一个学习这个脚本语言的好方法(并发现我更喜欢 Python ;))。

© . All rights reserved.