Windows Live Writer用のプラグインを開発するはじめにWindows Live WriterはMicrosoftが無償で提供しているデスクトップブログオーサリングツールです。Live Writerを使用すると、オフライン状態でもデスクトップからブログエントリを書くことができます。Live WriterはMicrosoftの専用ブログサイト(Windows Live Spaces)をサポートするだけでなく、MetaWeblog APIまたはAtom Publishing Protocolに対応する任意のブログエンジンをサポートしており、たとえばWordpress、Community Server、LiveJournal、Bloggerなどのブログエンジンで使用できます。Live Writerではユーザーのブログスタイルをダウンロードし、それをテンプレートとして使用できるので、自分のブログのスタイルでブログエントリを書くことができます。ブログを始めたばかりの初心者でも、ブログ歴が長い人でも、Live Writerを使用すればブログの使い勝手は格段に向上します。しかも、Live Writerに含まれていない機能をプラグインとして追加することができます。 編集者注
この記事はもともとCoDe Focus Magazineの「Windows Live」エディション(2008, Vol. 5, Issue 2)に掲載されたものですが、許可を得てここに転載しました。
必要な開発環境Live Writerのプラグインは.NETで開発します。.NET 1.1を使用することもできますが、Microsoftでは.NET 2.0以上を使用するように推奨しています。Visual Studio 2005以上をインストールしておく必要があります。Visual Studio 2005または2008のバージョンは問いません(Express Editionsも可)。また、Windows Live Writerをダウンロードすることも必要です。Windows Live Writerには、プラグインの基礎となる必須のAPI DLLが含まれています。著者注
プラグインの開発にはどの.NET言語を使用してもかまいませんが、本稿の例ではC#を使用しています。
プラグインを書くVisual Studioを開き、新しいクラスライブラリプロジェクトを作成して、プラグインとして意味のある名前を付けます。既定名の「class1.cs」を「plugin.cs」に変えましょう(これは必須ではありませんが、こうするとメインのプラグインクラスが一目でわかります)。次にWindows Live Writer APIの参照を追加する必要があります。[Add Reference]ウィンドウを開き、Windows Live Writerのインストールフォルダ(通常はC:\Program Files\Windows Live\Writer\)にあるWindowsLive.Writer.API.dllを選択します。この参照については、Copy Localプロパティをfalseに設定することをお勧めします(これにより、以前のバージョンのAPI DLLがLive WriterのPluginsフォルダにコピーされなくなります)。plugin.csを開き、次のusingステートメントを含めます。
using WindowsLive.Writer.Api;
using System.Windows.Forms;
具体的なコードを次に示します。 [WriterPlugin ("d39bba2b-9522-49b1-8731-61030ccd6c95", "My First Plugin", Description = "This is my first plugin", HasEditableOptions = false, Name = "My First Plugin", PublisherUrl = "http://www.liveside.net")] [InsertableContentSource ("Bold Text")] プラグインのアイコンを追加するには、次のコードを含めます。
ImagePath = "icon.gif",
この後、プラグインクラスを作成する必要があります。このプラグインクラスはLive Writer APIから継承する必要があります。継承元として、ContentSourceとSmartContentSourceという2つの選択肢があります。ContentSource系プラグインは非常に基本的なものであり、テキストをブログエントリに挿入するだけで、そのテキストを再度変更することはできません。一方、SmartContentSource系プラグインでは、これよりもずっと多くのことができます。この最初のサンプルではContentSourceインタフェースを継承します。 public class LiveWriterExamplePlugin : ContentSource
CreateContentです。プラグインコードでは、これらのメソッドをすべてオーバーライドします。また、これらは相互排他的な関係ではなく、1つのプラグインで2種類以上を使用できます。CreateContentメソッドをオーバーライドする下記のコードの、コンテンツが含まれる文字列参照(content)に注目してください。この参照については、次の2点に注意する必要があります。
DialogResult.OKを返します。このサンプルは、選択されているテキストをボールドにする単純なプラグインとなります。したがって、CreateContentメソッドのコードもごくシンプルです。public override DialogResult CreateContent (IWin32Window dialogOwner, ref string content) { if (!string.IsNullOrEmpty(content)) content = string.Format( "<b>{0}</b>", content); return DialogResult.OK; } リスト1 プラグインのコード
using System; using System.Collections.Generic; using System.Text; using WindowsLive.Writer.Api; using System.Windows.Forms; namespace LiveWriterExample { [WriterPlugin("d2c99304-8648-4696-9ef1-6a82a2d070c9", "LiveWriterExamplePlugin", Description = "Makes highlighted text bold.", HasEditableOptions = true, ImagePath = "icon.gif", Name = "Bold Text Plugin", PublisherUrl = "http://www.liveside.net")] [InsertableContentSource("Bold Text")] public class LiveWriterExamplePlugin : ContentSource { public override DialogResult CreateContent (IWin32Window dialogOwner, ref string content) { // If nothing is highlighted, content will be empty. // If this is the case, the plugin does nothing. if (!string.IsNullOrEmpty(content)) content = string.Format("<b>{0}</b>", content); return DialogResult.OK; } } } コンパイルとデバッグLive Writer用のプラグインのコンパイルとデバッグについては、いくつか知っておくべきコツがあります。なぜなら、これらのプラグインはDLLファイルなので、単独では実行できないからです。重要なコツは2つあり、ビルドされたDLLをLive Writerのプラグインディレクトリにコピーするときと、プラグインをデバッグする目的でLive Writerを起動するときに必要になります。図1は、完成したプラグインをLive Writerのプラグインディレクトリにコピーするために必要なコマンドを示しています。 XCOPY /D /Y /R "$(TargetPath)" "C:\Program Files\Windows Live\Writer\Plugins\" 著者注
Program FilesフォルダがWindowsの既定と違う場所にある場合は、システムの実際の構成に合わせて、これを変更する必要があります。あるいは、プロジェクトのoutputフォルダの設定をLive WriterのPluginsフォルダにするという方法もあります。
図2は、プラグインをデバッグするために必要な設定を示しています。プラグインをデバッグするには、Windows Live Writerを外部アプリケーションとして起動する必要があります。これが機能するのは、設定を図1のように変更してある場合だけです。このようにしないと、プラグインはプラグインディレクトリにコピーされないので、Writerによって認識されません。Visual Studioでこの2つの設定を構成した後は、F5キーを押すとプロジェクトがビルドされ、プラグインがプラグインディレクトリにコピーされ、さらにデバッグモードでWindows Live Writerにロードされます。 このプラグインをデバッグモードで使用したときのテキストの状態の変化を図3、図4、および図5に示します。図3は、通常のフォントスタイルで入力されたテキストです。図4はテキストを選択した状態であり、プラグインはこのテキストを受け取って使用することができます。図5は、Writerの[Insert]セクションでプラグイン名をクリックした結果を示しています。 フォームの使用Windows Formsをプラグインで使用することはよくあるので、プラグインコードから正しく呼び出す方法を知ることが大切です。フォームを使用することで、ユーザーがブログエントリに挿入するものを正確に制御できます。ビデオURLを挿入する、リストから顔文字を選択する、Amazonから書籍を検索するなど、フォームはさまざまな方法で使用できます。フォームはCreateContentメソッドから呼び出すことができます。 public override DialogResult CreateContent (IWin32Window dialogOwner, ref string content) { using (frmMain form = new frmMain()) { DialogResult result = form.ShowDialog(); if (result == DialogResult.OK) content = form.NewContent; return result; } } NewContentというプロパティを持つ単純なフォームを表示します。このプロパティはブログエントリに挿入する新しいコンテンツ値を返します。通常、プラグインが最終的に挿入するコードは、フォームのプロパティの値に基づきます。フォーム内の[Accept]ボタンのコードには次の行が必要です。
this.DialogResult = DialogResult.OK;
if条件が真になります。設定の使用プラグインの既定値をユーザーが設定できるようにしたい場合は、フォームと同様に、設定オブジェクトをプラグインに組み込みます。通常、ユーザーはLive Writerの[Options]領域でプラグインを選択し、[Options]ボタンを選択することで、プラグインの既定値を設定できます(図6を参照)。まず、プラグインにオプションフォームがあることをLive Writerに知らせなければなりません。これを行うには、プラグイン属性で HasEditableOptions=trueを設定する必要があります。設定クラスではWindowsLive.Writer.Apiも使用する必要があります。なぜなら、このオブジェクトには、プラグインが設定を読み書きするための媒体となるIPropertiesオブジェクトを渡す必要があるからです。設定クラスのコンストラクタは次のようになります。
IProperties m_properties;
public PluginSettings(IProperties properties)
{
m_properties = properties;
}
m_propertiesに注目してください。これはクラスのプロパティの中でIPropertiesを呼び出すために使われます。Live Writer APIは、String、Int、Float、Bool、Decimalという5種類の設定型を認識します。これらはすべて独自の GetメソッドとSetメソッドを持っており(GetString()やSetString()など)、これらはIPropertiesクラスの一部として公開されます。そのため、Live Writer APIを使って保存される設定の単純なプロパティは次のようになります。public bool DefaultBoldOption { get { return m_properties.GetBoolean ("DEFAULTBOLD", true); } set { m_properties.SetBoolean ("DEFAULTBOLD", value); } } Getメソッドには属性名(この場合はDEFAULTBOLD)と既定値が必要です(プラグインの初回使用時にはこの既定値が使われます)。それぞれのSetメソッドは、Getメソッドと同じように属性名を必要とし、渡された値を使って単純に属性を設定します。プラグインに持たせたいすべての設定について、これを行う必要があります。設定クラス(PluginSettings)の用意ができたら、次はプラグインコード内から設定を初期化する必要があります。元のプラグインクラスに戻り、グローバル変数を追加します。 PluginSettings m_defaultsettings; Initialize()メソッドをオーバーライドします。public override void Initialize (IProperties pluginOptions) { base.Initialize(pluginOptions); m_defaultsettings = new PluginSettings(pluginOptions); } base.Initialize(pluginOptions)により、このプラグインのレジストリからプラグイン設定が取得され、IPropertiesクラスに格納されます。このクラスをPluginSettingsオブジェクトに引き渡すことで、プラグイン設定の読み書きが可能になります。さらに、オプションフォームを作成する必要があります。この例では、図7のようなフォームを作成しました。このフォームにPluginSettingsオブジェクトを渡す必要があります。 図7 サンプルプラグインのオプションフォーム。 ![]() PluginSettings m_settings; public frmOptions(PluginSettings settings) { InitializeComponent(); m_settings = settings; // This sets the check box to be // whatever the default option was chBoldOption.Checked = m_settings.DefaultBoldOption; } private void btnSave_Click (object sender, EventArgs e) { // Sets the option to whatever // the check box is currently at m_settings.DefaultBoldOption = chBoldOption.Checked; this.Close(); } 次はプラグインクラスに戻り、 EditOptionsメソッドをオーバーライドして、オプションフォームを呼び出すようにする必要があります。public override void EditOptions (IWin32Window dialogOwner) { PluginSettings settings = new PluginSettings(this.Options); frmOptions op = new frmOptions(settings); op.ShowDialog(); } CreateContentメソッドのコードを次のように変更します。using (frmMain form = new frmMain()) { DialogResult result = form.ShowDialog(); if (result == DialogResult.OK) { content = form.NewContent; if (m_defaultsettings.DefaultBoldOption) { content = string.Format( "<b>{0}</b>", content); } } return result; } SmartContentSourceプラグインSmartContentSourceプラグインは基本的なContentSourceプラグインよりも複雑です。最も大きな違いは、ContentSourceプラグインはブログエントリにHTMLコードを挿入するだけで、後からプラグインを変更または編集することはできないという点です。それに対し、SmartContentSourceプラグインは、挿入されるコードに関連する詳細情報を格納できるため、ユーザーはWriter内の作成済みブログエントリを開き、プラグインを使っていつでも編集できます。必要なものを計画するSmartContentSourceプラグインを作成するときは、まずプラグインで内部的に必要になるもの、つまりプラグインの設定や、エンドユーザーによる編集を可能にしたいコンテンツ部分などについての計画を立てることをお勧めします。ユーザーによる再編集を可能にする部分をあらかじめ決めておけば、プラグインでどんな設定が必要になるか判断しやすくなります。これらの設定はISmartContentオブジェクトのIPropertiesプロパティ内に保持され、そのISmartContentオブジェクトがLive Writerからプラグインに渡されます。さらに、エンドユーザーにコンテンツの編集をどこで行わせるか、編集時にもコンテンツ作成時と同様のフォームを使用させるか、Writerサイドバーから編集させるかという点についても決める必要があります。 リスト2は、この例のプラグインのPluginSettingsクラスの中で使われているコードを示しています。 SmartContentSourceプラグインを書くには、ContentSourceではなく、SmartContentSourceから継承する必要があります。 public class HiddenText : SmartContentSource リスト2 SmartContentSourceプラグインのPluginSettingsクラスのコード
using System; using System.Collections.Generic; using System.Text; using WindowsLive.Writer.Api; namespace EditPublish { public class PluginSettings { IProperties m_properties; private const string PLACEHOLDER = "PLACEHOLDER"; private const string ACTUALCODE = "ACTUALCODE"; public PluginSettings(IProperties properties) { m_properties = properties; } public string PlaceHolder { get { return m_properties.GetString(PLACEHOLDER, ""); } set { m_properties.SetString(PLACEHOLDER, value); } } public string FinalText { get { return m_properties.GetString(ACTUALCODE, ""); } set { m_properties.SetString(ACTUALCODE, value); } } } } CreateContentメソッドをオーバーライドするContentSourceプラグインと同様、CreateContentメソッドをオーバーライドする必要がありますが、この場合のコードは少し異なります。public override DialogResult CreateContent (IWin32Window dialogOwner, ISmartContent newContent) { PluginSettings settings = new PluginSettings(newContent.Properties); using (frmMain main = new frmMain(settings)) { DialogResult result = main.ShowDialog(); return result; } } このコードでは、ISmartContentオブジェクトのIPropertiesプロパティを受け取るPluginSettingsオブジェクトを作成し、このオブジェクトをフォームに渡しています。これにより、必要な設定をフォームからPluginSettingsクラスに書き込むのが容易になります。 GeneratePublishHTMLメソッドメインプラグインクラスには、オーバーライドできる3つの新しいメソッドがあります(うちの2つは必須で、残り1つはオプション)。最初のメソッドはGeneratePublishHTMLで、ここで実際のブログに発行されるHTMLを生成します。このメソッドは必須です。GeneratePublishHTMLメソッドのごく簡単な例を次に示します。public override string GeneratePublishHtml (ISmartContent content, IPublishingContext publishingContext) { PluginSettings settings = new PluginSettings(content.Properties); return settings.FinalText; } IPublishingContextオブジェクト引数には、書き込み先のブログのタイプに関する情報が格納されます。これには各ブログのGUID(Live Writer自身が提供)と、現在の発行サービスの名前(Windows Live Spaces、LiveJournal、WordPress.comなど)が含まれます。 CreateEditorメソッドこれは2番目の必須メソッドで、オーバーライドする必要があります。このメソッドは、Live Writerエディタ内でSmartContentSourceオブジェクトが選択されたときにLive Writerに表示されるサイドバーを作成します。public override SmartContentEditor CreateEditor (ISmartContentEditorSite editorSite) { return new ContextEditor(); } GenerateEditorHTMLプラグインによっては、ブログエントリ内に表示するものと、Live Writerのエディタ内に表示するものとが必ずしも一致しないことがあります(たとえばJavaScript関数を扱うプラグインの場合など)。このような場合、実際のLive Writer編集領域には何か別のものを表示しなければなりません。そのためには、GenerateEditorHTMLメソッドをオーバーライドする必要があります。public override string GenerateEditorHtml (ISmartContent content, IPublishingContext publishingContext) { PluginSettings settings = new PluginSettings(content.Properties); return settings.PlaceHolder; } GeneratePublishHTMLメソッドと同じオブジェクトが渡されるので、同じ情報を使用してHTMLコードをエディタに入れることができます。このメソッドはオプションのメソッドであり、プラグインに必須のものではありません。これをオーバーライドしなければ、Live Writerは GeneratePublishHTMLメソッド内にあるものを単純に使用します。リスト3は、この例のプラグインのメインクラスのフルコードを示しています。 リスト3 SmartContentSourceプラグインのプラグインクラスのコード
using System.Windows.Forms; using WindowsLive.Writer.Api; namespace EditPublish { [WriterPlugin("18d43e01-4549-4fde-8ca6-c7b4b7385fac", "Insert Placeholder", PublisherUrl = "http://scottisafooldev.spaces.live.com", Description = "Lets you put in a placeholder for the editor, "+ "to be replaced with other text when published.", ImagePath = "writer.png", HasEditableOptions = false)] [InsertableContentSource("Placeholder")] public class HiddenText : SmartContentSource { public override DialogResult CreateContent (IWin32Window dialogOwner, ISmartContent newContent) { PluginSettings settings = new PluginSettings(newContent.Properties); using (frmMain main = new frmMain(settings)) { DialogResult result = main.ShowDialog(); return result; } } public override SmartContentEditor CreateEditor (ISmartContentEditorSite editorSite) { return new ContextEditor(); } public override string GeneratePublishHtml (ISmartContent content, IPublishingContext publishingContext) { PluginSettings settings = new PluginSettings(content.Properties); return settings.FinalText; } public override string GenerateEditorHtml (ISmartContent content, IPublishingContext publishingContext) { PluginSettings settings = new PluginSettings(content.Properties); return settings.PlaceHolder; } } } SmartContentSourceプラグインでフォームを使用するフォームを使用するのは、基本的なContentSourceプラグインを作成したときに説明したプロセスに似ています。主な違いは、最終的なHTMLを作成して返す代わりに、設定をPluginSettingsクラスに保存し、このクラスを後からプラグインのメインクラスのメソッドの中で使用するという点です。リスト4は、この例のプラグインで使用するメインフォームのフルコードを示しています。 リスト4 SmartContentSourceプラグインのメインフォームのコード
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace EditPublish { public class frmMain : Form { PluginSettings m_settings; public frmMain(PluginSettings settings) { m_settings = settings; InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { if (textBox1.Text != "") { m_settings.PlaceHolder = textBox1.Text; } else { MessageBox.Show("You need to set a placeholder", "Big Fat Hairy Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (textBox2.Text != "") { m_settings.FinalText = textBox2.Text; } else { MessageBox.Show("Enter some text", "Big Fat Hairy Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } this.DialogResult = DialogResult.OK; this.Close(); } /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing"> /// true if managed resources should be disposed; /// otherwise, false.</param> protected override void Dispose (bool disposing) { if(disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager( typeof(frmMain)); this.textBox1 = new System.Windows.Forms.TextBox(); this.textBox2 = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.button1 = new System.Windows.Forms.Button(); this.button2 = new System.Windows.Forms.Button(); this.SuspendLayout(); // // textBox1 // this.textBox1.Location = new System.Drawing.Point(13, 124); this.textBox1.Multiline = true; this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(407, 48); this.textBox1.TabIndex = 0; // // textBox2 // this.textBox2.Location = new System.Drawing.Point(13, 203); this.textBox2.Multiline = true; this.textBox2.Name = "textBox2"; this.textBox2.Size = new System.Drawing.Size(407, 56); this.textBox2.TabIndex = 1; // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(13, 105); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(168, 13); this.label1.TabIndex = 2; this.label1.Text = "Text to appear in the Writer editor:"; // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(13, 185); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(189, 13); this.label2.TabIndex = 3; this.label2.Text = "Text to appear in the actual blog entry:"; // // label3 // this.label3.Anchor = ((System.Windows.Forms.AnchorStyles) (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.label3.Location = new System.Drawing.Point(13, 9); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(407, 96); this.label3.TabIndex = 4; this.label3.Text = resources.GetString("label3.Text"); // // button1 // this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.button1.Location = new System.Drawing.Point(264, 272); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(75, 23); this.button1.TabIndex = 5; this.button1.Text = "Insert"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // button2 // this.button2.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.button2.Location = new System.Drawing.Point(345, 272); this.button2.Name = "button2"; this.button2.Size = new System.Drawing.Size(75, 23); this.button2.TabIndex = 6; this.button2.Text = "Cancel"; this.button2.UseVisualStyleBackColor = true; // // frmMain // this.AcceptButton = this.button1; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.button2; this.ClientSize = new System.Drawing.Size(432, 307); this.Controls.Add(this.button2); this.Controls.Add(this.button1); this.Controls.Add(this.label3); this.Controls.Add(this.label2); this.Controls.Add(this.label1); this.Controls.Add(this.textBox2); this.Controls.Add(this.textBox1); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "frmMain"; this.ShowIcon = false; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Insert Placeholder Plugin"; this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.TextBox textBox2; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.Button button1; private System.Windows.Forms.Button button2; } } サイドバー(ContextEditor)Live Writerのエディタ内でSmartContentSourceを選択すると、Live Writerウィンドウの右側にあるサイドバーがアクティブになります(図8を参照)。このようなサイドバーを表示するためには、プラグインプロジェクト内に新しいユーザーコントロールを作成する必要があります。この例ではContextEditorと名付けています。UserControlインタフェースを継承するのではなく、SmartContentEditorインタフェースから継承する必要があります(WindowsLive.Writer.APIを使用することを忘れないでください)。 public partial class ContextEditor : SmartContentEditor PluginSettings m_settings; ISmartContent m_content; public ContextEditor() { InitializeComponent(); this.SelectedContentChanged += new EventHandler( SelectedContentNowChanged); } イベントハンドラメソッドのコードも重要です。 void SelectedContentNowChanged (object sender, EventArgs e) { m_content = SelectedContent; m_settings = new PluginSettings(m_content.Properties); textBox1.Text = m_settings.PlaceHolder; textBox2.Text = m_settings.FinalText; } 重要
ISmartContentのIPropertiesプロパティを取得したい場合は、
ベストプラクティスとしては、このイベントハンドラメソッドを使用して、現在の設定をサイドバー内の設定に反映させるか、これらの設定を反映させるメソッドを呼び出すようにします。SelectedContent.Propertiesを使用しないでください(正しく機能しません)。代わりに、SelectedContentをローカル変数に割り当て、IPropertiesオブジェクトをPluginSettingsクラスに渡すときに、そのローカル変数を使用してください。ユーザーがサイドバーを使用してコンテンツを変更しても、 OnContentEdited()メソッドを呼び出すまでエディタ内では何も変化しません。このメソッドは、すべての変更が行われた後に1つのボタンから呼び出すことも、変更が行われるたびに呼び出すこともできます。エディタを更新するタイミングは開発者に任されています。リスト5に、サンプルプラグインのContextEditorに必要なコードを示します。 リスト5 SmartContentSourceプラグインのContextEditorのコード
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using WindowsLive.Writer.Api; namespace EditPublish { public class ContextEditor : SmartContentEditor { PluginSettings m_settings; ISmartContent m_content; public ContextEditor() { InitializeComponent(); this.SelectedContentChanged += new EventHandler( SelectedContentNowChanged); } void SelectedContentNowChanged (object sender, EventArgs e) { m_content = SelectedContent; m_settings = new PluginSettings(m_content.Properties); textBox1.Text = m_settings.PlaceHolder; textBox2.Text = m_settings.FinalText; } private void button1_Click (object sender, EventArgs e) { if (textBox1.Text != "") { m_settings.PlaceHolder = textBox1.Text; } else { MessageBox.Show("No placeholder", "Big Fat Hairy Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (textBox2.Text != "") { m_settings.FinalText = textBox2.Text; } else { MessageBox.Show("No text", "Big Fat Hairy Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } OnContentEdited(); } /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be /// disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.textBox1 = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.textBox2 = new System.Windows.Forms.TextBox(); this.label2 = new System.Windows.Forms.Label(); this.button1 = new System.Windows.Forms.Button(); this.label3 = new System.Windows.Forms.Label(); this.SuspendLayout(); // // textBox1 // this.textBox1.Location = new System.Drawing.Point(3, 91); this.textBox1.Multiline = true; this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(178, 58); this.textBox1.TabIndex = 0; // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(0, 73); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(168, 13); this.label1.TabIndex = 3; this.label1.Text = "Text to appear in the Writer editor:"; // // textBox2 // this.textBox2.Location = new System.Drawing.Point(3, 182); this.textBox2.Multiline = true; this.textBox2.Name = "textBox2"; this.textBox2.Size = new System.Drawing.Size(178, 58); this.textBox2.TabIndex = 4; // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(0, 164); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(171, 13); this.label2.TabIndex = 5; this.label2.Text = "Text to appear in actual blog entry:"; // // button1 // this.button1.Location = new System.Drawing.Point(3, 255); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(75, 23); this.button1.TabIndex = 6; this.button1.Text = "Apply"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // label3 // this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)( ((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.label3.BackColor = System.Drawing.Color.White; this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label3.Location = new System.Drawing.Point(0, 0); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(190, 32); this.label3.TabIndex = 7; this.label3.Text = "Insert Placeholder"; // // ContextEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.label3); this.Controls.Add(this.button1); this.Controls.Add(this.label2); this.Controls.Add(this.textBox2); this.Controls.Add(this.label1); this.Controls.Add(this.textBox1); this.Name = "ContextEditor"; this.Size = new System.Drawing.Size(190, 482); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.Label label1; private System.Windows.Forms.TextBox textBox2; private System.Windows.Forms.Label label2; private System.Windows.Forms.Button button1; private System.Windows.Forms.Label label3; } } 開発のヒントLive Writer APIをよく調べると、プラグインではもっと多くの、高度な機能を実現できることに気づくでしょう。たとえば、指定したURLのスクリーンショットを撮ってイメージを返す、優れたスクリーンスクレーピングメソッドなどがあります。MSDNのAPIドキュメントにひととおり目を通すことを強くお勧めします。著者紹介Scott Lovegrove(Scott Lovegrove)
Windows Live MVP for Microsoftであり、Windows Live Writerだけでなく、Windows Live全般に深く関係している。Windows Live専用のコミュニティWebサイト「LiveSide.net」の主要な寄稿者の1人であり、Windows Live上での開発について広範囲にわたって記事を執筆。人気のあるWindows Live Writerプラグインをいくつも書いており、Live Writerサポートフォーラムでは一般サポートと開発者サポートの両方で頻繁に名前が見られる。
![]()
|