こんにちは、SI部の藤沢です。
前回(LinuxでもC#プログラミング(導入編)) にMonoのインストールを行ったので、簡単なGUIアプリケーションの作成をします。
今回のアプリケーションは、社員簿(ID、名前、メールアドレス)で下記を要件とします。
- データの一覧表示が行える。
- 名前で検索が行える。
- 新規登録・編集登録(保存先はXMLファイル)が行える。
作成
まずは、MonoDevelopを起動し、「ファイル」-「新規」-「ソリューションを作成する」からソリューションを作成します。テンプレートは「.Net」-「Gtk# 2.0 プロジェクト」を選択します。プロジェクト名、ソリューション名、保存先を入力します。
プロジェクトの作成が行えたら、XMLファイルを扱うため参照に「System.Xml」を追加しておきます。
社員情報XML
まずは、社員情報のXMLの操作を行うクラスから作成していきます。ソリューションから新しいファイルの作成で「空のクラス」を選択します。
今回は、EmployeesModelでXMLの読書を行います。EmployeeModelが社員1名の情報になります。
using EmployeesBook; using System.Collections.Generic; using System.IO; using System.Text; using System; using System.Xml; using System.Xml.Serialization; /// <summary> /// 社員情報XMLクラス /// </summary> namespace EmployeesBook { [XmlRoot("Employees")] public class EmployeesModel { [XmlElement("Employee")] public List<EmployeeModel> Models { get; set; } /// <summary> /// 社員情報XMLファイル名 /// </summary> private static readonly string filename = "Employee.xml"; /// <summary> /// XMLを読み込みます。 /// </summary> public static EmployeesModel Read() { var xml = Path.Combine (Directory.GetCurrentDirectory (), filename); EmployeesModel models = new EmployeesModel (); using (var fs = new FileStream (@xml, FileMode.Open)) { var serializer = new XmlSerializer (typeof(EmployeesModel)); models = (EmployeesModel)serializer.Deserialize (fs); } return models; } /// <summary> /// XMLに書き込みます。 /// </summary> /// <param name="value">Value.</param> public static void Write(EmployeesModel value) { var xml = Path.Combine (Directory.GetCurrentDirectory (), filename); using (var fs = new FileStream (@xml, FileMode.Create)) using (var sw = new StreamWriter (fs, Encoding.UTF8)) { var ns = new XmlSerializerNamespaces (); ns.Add (string.Empty, string.Empty); var serializer = new XmlSerializer (typeof(EmployeesModel)); serializer.Serialize (sw, value, ns); } } } /// <summary> /// 社員情報(1レコード)のクラスです /// </summary> public class EmployeeModel { [XmlElement("ID")] public string ID{ get; set; } [XmlElement("Name")] public string Name{ get; set; } [XmlElement("Mail")] public string Mail{ get; set; } #region コンストラクタ /// <summary> /// コンストラクタ /// </summary> public EmployeeModel() : this(string.Empty,string.Empty,string.Empty) { } /// <summary> /// コンストラクタ /// </summary> /// <param name="id">Identifier.</param> /// <param name="name">Name.</param> /// <param name="mail">Mail.</param> public EmployeeModel(string id,string name,string mail) { ID = id; Name = name; Mail = mail; } #endregion /// <summary> /// オブジェクトが初期値か否か? /// </summary> /// <returns><c>true</c> if this instance is empty; otherwise, <c>false</c>.</returns> public bool IsEmpty() { return string.IsNullOrEmpty (ID + Name + Mail); } /// <summary> /// Returns a <see cref="System.String"/> that represents the current <see cref="EmployeesBook.EmployeeModel"/>. /// </summary> /// <returns>A <see cref="System.String"/> that represents the current <see cref="EmployeesBook.EmployeeModel"/>.</returns> public override string ToString () { return string.Format ("[EmployeeModel: ID={0}, Name={1}, Mail={2}]", ID, Name, Mail); } } }
初期データとして社員情報XMLを作成します。ファイル名は「EmployeesModelのfilename」で設定した名称になります。
また、ファイルの出力設定を行います。
<?xml version="1.0" encoding="UTF-8" ?> <Employees> <Employee> <ID>00001</ID> <Name>Casley Taro</Name> <Mail>taro@casley.com</Mail> </Employee> <Employee> <ID>00002</ID> <Name>Casley Jiro</Name> <Mail>jiro@casley.com</Mail> </Employee> <Employee> <ID>00003</ID> <Name>Casley Hanako</Name> <Mail>hanako@casley.com</Mail> </Employee> </Employees>
メイン画面
続いて、メイン画面を作成します。「MainWindow.cs」を開き画面の作成を行います。各widget下記のような配置になります。
・MainWindow └ boxMain(VBox) ├ boxSearch(HBox) │ ├ lblSearch(Label) │ ├ txtSearch(Entry) │ │ OnTxtSearchChanged(Change Event) │ └ btnNew(Button) └ GtkSerolledWindow └ viewEmployees(Node View)
using Gtk; using System.Linq; using System; using EmployeesBook; /// <summary> /// メインウィドウ /// </summary> public partial class MainWindow: Gtk.Window { #region メンバ変数 // フィルタ TreeModelFilter modelFilter; #endregion /// <summary> /// コンストラクタ /// </summary> public MainWindow () : base (Gtk.WindowType.Toplevel) { Build (); initTreeView (); initData (); } /// <summary> /// Raises the delete event event. /// </summary> /// <param name="sender">Sender.</param> /// <param name="a">The alpha component.</param> protected void OnDeleteEvent (object sender, DeleteEventArgs a) { Application.Quit (); a.RetVal = true; } #region 初期化処理 /// <summary> /// TreeViewの初期化処理をします。 /// </summary> private void initTreeView() { // 各カラム var colId = new TreeViewColumn (); colId.Title = "ID"; var colName = new TreeViewColumn (); colName.Title = "名前"; var colMail = new TreeViewColumn (); colMail.Title = "メールアドレス"; // 各セル var cellId = new CellRendererText(); colId.PackStart (cellId, true); colId.AddAttribute (cellId, "text", 0); var cellName = new CellRendererText (); colName.PackStart (cellName, true); colName.AddAttribute (cellName, "text", 1); var cellMail = new CellRendererText (); colMail.PackStart (cellMail, true); colMail.AddAttribute (cellMail, "text", 2); viewEmployees.AppendColumn (colId); viewEmployees.AppendColumn (colName); viewEmployees.AppendColumn (colMail); colId.SetCellDataFunc (cellId, new TreeCellDataFunc ( delegate(TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter) { var emp = (EmployeeModel)tree_model.GetValue(iter, 0); (cell as CellRendererText).Text = emp.ID; })); colName.SetCellDataFunc (cellName, new TreeCellDataFunc ( delegate(TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter) { var emp = (EmployeeModel)tree_model.GetValue(iter, 0); (cell as CellRendererText).Text = emp.Name; })); colMail.SetCellDataFunc (cellMail, new TreeCellDataFunc ( delegate(TreeViewColumn tree_column, CellRenderer cell, TreeModel tree_model, TreeIter iter) { var emp = (EmployeeModel)tree_model.GetValue(iter, 0); (cell as CellRendererText).Text = emp.Mail; })); viewEmployees.CursorChanged += (object sender, EventArgs e) => { var selection = (sender as TreeView).Selection; TreeModel model; TreeIter iter; if(selection.GetSelected(out model, out iter)) { var emp = (EmployeeModel)model.GetValue(iter, 0); showEditDialog(emp); } }; } /// <summary> /// データの初期を行う。 /// </summary> private void initData() { var store = new TreeStore (typeof(EmployeeModel)); foreach (var emp in EmployeesModel.Read().Models.OrderBy(o => o.ID)) { store.AppendValues (emp); } modelFilter = new TreeModelFilter (store, null); modelFilter.VisibleFunc = new TreeModelFilterVisibleFunc ( delegate(TreeModel model, TreeIter iter) { var emp = (EmployeeModel)model.GetValue(iter, 0); if(string.IsNullOrEmpty(txtSearch.Text)) { return true; } if(emp.Name.IndexOf(txtSearch.Text) > -1) { return true; } else { return false; } }); viewEmployees.Model = modelFilter; } #endregion #region 編集画面 /// <summary> /// 編集画面を表示します。 /// </summary> /// <param name="employee">Employee.</param> private void showEditDialog(EmployeeModel employee) { var edit = new EmployeeDialog (employee); edit.Run (); edit.Destroy (); initData (); } /// <summary> /// 検索をします。 /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">E.</param> protected void OnTxtSearchChanged (object sender, EventArgs e) { modelFilter.Refilter (); } /// <summary> /// 新規登録ボタン処理をします。 /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">E.</param> protected void OnBtnNewClicked (object sender, EventArgs e) { showEditDialog (new EmployeeModel ()); } #endregion }
編集ダイアログ
続いて編集ダイアログを作成します。新しいダイアログの作成から、「ダイアログ」を選択します。
MainWindowと同様にwidgetは下記のようになります。
・EmployeesBook.EmplyeeDialog ├ boxMain(VBox) │ └ tableDetail(Table) │ ├ lblTitle(Label,RightAttach:2) │ ├ lblID(Label) │ ├ txtID(Entry) │ ├ lblName(Label) │ ├ txtName(Entry) │ ├ lblMail(Label) │ └ txtMail(Entry) └ ActionArea(HButtonBox) ├ btnCancel(Button) └ btnOk(Button)
using System; using System.Linq; using Gtk; /// <summary> /// 編集ダイアログ /// </summary> namespace EmployeesBook { /// <summary> /// 編集ダイアログクラス /// </summary> public partial class EmployeeDialog : Gtk.Dialog { #region メンバ変数 /// <summary> /// 編集中か? /// </summary> private bool editing = false; #endregion /// <summary> /// コンストラクタ /// </summary> public EmployeeDialog (EmployeeModel employess) { this.Build (); initData (employess); } #region 初期化 /// <summary> /// 画面の初期化をします。 /// </summary> /// <param name="employee">Employee.</param> private void initData(EmployeeModel employee) { txtID.Text = employee.ID; txtName.Text = employee.Name; txtMail.Text = employee.Mail; if (!employee.IsEmpty ()) { lblTitle.Text = "編集登録"; txtID.IsEditable = false; txtID.CanFocus = false; txtName.IsFocus = true; editing = true; } } #endregion /// <summary> /// 登録できるか? /// </summary> /// <returns><c>true</c>登録可能<c>false</c> 登録不可</returns> private bool canRegister() { if (string.IsNullOrEmpty (txtID.Text)) { showErrorMeesage ("IDを入力してください。"); return false; } if (!editing && EmployeesModel.Read ().Models.Any (w => w.ID.Equals (txtID.Text))) { showErrorMeesage ("IDは登録されています。"); return false; } if (string.IsNullOrEmpty (txtName.Text)) { showErrorMeesage ("名前を入力してください。"); return false; } if (string.IsNullOrEmpty (txtMail.Text)) { showErrorMeesage ("メールアドレスを入力してください。"); return false; } return true; } /// <summary> /// エラーメッセージを表示します。 /// </summary> /// <param name="msg">Message.</param> private void showErrorMeesage(string msg) { var dialog = new MessageDialog (this, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, msg); dialog.Run (); dialog.Destroy (); } /// <summary> /// データを登録します。 /// </summary> private void register() { var emp = new EmployeeModel (); emp.ID = txtID.Text; emp.Name = txtName.Text; emp.Mail = txtMail.Text; var employees = EmployeesModel.Read (); var find = employees.Models.FirstOrDefault (w => w.ID.Equals (emp.ID)); if (find != null) { find.Name = emp.Name; find.Mail = emp.Mail; } else { employees.Models.Add (emp); } EmployeesModel.Write (employees); } /// <summary> /// OKボタン処理をします。 /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">E.</param> protected void OnBtnOkClicked (object sender, EventArgs e) { if (canRegister ()) { register (); OnClose (); } } /// <summary> /// キャンセルボタン処理をします。 /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">E.</param> protected void OnBtnCancelClicked (object sender, EventArgs e) { OnClose (); } } }
以上でソースの作成が完了しました。
実行
さいごに
前回の続きでC#を使用してLinuxのGUIアプリケーションを作成してみました。
WindowsFormと似ているため、WindowsFormを作ったことのある人ならば、すぐに理解が出来たかと思います。
LinuxでGUIアプリケーションを作成する機会は少ないですが、.Netを使用してる方は試してみてください。
最後までご高覧頂きまして有難うございます。