2015年11月5日木曜日

【VBA for Excel】分割された条件付き書式の統合

Excelおいて条件付き書式を設定し、設定されたセルをコピー&貼り付けを行った場合に、
条件付き書式が分割されてしまうという既知の問題に対して、
未然に防ぐ方法はいくつかあるものの、
分割されてしまった場合に同条件だった場合に統合する方法が見つからなかった。
これらを事後においても統合できるようにするため、
VBAにて統合処理のアドインを作成した。

 アドイン

アドインより追加すると「アドイン」というタブが追加され、「メニュー コマンド」というグループ内に
「条件付き書式の統合」というボタンが追加される。
これをクリックすることで条件書式の統合が行われる。

同条件同書式に関しては全て統合してしまうため、
あえてグループ毎に分けている場合なども統合されてしまうので注意。

※本アドインの使用により発生した損害については、一切関知致しません。
  使用は個人責任でお願い致します。



一番面倒かと思われた数式の一致判定に関しては、
セル値と思われる箇所を正規表現で取得し、
それらをした文字列が同様かどうかを比較。
その後絶対参照か絶対参照を判定し、絶対参照であれば一致するかどうか、
相対参照であれば適用範囲の先頭セルとの相対位置が一致するかどうかで、
同数式かどうかを判定で思いの外簡単に対応できた。

あとは全条件、全書式網羅を粛々と対応するだけの作業。

主にはFormatConditionクラスのオブジェクトに対応すればよいものの、
各条件によって、使用しているクラスが異なるかつ、インタフェース実装でもないため、
リフレクションによってアクセスしなければいけないのがExcel側の設計としてどうかと思われる。
リフレクションしたうえで実装されていないメソッド、プロパティがあった場合は、
実行時エラー不可避というのも改善して欲しい。
※何か回避策を知っている人がいたら教えてください。
  CallByName関数でアクセスしているのですが、
  IsImplimented関数(仮)などでサポートしているかどうかの事前チェックがしたい。

また、罫線に関しては、上下左右の4か所しか設定できないはずが、
Bordersオブジェクトが6つのBorderオブジェクトを持っているケースがあり、(CellValuesなど)
5つ目、6つ目を参照すると、
LineStyleプロパティをサポートしていないという問題が発生してしまう。
暫定として4つで判定しているが、(単一セルの書式設定なので問題ないはず?)
もし必要になる場合があるならば不具合となってしまう。

現状、テストした環境では問題なく動作しているため、
必要に応じて改良を加えていく。

2015年4月19日日曜日

【C#】DataTableをバインドしたDataGridViewのソートをアンドゥー・リドゥーする


DataTableをバインドしたDataGridViewのソートをしたかったものの、
検索に引っかからなかったのでメモ。
とりあえずは列のソートのみでサンプルを作成。

キモとしてはDataViewをIBindingListにキャストして、
SortProperty、SortDirectionプロパティを取得及び設定するところ。
デフォルトはSortPropertyはnull、SortDirectionはAscendingとする。

取得を行うためのソート前のイベントとして適当なものが無かったため、
あくまでもソートをするためにはヘッダーセルをクリックする前提で
CellMouseEnterイベントを使用した。
まずは当該イベントにて、
SortProperty、SortDirectionプロパティよりソート前の値を取得する。
あくまでもソートする候補として都度取得を行うのみとし、
ソート後にソート前の値として使用する。

その後、ソート後のイベントではSortProperty、SortDirectionプロパティより
ソート後の値を取得し、コマンドを作成する。
コマンドの作成は検索してすぐに見つかると思われるので、省略。

ソート前の列とソート後の列が一緒かどうかの検証を行うべきかと思われるが、
上記のとおり、ヘッダーセルをクリックする前提であれば、問題なく動作している模様。

PropertyDescriptor prevSortProperty_; // ソート前のソートプロパティ
ListSortDirection prevSortDirection_; // ソート前のソート方向

void dataGridView1_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
    // 列ヘッダーセルにマウスが入った場合のみ、
    // 各プロパティの値を取得
    if(e.RowIndex == -1)
    {
        IBindingList bindingList =
            (IBindingList)this.dataGridView1.DataSource.DefaultView;
        prevSortProperty_ = bindingList.SortProperty;
        prevSortDirection_ = bindingList.SortDirection;
    }
}

void dataGridView1_Sorted(object sender, EventArgs e)
{
    // ソート後のソートプロパティとソート方向を取得
    IBindingList bindingList =
        (IBindingList)this.dataGridView1.DataSource.DefaultView;
    PropertyDescriptor currentSortProperty = bindingList.SortProperty;
    ListSortDirection currentSotrDirection = bindingList.SortDirection;

    // prevSortProperty_、prevSortDirection_、
    // currentSortProperty、currentSotrDirectionを使用して、
    // ソートのアンドゥー・リドゥーコマンドを作成
}

2015年3月24日火曜日

【TeraStation】HDD換装でのEMモードからの復旧

1TB×2のRAID1にて運用していたTeraStationの片方のHDDが壊れてしまったため、
4TB×2のHDD入れ替えを検討。

HDDを両方とも入れ替えたところ、EMモードとなってしまった。
Web管理画面にもアクセスできず、ファームアップデートを試みたものの、
パーティション情報が無いと言われてアップデートできずに試行錯誤。

アップデータに含まれるTSUpdater.iniというファイルの
VersionCheck, NoFormattingを0にしたところ、
アップデートをすることが出来た。

パーティション情報が無いと言われるのは当然としても、
アップデートが出来ないのは納得できない。

それよりも、上記書き換えについて、分りやすく書いていてほしかった。
(正規のやり方ではないかもしれないが。)

2015年3月12日木曜日

【C#】XmlSerializerではsetアクセサーが呼ばれない

Dictionary<string, string>を保持したクラスにて、
インスタンス丸ごとシリアライズ・デシリアライズを行うため、
以下のようなプロパティを作成して実行した。 

public List<KeyAndValue<string, string>> Parameters
{
    set { Dictionary = ListToDictionary(value); }
    get { return DictionaryToList(value); }
}

シリアライズは問題なく行われたものの、
デシリアライズ後のDictionaryは空のまま。

シリアライズ時はget、デシリアライズ時はsetを行うのではという想定で、
setアクセサにブレークを張るものの引っかからない。

試しにgetアクセサにてブレークを張ったところ、
予想外に引っかかった。

なぜ?と言うことで調べて見たところ、
以下の記事が見つかった。

 http://goo.gl/W94Md0

XmlSerializer does not call the setter for a collection. Instead is calls the getter, and then adds items to the collection returned.
setアクセサを呼ばずに、getアクセサで取得したコレクションに対してアイテムを追加すると。

原因判明。