自訂文件內容 (受管理的封裝架構)
文件內容可以顯示在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 類別執行個體化
若要將您自訂的文件屬性的類別執行個體化,您必須覆寫CreateDocumentProperties的版本中的方法LanguageService類別以傳回單一執行個體您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();
}
}
}
}
}
}