院長のメモ帖
2014年4月30日 水曜日
DataGridViewとBindingList<T>
.NETでデーターをいじるプログラミングをすると、DataGridViewは非常に便利なコントロールです。SQL ServerにLinqToSQLでクエリした結果をデーターバインドするだけでソート可能な表が作れるので、日常的に多用しています。
ただ、クエリ結果を演算してローカルのLinqToObjectで匿名型のIEnumerable<T>(具体的にはList<T>)に変換すると、表に表示はできますが、そのままではソートできなくなるのが不便に感じてました。DataSetやらで型情報を作ればいいのですが、せっかく匿名型で簡単にクエリーできるのに、表示のためだけに手間が多すぎて困ってました。
なんか方法があるはずだと思いながら長年ほかっておきましたが、検索したらカスタムデーターバインド(第2部)にやり方の概論が書いてありました。要するにはIBindingListインターフェースを実装するソート可能なジェネリッククラスを作ればいいということで、具体的にはBindingList<T>を継承した列挙クラスを作り、汎用ソートロジックを組み込んだIComparer<T>を継承したコンペアラークラスを作成し、IBindingListのソートメソッドをオーバーライドすればよいということでした。
サンプルソースがあったみたいなんですが、ダウンロードできず、HP上のコードは不完全なものだったので参考にしてSortableBindingList<T>を完成させました。コンストラクタでList<T>オブジェクトを受け取りIBindingListに変換し、IComparableな公開プロパティすべてを標準の序列でソートできます。IBindingListにはほかにも機能があるようですが、とりあえずソートできれば用が足りるので他の機能については実装しませんでしたが、とっても便利なクラスを作ることができました。
ただ、クエリ結果を演算してローカルのLinqToObjectで匿名型のIEnumerable<T>(具体的にはList<T>)に変換すると、表に表示はできますが、そのままではソートできなくなるのが不便に感じてました。DataSetやらで型情報を作ればいいのですが、せっかく匿名型で簡単にクエリーできるのに、表示のためだけに手間が多すぎて困ってました。
なんか方法があるはずだと思いながら長年ほかっておきましたが、検索したらカスタムデーターバインド(第2部)にやり方の概論が書いてありました。要するにはIBindingListインターフェースを実装するソート可能なジェネリッククラスを作ればいいということで、具体的にはBindingList<T>を継承した列挙クラスを作り、汎用ソートロジックを組み込んだIComparer<T>を継承したコンペアラークラスを作成し、IBindingListのソートメソッドをオーバーライドすればよいということでした。
サンプルソースがあったみたいなんですが、ダウンロードできず、HP上のコードは不完全なものだったので参考にしてSortableBindingList<T>を完成させました。コンストラクタでList<T>オブジェクトを受け取りIBindingListに変換し、IComparableな公開プロパティすべてを標準の序列でソートできます。IBindingListにはほかにも機能があるようですが、とりあえずソートできれば用が足りるので他の機能については実装しませんでしたが、とっても便利なクラスを作ることができました。
using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Data;
namespace mynamespace
{
//IBindingListのソート機能を実装したジェネリックリストクラス
public class SortableBindingList<T> : BindingList<T>
{
public static SortableBindingList<T> ToSortableBindingList(List<T> list)
{
return new SortableBindingList<T>(list);
}
public SortableBindingList(List<T> list) : base(list) { }
protected override bool SupportsSortingCore
{
get
{
return true;
}
}
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
List<T> items = this.Items as List<T>;
if (items != null)
{
PropertyComparer<T> pc = new PropertyComparer<T>(prop, direction);
items.Sort(pc);
_isSorted = true;
}
else
_isSorted = false;
_direction = direction;
_SortProperty = prop;
this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
private bool _isSorted;
protected override bool IsSortedCore
{
get { return _isSorted; }
}
private ListSortDirection _direction;
protected override ListSortDirection SortDirectionCore
{
get
{
return _direction;
}
}
private PropertyDescriptor _SortProperty;
protected override PropertyDescriptor SortPropertyCore
{
get
{
return _SortProperty;
}
}
}
//汎用コンペアラークラス
public class PropertyComparer<T> : IComparer<T>
{
public PropertyComparer(PropertyDescriptor propertyName, ListSortDirection direction)
{
this.name = propertyName;
sortDirection = (direction == ListSortDirection.Ascending) ? 1 : -1;
}
private PropertyDescriptor name;
private int sortDirection;
#region IComparer<T> メンバー
public int Compare(T x, T y)
{
IComparable left = name.GetValue(x) as IComparable;
IComparable right = name.GetValue(y) as IComparable;
int result;
if (left != null)
result = left.CompareTo(right);
else if (right == null)
result = 0;
else
result = -1;
return result * sortDirection;
}
//呼び出し用拡張メソッド
public static class BindingListExtensions
{
public static SortableBindingList<T> ToSortableBindingList<T>(this List<T> list)
{
return SortableBindingList<T>.ToSortableBindingList(list);
}
}
}
2014年4月 8日 火曜日
ブロードキャストアドレス
もうすぐ、XPのサポート切れということですが、うちにも1台だけ対象PCがあります。それは、電話番号通知システムに使っているCTIサーバーです。CTIソフトは自作で、はじめはVBで作っていたのを.NET4.0に書き直したので最新OSで問題なく動くのですが、ファイルバックアップソフトも入っていて、これはライセンスをバージョンアップしていないのでXPまでしか対応していないんです。うちで一番古いPCですが、大した故障しなかったのでずっとそのままにして、ファンだけ何回か取り替えましたが、この際新しくしてみました。
ところが、CTIソフトに不具合が出ました。電話番号を読み取ってSQLサーバーに記録するところは大丈夫なんですが、他のPCへの通知がうまくいきません。なんでだろうといじっていると、このPCはNICが二つあって一つは院内LANに、もう一つはフレッツ光の端末につなげてあったんですが、どうもフレッツ光端末にUDPをブロードキャストしているようでした。
いろいろ実験してみると、2つNICがあって255.255.255.255をSystem.Net.SocketのUdpClientクラスを使ってブロードキャストした場合、どちらか一つのNICでしかブロードキャストしてくれず、プログラム的にどちらかを指定することもできないようでした。
いろいろ調査してもよくわかりませんでしたが、ふと192.168.0.255でブロードキャストしたら、院内LANのNICでブロードキャストしてくれました。ハードコードなんでネットワークアドレスを変更したらソフトを書き換えなくちゃならないけど、たぶんそういうことはしないのでこれで良しということにしました。
ところが、CTIソフトに不具合が出ました。電話番号を読み取ってSQLサーバーに記録するところは大丈夫なんですが、他のPCへの通知がうまくいきません。なんでだろうといじっていると、このPCはNICが二つあって一つは院内LANに、もう一つはフレッツ光の端末につなげてあったんですが、どうもフレッツ光端末にUDPをブロードキャストしているようでした。
いろいろ実験してみると、2つNICがあって255.255.255.255をSystem.Net.SocketのUdpClientクラスを使ってブロードキャストした場合、どちらか一つのNICでしかブロードキャストしてくれず、プログラム的にどちらかを指定することもできないようでした。
いろいろ調査してもよくわかりませんでしたが、ふと192.168.0.255でブロードキャストしたら、院内LANのNICでブロードキャストしてくれました。ハードコードなんでネットワークアドレスを変更したらソフトを書き換えなくちゃならないけど、たぶんそういうことはしないのでこれで良しということにしました。