文档属性可以显示在 Visual Studio 属性 窗口。 编程语言通常没有特性与各个源文件。 但是, XML 文档的支持影响编码、架构和样式表的属性。
讨论
如果该语言需要自定义文档属性,必须从 DocumentProperties 类派生类并实现在派生类中的所需特性。
此外,文档属性通常存储在源文件。 ,在一个对文档属性。 属性 窗口上时,这将 属性 窗口需要语言服务分析属性信息从源文件显示和更新源文件。
自定义 DocumentProperties 类
若要支持自定义文档属性,必须从 DocumentProperties 类派生类并添加多个属性,当您需要。 还应提供用户属性来组织它们在 属性 显示窗口。 如果属性只有 get 访问器,显示为只读在 属性 窗口。 如果特性具有 get 和 set 访问器,属性在 属性 窗口可能也会更新。
示例
这是从 DocumentProperties派生的示例类,显示两个属性、文件名和说明。 当属性更新时,在 LanguageService 类的自定义方法调用写入源文件的属性。
using System.ComponentModel;
using Microsoft.VisualStudio.Package;
namespace TestLanguagePackage
{
class TestDocumentProperties : DocumentProperties
{
private string m_filename;
private string m_description;
public TestDocumentProperties(CodeWindowManager mgr)
: base(mgr)
{
}
// Helper function to initialize this property without
// going through the FileName property (which does a lot
// more than we need when the filename is set).
public void SetFileName(string filename)
{
m_filename = filename;
}
// Helper function to initialize this property without
// going through the Description property (which does a lot
// more than we need when the description is set).
public void SetDescription(string description)
{
m_description = description;
}
////////////////////////////////////////////////////////////
// The document properties
[CategoryAttribute("General")]
[DescriptionAttribute("Name of the file")]
[DisplayNameAttribute("Filename")]
public string FileName
{
get { return m_filename; }
set
{
if (value != m_filename)
{
m_filename = value;
SetPropertyValue("Filename", m_filename);
}
}
}
[CategoryAttribute("General")]
[DescriptionAttribute("Description of the file")]
[DisplayNameAttribute("Description")]
public string Description
{
get { return m_description; }
set
{
if (value != m_description)
{
m_description = value;
SetPropertyValue("Description", m_description);
}
}
}
///////////////////////////////////////////////////////////
// Private methods.
private void SetPropertyValue(string propertyName, string propertyValue)
{
Source src = this.GetSource();
if (src != null)
{
TestLanguageService service = src.LanguageService as TestLanguageService;
if (service != null)
{
// Set the property in to the source file.
service.SetPropertyValue(src, propertyName, propertyValue);
}
}
}
}
}
实例化自定义 DocumentProperties 类
若要实例化自定义文档属性类,必须重写在 LanguageService 类版本的 CreateDocumentProperties 方法返回 DocumentProperties 类的一个实例。
示例
using System.ComponentModel;
using Microsoft.VisualStudio.Package;
namespace TestLanguagePackage
{
class TestLanguageService : LanguageService
{
private TestDocumentProperties m_documentProperties;
public override DocumentProperties CreateDocumentProperties(CodeWindowManager mgr)
{
if (m_documentProperties == null)
{
m_documentProperties = new TestDocumentProperties(mgr);
}
return m_documentProperties;
}
}
}
在源文件中的属性
由于文档属性通常是特定于源文件,值存储在源文件。 这需要从语言分析器或扫描仪支持定义这些属性。 例如, XML 的属性在根节点文档存储。 修改在根节点的值,更改时 属性 窗口值,并且,根节点在编辑器中进行更新。
示例
此示例在源文件中的前两行存储属性 “文件名”和 “说明”,嵌入在一个特殊注释头,如下所示:
//!Filename = file.testext
//!Description = A sample file
此示例演示两个方法必要获取和设置从源文件的前两行的文档属性和属性如何更新,如果用户直接修改源文件。 在显示的示例中的 SetPropertyValue 方法此处从 TestDocumentProperties 类调用的相同一个如 “自定义 DocumentProperties 类”一节所示。
此示例使用扫描仪确定标记的类型在前两行。 此示例仅用于阐释目的。 此情况的一个常见的方法是分析源文件到哪些调用分析树树的每个节点都包含有关特定标记的位置信息。 根节点将包含文档属性。
using System.ComponentModel;
using Microsoft.VisualStudio.Package;
namespace TestLanguagePackage
{
// TokenType.Comment is the last item in that enumeration.
public enum TestTokenTypes
{
DocPropertyLine = TokenType.Comment + 1,
DocPropertyName,
DocPropertyAssign,
DocPropertyValue
}
class TestLanguageService : LanguageService
{
// Search this many lines from the beginning for properties.
private static int maxLinesToSearch = 2;
private TestDocumentProperties m_documentProperties;
// Called whenever a full parsing operation has completed.
public override void OnParseComplete(ParseRequest req)
{
if (m_documentProperties != null)
{
Source src = GetSource(req.View);
if (src != null)
{
string value = GetPropertyValue(src, "Filename");
m_documentProperties.SetFileName(value);
value = GetPropertyValue(src, "Description");
m_documentProperties.SetDescription(value);
// Update the Properties Window.
m_documentProperties.Refresh();
}
}
}
// Retrieves the specified property value from the given source.
public string GetPropertyValue(Source src, string propertyName)
{
string propertyValue = "";
if (src != null)
{
IVsTextColorState colorState = src.ColorState;
if (colorState != null)
{
string line;
TokenInfo[] lineInfo = null;
int i;
for (i = 0; i < maxLinesToSearch; i++)
{
line = src.GetLine(i);
lineInfo = src.GetColorizer().GetLineInfo(
src.GetTextLines(),
i,
colorState);
if (lineInfo == null)
{
continue;
}
if (lineInfo[0].Type != (TokenType)TestTokenTypes.DocPropertyLine)
{
// Not a property line.
continue;
}
TokenInfo valueInfo = new TokenInfo();
int tokenIndex = -1;
for (tokenIndex = 0;
tokenIndex < lineInfo.Length;
tokenIndex++)
{
if (lineInfo[tokenIndex].Type == (TokenType)TestTokenTypes.DocPropertyName)
{
break;
}
}
if (tokenIndex >= lineInfo.Length)
{
// No property name on the line.
continue;
}
string name = src.GetText(i,
lineInfo[tokenIndex].StartIndex,
i,
lineInfo[tokenIndex].EndIndex + 1);
if (name != null)
{
if (String.Compare(name, propertyName, true) == 0)
{
for ( ;
tokenIndex < lineInfo.Length;
tokenIndex++)
{
if (lineInfo[tokenIndex].Type == (TokenType)TestTokenTypes.DocPropertyValue)
{
break;
}
}
if (tokenIndex < lineInfo.Length)
{
propertyValue = src.GetText(i,
lineInfo[tokenIndex].StartIndex,
i,
lineInfo[tokenIndex].EndIndex + 1);
}
break;
}
}
}
}
}
return propertyValue;
}
// Sets the specified property into the given source file.
// Called from the TestDocumentProperties class.
public void SetPropertyValue(Source src, string propertyName, string propertyValue)
{
string newLine;
if (propertyName == null || propertyName == "")
{
// No property name, so nothing to do
return;
}
if (propertyValue == null)
{
propertyValue = "";
}
// This is the line to be inserted.
newLine = String.Format("//!{0} = {1}", propertyName, propertyValue);
// First, find the line on which the property belongs.
// If line is found, replace it.
// Otherwise, insert the line at the beginning of the file.
if (src != null)
{
IVsTextColorState colorState = src.ColorState;
if (colorState != null)
{
int lineNumber = -1;
string line;
TokenInfo[] lineInfo = null;
int i;
for (i = 0; i < maxLinesToSearch; i++)
{
line = src.GetLine(i);
lineInfo = src.GetColorizer().GetLineInfo(
src.GetTextLines(),
i,
colorState);
if (lineInfo == null)
{
continue;
}
if (lineInfo[0].Type != (TokenType)TestTokenTypes.DocPropertyLine)
{
// Not a property line
continue;
}
TokenInfo valueInfo = new TokenInfo();
int tokenIndex = -1;
for (tokenIndex = 0;
tokenIndex < lineInfo.Length;
tokenIndex++)
{
if (lineInfo[tokenIndex].Type == (TokenType)TestTokenTypes.DocPropertyName)
{
break;
}
}
if (tokenIndex >= lineInfo.Length)
{
// No property name on the line.
continue;
}
string name = src.GetText(i,
lineInfo[tokenIndex].StartIndex,
i,
lineInfo[tokenIndex].EndIndex + 1);
if (name != null)
{
if (String.Compare(name, propertyName, true) == 0)
{
lineNumber = i;
break;
}
}
}
// Set up an undo context that also handles the insert/replace.
EditArray editArray = new EditArray(src,
true,
"Update Property");
if (editArray != null)
{
TextSpan span = new TextSpan();
if (lineNumber != -1)
{
// Replace line.
int lineLength = 0;
src.GetTextLines().GetLengthOfLine(lineNumber,
out lineLength);
span.iStartLine = lineNumber;
span.iStartIndex = 0;
span.iEndLine = lineNumber;
span.iEndIndex = lineLength;
}
else
{
// Insert new line.
span.iStartLine = 0;
span.iStartIndex = 0;
span.iEndLine = 0;
span.iEndIndex = 0;
newLine += "\n";
}
editArray.Add(new EditSpan(span, newLine));
editArray.ApplyEdits();
}
}
}
}
}
}