SkyDrive へ移動 解説ページ DataGrid へデータベースの内容を表示する問い合わせのサンプルですが、XAML を利用する上でデータはバインドでコントロールに表示されます。その為、データベースのデータを扱う為のプライベートクラス( SELECT 文の列リストを格納するクラス )を作成して利用します。 DataGrid は、ある手順を踏むと、そのクラスから自動的に列を作成してくれる上に、バインドの Mode=OneWay のルールで、コレクションの内容を変更するだけでコントロールの表示を変更する事ができます。
2013年11月04日
VS2010 WPF(C#) DataGrid + データベース バインド / DataGrid に MDB のデータを読み込んで表示するテンプレート
2013年11月01日
VS2012 WPF(C#) : 『TKMP.DLL を使用したイベントによる非同期メール受信処理を Task(await) を使用して順次処理のように記述する』テンプレート
SkyDrive へ移動 Framework 4.5 では、Task<TResult> クラスによって非同期処理の結果を取得してあたかも同期処理のような記述を行う事ができるようになりましたが、既存の非同期処理をそのように書き換える為に、TaskCompletionSource<TResult> クラスを使用します。 単純に考えると、Taskを戻り値とするメソッド内は全て管理されていて、その中で登録されたイベントは全て管理下に置かれるようです。イベントが終了するまで、メソッドが終わる事無く、TaskCompletionSource<TResult> クラスの TrySetResult( SetResult との違いは良く解りません )で、値をセットした時にメソッドが終了するように見えます。 こうする事によって、大きなメリットの一つとして、UI スレッドにふつうに戻って来て処理を行えるところであり、当然ループ内の一部として自然に行を追加しながら非同期を実現する事ができました MainWindow.xaml.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.Net; using System.Windows; using System.Windows.Input; using System.Collections.ObjectModel; using TKMP.Net; using System.IO; using TKMP.Reader; using System.Threading; using System.Threading.Tasks; namespace WPF_DataGrid_MailGet1 { public partial class MainWindow : Window { private MainViewModel mvm = new MainViewModel(); private ImapClient client = null; // ********************************************* // コンストラクタ // ********************************************* public MainWindow() { InitializeComponent(); this.dataGrid1.DataContext = mvm; } // 接続および、メールヘッダの取得 private async void actButton_Click(object sender, RoutedEventArgs e) { mvm.mail_data.Clear(); BasicImapLogon logon = new BasicImapLogon(this.userName.Text, this.password.Password); client = new ImapClient(logon, "imap.gmail.com", 993); client.AuthenticationProtocol = AuthenticationProtocols.SSL; if (!client.Connect()) { return; } // メールデータ一覧を格納するオブジェクト IMailData[] md = client.GetMailList(); // データがありません if (md == null) { return; } // メールデータの数 mailCount.Text = md.Length.ToString(); // 読込み制限 int maxCount = 20; int idx = 0; // 非同期で全て表示 foreach (var data in md) { idx++; if (idx > maxCount) { break; } // Framework 4.5 : 値を返す非同期処理 MailReader reader = await ReadBodyAnsync( data ); // UI スレッドに戻って来ているので、特殊な処理は必要無い string from = ""; string subject = ""; string mdate = ""; // ヘッダの一覧より、目的のヘッダを探す foreach (TKMP.Reader.Header.HeaderString headerdata in reader.HeaderCollection) { if (headerdata.Name == "From") { from = headerdata.Data; } if (headerdata.Name == "Subject") { subject = headerdata.Data; } if (headerdata.Name == "Date") { mdate = headerdata.Data; } } // 行追加 mvm.mail_data.Add(new ItemViewModel() { from = from, subject = subject, mdate = mdate }); } // ソース的には、順次処理なので全てのデータ処理後のクローズ client.Close(); } // ********************************************* // 戻り値を返す事のできる非同期処理 【開始】 // ********************************************* private TaskCompletionSource<MailReader> tcs = null; private Task<MailReader> ReadBodyAnsync(IMailData MailData) { // 戻り値用のクラス tcs = new TaskCompletionSource<MailReader>(); // 個別にイベント登録 MailData.BodyLoaded += new EventHandler(MailData_event); // 非同期処理開始 MailData.ReadBodyAnsync(); return tcs.Task; } // 本来のイベント処理 private void MailData_event(object sender, EventArgs e) { IMailData MailData = (IMailData)sender; // イベント削除 MailData.BodyLoaded -= new EventHandler(MailData_event); // 本文無し( 本文が必要な場合は、false で、reader.MainText ) MailReader reader = new MailReader(MailData.DataStream, true); // **************************** // 戻り値用のクラスの戻り値をセット // **************************** tcs.TrySetResult(reader); } // ********************************************* // 戻り値を返す事のできる非同期処理 【終了】 // ********************************************* // ダブルクリック private void dataGrid1_MouseDoubleClick(object sender, MouseButtonEventArgs e) { Debug.WriteLine(dataGrid1.SelectedIndex); } } }
参考ページ c# - Best way to convert callback-based async method to awaitable task - Stack Overflow c# - TaskCompletionSource : When to use SetResult() versus TrySetResult(), etc - Stack Overflow 関連するページ ▼ このページの処理の元となった、通常のイベント非同期処理 WPF(C#) : 『DataGrid に、TKMP.DLL を使用して非同期にメールヘッダを受信する』 テンプレート
2013年10月29日
Framework4(C#) : WebClient で Post と Get する汎用 static クラス
テストしたのは、Windows7 + Visual Studio 2010 + WPF アプリケーションです。 Web アクセスには、UrlEncode が必要になりますが、HttpUtility.UrlEncode はプロジェクトのプロパティで、『.NET Framework 4』にして System.Web を参照します。最初にプロジェクトを作成した場合、『.NET Framework 4 Client Profile』にセットされている場合があり、その場合 System.Web を参照できません。 HttpUtility.UrlEncode は、Windows Phone(OS 7.1) にもある事はあるのですが、エンコード結果が UTF-8 系列しか指定できないので、Encoding を指定する第二引数がありません。参照も、System.Windows の中の System.Net にあります。ですから、このクラスは Windows Phone(OS 7.1) では使用できません。 %エンコーディングの仕様が違いますが、WEB へ送信する場合は、Uri.EscapeDataString でも使用可能で、Twitter への投稿ではこちら(RFC 3986)が良いと思いますが、文字列は UTF-8 に変換されてエンコーディングされます。
using System; using System.Collections.Generic; using System.Net; using System.Text; using System.Web; namespace winofsql { class Tool { public static string serverEncoding = "utf-8"; // ********************************************* // UTF-8 POST // ********************************************* public static string Post(string url, Dictionary<string, string> param, UploadStringCompletedEventHandler UploadStringCompleted=null) { string result = ""; try { WebClient webClient = new WebClient(); webClient.Encoding = Encoding.GetEncoding(serverEncoding); if ( UploadStringCompleted != null ) { // 呼び出し側でラムダ式を使う事を想定した // イベント登録 webClient.UploadStringCompleted += UploadStringCompleted; } webClient.Headers["Content-Type"] = "application/x-www-form-urlencoded"; string data_string = ""; foreach (KeyValuePair<string, string> kvp in param) { if (data_string != "") { data_string += "&"; } data_string += kvp.Key + "="; // ******************************************************* // プロジェクトのプロパティの対象フレームワークを //『Framework 4』 にして System.Web を参照します // ******************************************************* data_string += HttpUtility.UrlEncode(kvp.Value, Encoding.UTF8); } webClient.UploadStringAsync(new Uri(url), "POST", data_string); } catch (Exception Err) { result = "ERROR: " + Err.Message; } return result; } // ********************************************* // エンコード指定 POST // ********************************************* public static string Post(string url, string encoding, Dictionary<string, string> param, UploadStringCompletedEventHandler UploadStringCompleted=null) { string result = ""; try { WebClient webClient = new WebClient(); webClient.Encoding = Encoding.GetEncoding(serverEncoding); if ( UploadStringCompleted != null ) { // 呼び出し側でラムダ式を使う事を想定した // イベント登録 webClient.UploadStringCompleted += UploadStringCompleted; } webClient.Headers["Content-Type"] = "application/x-www-form-urlencoded"; string data_string = ""; foreach (KeyValuePair<string, string> kvp in param) { if (data_string != "") { data_string += "&"; } data_string += kvp.Key + "="; // ******************************************************* // プロジェクトのプロパティの対象のフレームワークを //『.NET Framework 4』 にして System.Web を参照します // ******************************************************* data_string += HttpUtility.UrlEncode(kvp.Value, Encoding.GetEncoding(encoding)); } webClient.UploadStringAsync(new Uri(url), "POST", data_string); } catch (Exception Err) { result = "ERROR: " + Err.Message; } return result; } // ********************************************* // URL のみ呼び出し GET // ********************************************* public static string Get( string url, DownloadStringCompletedEventHandler DownloadStringCompleted=null) { string result = ""; try { WebClient webClient = new WebClient(); webClient.Encoding = Encoding.GetEncoding(serverEncoding); if ( DownloadStringCompleted != null ) { // 呼び出し側でラムダ式を使う事を想定した // イベント登録 webClient.DownloadStringCompleted += DownloadStringCompleted; } webClient.DownloadStringAsync(new Uri(url)); } catch (Exception Err) { result = "ERROR: " + Err.Message; } return result; } // ********************************************* // データ呼び出し( UTF-8 ) GET // ********************************************* public static string Get( string url, Dictionary<string, string> param, DownloadStringCompletedEventHandler DownloadStringCompleted=null) { string result = ""; try { WebClient webClient = new WebClient(); webClient.Encoding = Encoding.GetEncoding(serverEncoding); if (DownloadStringCompleted != null) { // 呼び出し側でラムダ式を使う事を想定した // イベント登録 webClient.DownloadStringCompleted += DownloadStringCompleted; } string data_string = ""; foreach (KeyValuePair<string, string> kvp in param) { if (data_string != "") { data_string += "&"; } data_string += kvp.Key + "="; // ******************************************************* // プロジェクトのプロパティの対象フレームワークを //『Framework 4』 にして System.Web を参照します // ******************************************************* data_string += HttpUtility.UrlEncode(kvp.Value, Encoding.UTF8); } webClient.DownloadStringAsync(new Uri(url + "?" + data_string)); } catch (Exception Err) { result = "ERROR: " + Err.Message; } return result; } // ********************************************* // データ呼び出し( エンコード指定 ) GET // ********************************************* public static string Get( string url, string encoding, Dictionary<string, string> param, DownloadStringCompletedEventHandler DownloadStringCompleted=null) { string result = ""; try { WebClient webClient = new WebClient(); webClient.Encoding = Encoding.GetEncoding(serverEncoding); if (DownloadStringCompleted != null) { // 呼び出し側でラムダ式を使う事を想定した // イベント登録 webClient.DownloadStringCompleted += DownloadStringCompleted; } string data_string = ""; foreach (KeyValuePair<string, string> kvp in param) { if (data_string != "") { data_string += "&"; } data_string += kvp.Key + "="; // ******************************************************* // プロジェクトのプロパティの対象フレームワークを //『Framework 4』 にして System.Web を参照します // ******************************************************* data_string += HttpUtility.UrlEncode(kvp.Value, Encoding.GetEncoding(encoding)); } webClient.DownloadStringAsync(new Uri(url + "?" + data_string)); } catch (Exception Err) { result = "ERROR: " + Err.Message; } return result; } } }
使用方法のサンプル
Tool.serverEncoding = "utf-8"; Tool.Post("http://student.xii.jp/lightbox/php_sample_1/log.php", "utf-8", new Dictionary<string, string>() { { "send", "ok" }, { "text", "日本語" } }, (object _sender, UploadStringCompletedEventArgs _e) => { // Error プロパティがセットされているとエラー if (_e.Error != null) { // エラー処理 } // 正常な結果 else { // 正常処理 } });
関連する記事 Framework4.5(C#)ストア : HttpClient で Post と Get する汎用 static クラス Framework4(C#) : Windows Phone OS 7.1 : WebClient で Post と Get する汎用 static クラス Android で Post と Get いろいろな言語におけるバーセントエンコーディング
2013年10月27日
WPF(C#) : 『DataGrid に、TKMP.DLL を使用して非同期にメールヘッダを受信する』 テンプレート
SkyDrive へ移動 メールサーバーは GMail の IMAP を使用してテストしています MainWindow.xaml.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.Net; using System.Windows; using System.Windows.Input; using System.Collections.ObjectModel; using TKMP.Net; using System.IO; using TKMP.Reader; using System.Threading; namespace WPF_DataGrid1 { public partial class MainWindow : Window { private MainViewModel mvm = new MainViewModel(); private SynchronizationContext sc = null; private ImapClient client = null; private int endCounter = 0; // ********************************************* // コンストラクタ // ********************************************* public MainWindow() { InitializeComponent(); this.dataGrid1.DataContext = mvm; } // 接続および、メールヘッダの取得 private void actButton_Click(object sender, RoutedEventArgs e) { mvm.mail_data.Clear(); BasicImapLogon logon = new BasicImapLogon(this.userName.Text, this.password.Password ); client = new ImapClient(logon, "imap.gmail.com", 993); client.AuthenticationProtocol = AuthenticationProtocols.SSL; if (!client.Connect()) { return; } // メールデータ一覧を格納するオブジェクト IMailData[] md = client.GetMailList(); // データがありません if (md == null) { return; } // メールデータの数 mailCount.Text = md.Length.ToString(); // 読込み制限 int maxCount = 20; int idx = 0; // 接続解除用 endCounter = 0; // 非同期で全て表示 foreach( var data in md ) { idx++; if (idx > maxCount) { break; } // 個別にイベント登録 data.BodyLoaded += new EventHandler(MailData_BodyLoaded); // 非同期処理開始 data.ReadBodyAnsync(); } } private void MailData_BodyLoaded(object sender, EventArgs e) { IMailData MailData = (IMailData)sender; // 本文無し( 本文が必要な場合は、false で、reader.MainText ) MailReader reader = new MailReader(MailData.DataStream, true); //Console.WriteLine(reader.MainText); // UI スレッドへの処理( この場合、post_state は null ) sc.Post((object post_state) => { string from = ""; string subject = ""; string mdate = ""; // ヘッダの一覧より、目的のヘッダを探す foreach (TKMP.Reader.Header.HeaderString headerdata in reader.HeaderCollection) { if (headerdata.Name == "From") { from = headerdata.Data; } if (headerdata.Name == "Subject") { subject = headerdata.Data; } if (headerdata.Name == "Date") { mdate = headerdata.Data; } } // 行追加 mvm.mail_data.Add(new ItemViewModel() { from = from, subject = subject, mdate = mdate }); }, null); // イベント削除 MailData.BodyLoaded -= new EventHandler(MailData_BodyLoaded); // 接続解除用 endCounter++; if (endCounter == 20) { client.Close(); } } // UI スレッドへの処理用 private void Window_Loaded(object sender, RoutedEventArgs e) { sc = SynchronizationContext.Current; } // ダブルクリック private void dataGrid1_MouseDoubleClick(object sender, MouseButtonEventArgs e) { Debug.WriteLine(dataGrid1.SelectedIndex); } } }
非同期でのサーバーとの通信処理はすべてキューとして扱われるため、 前の処理が終了する前に次の要求を発行すると、要求順に順次処理が行われます関連する記事 WPF(C#) : 『DataGrid に、バインド用クラスを使って自動的にカラムと行を生成する』 テンプレート
WPF(C#) : 『DataGrid に、バインド用クラスを使って自動的にカラムと行を生成する』 テンプレート
SkyDrive へ移動 ※ Window の背景にイラスト画像を使用しています DataGrid の重要なプロパティItemsSource="{Binding mail_data}" AutoGenerateColumns="True" CanUserAddRows="false"MainWindow.xaml
<Window x:Class="WPF_MailGet.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Title="MainWindow" Height="719" Width="805" BorderBrush="Black" BorderThickness="1"> <Window.Background> <ImageBrush ImageSource="/WPF_MailGet;component/Images/back_001.jpg" Stretch="UniformToFill" TileMode="None" /> </Window.Background> <!-- このウインドウのリソース --> <Window.Resources> <Style TargetType="{x:Type ListView}"> <Setter Property="ItemContainerStyle"> <Setter.Value> <Style TargetType="ListViewItem"> <Setter Property="VerticalContentAlignment" Value="Top" /> </Style> </Setter.Value> </Setter> </Style> <!-- 一覧データ 用の表示部品定義 --> <DataTemplate x:Key="ListFromTemplate"> <StackPanel HorizontalAlignment="Left" Width="60"> <TextBlock Text="{Binding from}" TextWrapping="Wrap" Padding="4" /> </StackPanel> </DataTemplate> <DataTemplate x:Key="ListSubjectTemplate"> <StackPanel Margin="0,0,0,17" Width="250"> <Border BorderThickness="1" BorderBrush="Black" Padding="4" Height="50"> <TextBlock Text="{Binding subject}" TextWrapping="Wrap" /> </Border> </StackPanel> </DataTemplate> </Window.Resources> <Grid AllowDrop="True"> <Grid.RowDefinitions> <RowDefinition Height="70*" /> <RowDefinition Height="608*" /> </Grid.RowDefinitions> <!--実行ボタン--> <Button Name="actButton" Content="データ表示" Height="35" HorizontalAlignment="Left" Margin="40,26,0,0" VerticalAlignment="Top" Width="154" Click="actButton_Click" /> <!--一覧表示--> <DataGrid Grid.Row="1" Height="534" HorizontalAlignment="Left" Margin="40,0,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="700" Background="#C5FFFFFF" MouseDoubleClick="dataGrid1_MouseDoubleClick" ItemsSource="{Binding mail_data}" AutoGenerateColumns="True" CanUserAddRows="false" IsReadOnly="True"> <!--<DataGrid.Columns> <DataGridTemplateColumn Header="差出人" CellTemplate="{StaticResource ListFromTemplate}"> </DataGridTemplateColumn> <DataGridTemplateColumn Header="件名" CellTemplate="{StaticResource ListSubjectTemplate}"> </DataGridTemplateColumn> </DataGrid.Columns>--> </DataGrid> </Grid> </Window>
コメント部分は、カラムをテンプレートで作成してカスタマイズする場合に使用します。 MainWindow.xaml.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.Net; using System.Windows; using System.Windows.Input; using System.Collections.ObjectModel; namespace WPF_MailGet { public partial class MainWindow : Window { private MainViewModel mvm = new MainViewModel(); // ********************************************* // コンストラクタ // ********************************************* public MainWindow() { InitializeComponent(); this.dataGrid1.DataContext = mvm; } private void actButton_Click(object sender, RoutedEventArgs e) { mvm.mail_data.Add(new ItemViewModel() { from = "Button@event.DataGrid.wpf", subject = "DataGrid は自動カラム作成が有効です", mdate = "2013/10/26 19:00:30", }); } private void dataGrid1_MouseDoubleClick(object sender, MouseButtonEventArgs e) { Debug.WriteLine(dataGrid1.SelectedIndex); mvm.mail_data.Add(new ItemViewModel() { from = "DoubleClick@event.DataGrid.wpf", subject = "CanUserAddRowsは、\n意図的に false にする必要があります", mdate = "2013/10/26 19:00:30", }); mvm.mail_data[0].mdate = "0000/00/00"; } } }
MainViewModel.cs
using System; using System.ComponentModel; using System.Collections.ObjectModel; namespace WPF_MailGet { // ********************************************* // バインドする一覧データのを定義するクラス // ********************************************* public class MainViewModel : MainBaseModel { // ***************************************************** // コンストラクタ // ***************************************************** public MainViewModel() { mail_data = new ObservableCollection<ItemViewModel>(); } // ***************************************************** // バインド用のコレクションのプロパティ // ***************************************************** public ObservableCollection<ItemViewModel> mail_data { get; set; } } }
ItemViewModel.cs
using System; using System.ComponentModel; namespace WPF_MailGet { // ********************************************* // バインドする一覧データの構造を定義するクラス // ********************************************* public class ItemViewModel : ItemBaseModel { // ********************************************* // from エントリ // ********************************************* private string _from; public string from { get { return _from; } set { SetAndNotifyString(GetName(() => from), ref _from, value); } } // ********************************************* // subject エントリ // ********************************************* private string _subject; public string subject { get { return _subject; } set { SetAndNotifyString(GetName(() => subject), ref _subject, value); } } // ********************************************* // mdate エントリ // ********************************************* private string _mdate; public string mdate { get { return _mdate; } set { SetAndNotifyString(GetName(() => mdate), ref _mdate, value); } } } }
2013年10月18日
VS2010(C#) : データベースを System.Data.Linq.DataContext で読み込んでからの LINQ
標準クエリ演算子の概要
標準クエリ演算子は、統合言語クエリ (LINQ) パターンを形成するメソッドです。 ほとんどの場合、そのメソッドの操作の対象はシーケンスです。シーケンスとは、IEnumerable インターフェイスまたは IQueryable インターフェイスを実装している型を持つオブジェクトのことです 。 標準クエリ演算子には、クエリ機能が用意されています。たとえば、フィルター処理、射影、集計、並べ替えなどです。
データベースへのアクセスは SQL で実行しておいて、メモリにあるデータに対して LINQ を使用します。文字列の比較の為に、メソッドを一つ追加しています。
変数名に日本語を使用しているのは、テーブルの列定義が日本語の為です。変数を 英数字にするには、SELECT 構文で列に別名を付けて、格納用のクラスをその名前にします。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Data.Odbc; using System.Data.Linq; using System.Data; namespace DataContextTest { class Program { static void Main(string[] args) { OdbcConnection cn = null; string cs = "Driver={MySQL ODBC 5.2w Driver};" + "Server=localhost;" + "Database=lightbox;" + "Uid=root;" + "Pwd=password;"; try { cn = new OdbcConnection(cs); // System.Data.Linq を参照 DataContext context = new DataContext(cn); // select * from 社員マスタ で動作します string query = String.Format("select 社員コード,氏名 from 社員マスタ"); // シーケンスとは、IEnumerable<T> インターフェイスまたは // IQueryable<T> インターフェイスを実装している型を持つオブジェクト IEnumerable<Syain> tables = context.ExecuteQuery<Syain>(query); var q = from s in tables where comp(true,s.社員コード,"0005") select s; foreach (var s in q) { Console.Write("{0}, {1}\n", s.社員コード, s.氏名); } } catch (Exception ex) { Console.WriteLine(ex.Message); } if (cn.State == ConnectionState.Open) { cn.Close(); cn.Dispose(); } Console.ReadLine(); } // 文字列比較用 public static bool comp(bool type,string a, string b) { if (a == b) { return true; } if (a.CompareTo(b) < 0) { return !type; } else { return type; } } private class Syain { public string @社員コード { get; set; } public string @氏名 { get; set; } } } }
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。 Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。 また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。 ※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです 対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。 ※ エキスパートモードで表示しています アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります<% if:page_name eq 'archive' -%> アーカイブページでのみ表示される内容 <% /if %> <% if:page_name eq 'category' -%> カテゴリページでのみ表示される内容 <% /if %> <% if:page_name eq 'tag' -%> タグページでのみ表示される内容 <% /if %>この記述は、以下の場所で使用します
|