気楽なソフト工房

プログラミングについていろいろな記事を書いています。



mykonos2008

Author:mykonos2008
システムエンジニアとして働いている30代の会社員です。
仕事や趣味でプログラムを書いている方の役に立つ記事を書いていきたいと思っています。
ご意見、ご感想はこちらまで
If you are an english speaker,Please visit my english blog.

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
随分、久しぶりにC#のアプリを開発していて、ふと思い出したのですが、以前の記事で、
アプリケーションの設定をオブジェクトのシリアライズを使用して、XMLファイルに保存する方法を紹介しました。

現在、ちょっとしたアプリをWPFで開発していて、TextBoxなどに入力された内容を設定ファイルに保存しておきたいなと思って、
オブジェクトのシリアライズの方法でやろうかと思ったのですが、もっと簡単な方法が無いかと調べました。
もう、皆さんご存知だと思いますが、Properties.Settings.Defaultを使用する方法です。

ソリューションエクスプローラでプロジェクトのプロパティを選んで、「設定」を選択します。
そこで以下の画面から、アプリケーション内で、使用できるプロパティを登録することが出来ます。


スコープには、「アプリケーション」と「ユーザ」を指定できますが、
今回のようにユーザの入力内容を保存したりしたい場合は、ユーザを選択します。
値は、それぞれの項目のデフォルト値になります。一度ユーザが入力した内容を
保存した場合は、その値で上書きされます。
WPFではXAMLからBindingを利用してこれらのプロパティを利用することが出来ます。

<Window x:Class="Test.MainWindow"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:p="clr-namespace:Test.Properties" ・・これ、忘れずに。(Testのところはプロジェクトの名前空間)
   Title="MainWindow" Height="429" Width="619"
   Closing="Window_Closing"/>
<TextBox Text="{Binding Source={x:Static p:Settings.Default}, 
          Path=User, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />

ModeにTwoWay、またはOneWayToSourceを設定することで、ユーザが入力した内容を
プロパティに反映することが出来ます。
※デフォルトの動作では、コントロールがフォーカスを失った時に、反映されます。

最後に、ウィンドウが閉じる時にプロパティの内容をローカルファイルに保存する処理を記述します。

  private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
  {
     Properties.Settings.Default.Save();            
  }
スポンサーサイト
本日は、C#からIEの履歴にアクセスするためのTipsを紹介します。

IEの履歴にはCOMインターフェース「IUrlHistory」を使用すると、アクセスできます。
このコンポーネントからIEの履歴の一覧を取得したり、履歴を削除(正確にはクリア)したり、
履歴を追加したり(あまり使わないと思いますが)することが出来ます。

そして、大変すばらしいことに「IUrlHistory」のC#用Wrapperクラスを
提供してくれている方がいます。

「IUrlHistory」のC#用Wrapperのダウンロード

Wrapperライブラリはソースコード形式で提供されていますので、
ダウンロードしたファイルを展開して、「UrlHistoryLibrary」プロジェクトをビルドします。

そして、生成されたアセンブリ「UrlHistoryLibrary.dll」を、利用するプロジェクトから参照設定すれば
Wrapperライブラリを使用できるようになります。

以下は、履歴の一覧を取得するコードです。
using UrlHistoryLibrary; //Wrapperライブラリ

・・・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・・・
    public partial class MainWindow : Window
    {
        private UrlHistoryWrapperClass urlHistory;

        public MainWindow()
        {
            //Wrapperクラスをインスタンス化
            urlHistory = new UrlHistoryWrapperClass();
        }

・・・・・・・・・・・・・・・・・・
        public  List<STATURL> GetHistoryList()
        {
            List<STATURL> historyList = new List<STATURL>();

            //履歴の一覧の列挙を取得
            UrlHistoryWrapperClass.STATURLEnumerator enumerator
                                         = urlHistory.GetEnumerator();

            //1件ずつ列挙する
            while (enumerator.MoveNext())
            {
                historyList.Add(enumerator.Current);
            }

            return historyList;
        }
    }

「STATURL」は1つの1つの履歴レコードの情報を管理する構造体オブジェクトで、
この構造体から履歴のURLやTitle、最終訪問日などの情報にアクセスすることが出来ます。

次に履歴の削除について、紹介します。
履歴の削除には、ClearHistory()を使用してユーザの全ての
履歴を削除してしまう方法と、DeleteUrl(String url)を使用して
指定された履歴のみを削除する方法があります。

後者のDeleteUrlの方が使い勝手が良さそうなのですが、
以下のページにも記述されている通り、このメソッドでは、
IEの履歴フォルダーのエントリーを削除することが出来ません。

http://support.microsoft.com/kb/327569/ja

実質的には「DeleteUrl(String url)」は役に立たないメソッドのようです。

Visual Studioで開発をしていて、クラスやコントロールを新規追加すると、
たくさんのusing句が挿入された状態でファイルが作成されます。

そして、コーディングを終えてそれらのusing句は使わないで終わってしまうケースも
結構あったりします。

Visual Studioにはこれら未使用のusing句を自動的に削除してくれる便利な機能が備わっていますが、
では、これをうっかりやり忘れたら何が起こるのでしょうか?

プログラムが余計なメモリを使用して遅くなったりするのでしょうか?
ふと気になって調べて見ました。

まず、結論から言いますと、「未使用のusing句」は実行時には何の影響も与えません。

不要なusing句が含まれていようと含まれていなくても、コンパイルの結果、生成されるアセンブリに違いはありません。
コンパイルの結果、生成される中間コードにはusing句に該当する部分は全く生成されないからです。
ですので、不要なusing句があるからと言って、プログラムのパフォーマンスが劣化したりすることはないのです。

では何故、不要なusing句を削除した方が良いのでしょうか?

いくつか理由があります。

まず、1つ目は、当たり前過ぎるのですが、不要なusing句が無いほうがコードが見易いということです。
不要なusing句が無ければそのコードがどのNamespaceを使用しているか一見して分かるようになります。

2つ目は、コーディング時にインテリセンスが提示してくる型の候補が減ることで、
早く目的の型を発見できるようになることです。これも些細な事ですが、
たくさんコーディングする時は結構、大事かもしれません。

3つ目はコンパイルが早くなることです。コンパイラが型解決をするのに不要な名前空間を探しにいくと、
その分、遅くなってしまいます。これについてはよほどソースの数が多くないと影響が無いと思いますが。。

私の主観では1つ目と2つ目は結構大きい理由になるかなと思います。

ただ、最初に言いましたように不要なusing句が有っても実行時には差が出ないので、
消し忘れたからと言って、後から悩む必要は無いと思います。

C#でWindowsのアプリケーションを開発する際、ユーザが行ったアプリケーションの設定をどのように保存するのか
ということについて頭を悩ませることがあると思います。

本日はアクセス解析ソフト「Crete」を開発した際に使用したアプリケーション設定を保存する方法を紹介します。

「Crete」で使用したアプリケーション設定保存のテクニックは、アプリケーションの設定情報を管理するクラスのオブジェクトを
XMLにシリアライズし、ファイルに保存するといったものです。

それでは少しずつコードを見ていきます。アプリケーション設定を保存するクラスはSingletonパターンで作成します。
こうすることでアプリケーションの様々な箇所から同一の設定クラスのインスタンスに簡単にアクセスすることが
出来るようになります。

[AppSettings.cs]
  public class AppSettings
  {
      private static AppSettings _appSettings = null;
      private List<String> _urls = null;

      //クラス内からしかインスタンス化できないようにする
      private AppSettings(){}

      public static AppSettings GetInstance()
      {
          if (_appSettings == null)
          {
              _appSettings = new AppSettings();
              _appSettings.Urls = new List();
          }

          return _appSettings;
      }

      //解析対象のページのURLのリストを管理するプロパティ
      public List<String> Urls
      {
          get { return _urls; }
          set { _urls = value; }
      }
  }

これだけでは、設定を保存したり、読み込んだりすることが出来ないので、それを行うためのメソッドをAppSettingsクラスに
追加します。

[AppSettings.cs]
  public class AppSettings
  {
      //設定をロードする
      public static void Load()
      {
          //ユーザ毎のアプリケーションデータディレクトリに保存する
          String appPath = String.Format("{0}\\{1}", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                                                        "\\Crete\\settings.xml");

          if(File.Exists(appPath))
          {
              XmlSerializer serializer = new XmlSerializer(typeof(AppSettings));

              using (FileStream stream = new FileStream(appPath, FileMode.Open))
              {
                  _appSettings = serializer.Deserialize(stream) as AppSettings;
              }
          }
      }

      //設定を保存する
      public static void Save()
      {
          String appPath = String.Format("{0}\\{1}", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                                                        "\\Crete\\settings.xml");

          XmlSerializer serializer = new XmlSerializer(typeof(AppSettings));

          using (FileStream stream = new FileStream(appPath,FileMode.Create))
          {
              serializer.Serialize(stream, _appSettings);
          }
      }
  }

クラスのインスタンスの保存、復元は「XmlSerializer」クラスを利用することで簡単に行うことが出来ます。
「XmlSerializer」はpublicなプロパティとフィールドが保存、復元の対象となります。詳しく解説しませんが、
独自に定義したクラスを型とするフィールドを保存することも可能です。

この設定クラスをアプリケーションから利用する際は以下のようにします。

まず、フォームのLoadイベントのハンドラで設定をロードします。

    public partial class MainForm : Form
    {
        protected override void OnLoad(EventArgs e)
        {
            ・・・・・・・・・・・・・

            //設定をロードする
            AppSettings.Load();
        }
    }

そして、設定を利用する箇所で、以下のようにして設定クラスのプロパティにアクセスします。


  String<String> urls = AppSettings.GetInstance().Urls;

  foreach (String url in urls)
  {
      _address.Items.Add(url);
  }

設定の保存は、フォームのClosingイベントで行っています。

    public partial class MainForm : Form
    {
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            try
            {
                //設定を保存する
                AppSettings.Save();
            }
            catch { }
        }
    }

この方法はWindowsアプリケーションを開発する際には私が頻繁に使用する方法なのですが、最大の利点は、
様々な種類のアプリケーション設定項目を設定クラスにそれに対応するプロパティを追加するだけで、
保存することが出来るという点です。

C#の標準APIには「DateTimePicker」などのようにコントロールとして、カレンダーを扱うクラスは有りますが、
データとしてカレンダーを扱うクラスはありません。(たぶん)

最近、Google Calendar APIを利用したカレンダーアプリケーションを作成しようとしているのですが、
そんなクラスが欲しいと思って作成しました。

このクラスで実行する処理は、与えられた日付を含む当月のカレンダーデータを作成するというものです。
通常のカレンダーでは、前月の最終週と当月の最初の週が同じ場合、その週に関しては前月の日付と当月の日付が
混ざった形になります。今回、作成したクラスでは、その場合の処理にも対応しています。

まず、カレンダーデータを管理するために、月を表す「CalendarMonth」、週を表す「CalendarWeek」、日を表す「CalendarDay」
という3つのクラスを作成しました。

[CalendarDay.cs]
    public class CalendarDate
    {
        public int Year
        {
            get;
            set;
        }

        public int Month
        {
            get;
            set;
        }

        public int Day
        {
            get;
            set;
        }
    }

[CalendarWeek.cs]
    public class CalendarWeek
    {
        private CalendarDay[] _days = null;

        public CalendarWeek(CalendarDay[] days)
        {
            _days = days;
        }

        public CalendarDay[] Days
        {
            get
            {
                return _days;
            }

            set
            {
                _days = value;
            }
        }
    }

[CalendarMonth.cs]
    public class CalendarMonth
    {
        public CalendarWeek[] Weeks
        {
            get;
            set;
        }
    }

CalendarMonthクラスがCalendarWeekクラスを、CalendarWeekクラスがCalendarDayクラスを所有する関係です。

そして、指定された日付を含むカレンダーデータを作成する処理が以下になります。

    public class Calendar
    {
        //引数はYYYY-MM-DD形式の文字列
        public CalendarMonth GetCalendarMonth(String date)
        {
            //月の週のリスト
            List weeks = new List();

            //日付を管理する変数
            int day = 1;

            //対象の日付オブジェクトを取得する
            DateTime target = DateTime.Parse(date);

            //当月の日数を求める
            int daysInMonth = DateTime.DaysInMonth(target.Year, target.Month);

            //当月1日の曜日を求める
            DateTime firstDay = target.AddDays(-(target.Day - 1));
            int dayOfWeek = (int)firstDay.DayOfWeek;

            //前月の最終日を取得する
            DateTime prevMonth = target.AddMonths(-1);
            int daysInPrevMonth = DateTime.DaysInMonth(prevMonth.Year, prevMonth.Month);

            //第一週のリストを作成する
            CalendarDay[] weekDays = new CalendarDay[7];

              //今月1日から週の終わりまでを配列にいれる
            for (int i = dayOfWeek; i < 7; i++,day++)
            {
                CalendarDay calDate = new CalendarDay();
                calDate.Year = target.Year;
                calDate.Month = target.Month;
                calDate.Day = day;
                weekDays[i] = calDate;
            }

            //前月月末から週の始めまでを配列に入れる
            for (int i = dayOfWeek - 1, x = daysInPrevMonth; i >= 0; i--, x--)
            {
                CalendarDay calDate = new CalendarDay();
                calDate.Year = prevMonth.Year;
                calDate.Month = prevMonth.Month;
                calDate.Day = x;
                weekDays[i] = calDate;
            }

            weeks.Add(new CalendarWeek(weekDays));

            //月末までの残りの日数を週毎にコレクションに入れる
            for (; day <= daysInMonth;)
            {
                weekDays = new CalendarDay[7];

                for (int i = 0; i < 7; i++)
                {
                    CalendarDay calDate = new CalendarDay();
                    calDate.Year = target.Year;
                    calDate.Month = target.Month;
                    calDate.Day = day;

                    weekDays[i] = calDate;
                    day++;

                    if (day > daysInMonth)
                    {
                        break;
                    }
                }

                weeks.Add(new CalendarWeek(weekDays));
            }

            //月に週を設定する
            CalendarMonth month = new CalendarMonth();
            month.Weeks = weeks.ToArray();

            return month;
        }
    }

前月と当月が混ざる週のロジックは少しややこしいですが、最初に当月1日の曜日を求めて、それより前の曜日を前月の日付で、
後の曜日を当月の日付で埋めるようにしています。

祝祭日に関しては扱っていませんが、Google Calendar APIから取得する方法が有るようですので、
また今度、トライしてみたいと思います。

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。