Firefoxの各タブに閉じるボタンを常に表示する

以前、browser.tabs.tabClipWidth = 0にすればタブがどんなに細くなっても閉じるボタンが常に表示されると書いた。あれは嘘だった。
フォクすけが言っていたので信用してしまったが、どう見ても画像のUIが古いその時点で。疑うべきだった。

そういうわけでこれを実現するにはUserChromeをいじる必要があるらしい。
調べてみるとこのような記事を見つけた。
b.0218.jp

私はマウスオーバー時でなく常に表示したいので:hoverを消せばよさそうである。

早速お借りして…

.tabbrowser-tab:not([pinned]):hover .tab-close-button {
  display: block !important;
}

これを…こうじゃ!

.tabbrowser-tab:not([pinned]) .tab-close-button {
  display: block !important;
}

Firefoxの中止ボタンがしいたけじゃなくて困る

インターネット老人かな?

userChrome.cssを書く

場所:AppData\Roaming\Mozilla\Firefox\Profiles\プロファイル名\chrome
chromeフォルダはないので作る。

ここにuserChrome.cssというファイルを作り、内容は以下。

#stop-button {
  list-style-image: url("shiitake.png")!important;
}

しいたけ画像は拾ったやつを加工して使っているので掲載できない。
userChrome.cssと同じところにしいたけの画像も置く。

about:configを変更

toolkit.legacyUserProfileCustomizations.stylesheetsをtrueにする。

ページの読み込み中のほんの一瞬だけしいたけになるんですけど、このこっそり感がちょうどいいですね?

about:configのほかの設定(しいたけには無関係)

browser.tabs.insertAfterCurrent = true
新しいタブをカレントタブの右隣に開く
browser.tabs.tabClipWidth = 0
タブがどんなに細くなっても閉じるボタンを常に表示
browser.tabs.tabMinWidth = 90
タブの最小幅
browser.tabs.closeWindowWithLastTab = false
最後のタブを閉じてもFirefoxを終了しない
browser.search.openintab = true
検索窓を新しいタブで開く

【C#】Excelのテーブルをソート

このようなテーブルがあって、個数の昇順、同値なら価格の昇順にソートしたい。

using Microsoft.Office.Interop.Excel;

まずはファイルを開く。

var xlapp = new Application();
var xlbook = xlapp.Workbooks.Open("ファイル名");

テーブルはWorksheet配下のListObjectsコレクションの中に格納されている。ので、まずはそれを取得する。

Worksheet xlsheet = xlbook.Worksheets["Sheet1"];

var tablename = "テーブル1";
ListObject table1 = xlsheet.ListObjects[tablename];

SortFields

table1にソートのルールを書き込んでゆく。

table1.Sort.SortFields.Clear();
table1.Sort.SortFields.Add(table1.HeaderRowRange[3], XlSortOn.xlSortOnValues, XlSortOrder.xlAscending, null, XlSortDataOption.xlSortNormal);

SortFieldsというのはGUIで言うところのレベル1行分のことを指す。
Addを増やしていけば次に優先されるキーとして追加される。

なのでまずSortFields.Clear()で既存の条件を削除する。
次にSortFields.Addで条件を指定してゆく。

SortFields.Add

公式ドキュメントを読んでも全部object型でうんこなのだが、型はこう。
SortFields.Add(Range, XlSortOn, XlSortOrder, ?, XlSortDataOption)

2つ目以降の引数は省略できる。

Key

並び替えの基準にするセル範囲を指定。今回の例ではC列を基準にしたいので、C列を含むRange型をどうにかして用意して与えればいい。
例えば
Range hoge = xlapp.ActiveSheet.Range("C1");
のようにして強引に作ったり。セル範囲はC列であることが分かればC列全体を表すC:Cや、はたまたテーブルをはみ出したC6みたいなのでも動く。
もうひとつ便利な方法がListObjectのHeaderRowRangeコレクションを使う方法で、これだと列の挿入でセルアドレスが変わってしまっても追尾できる。
HeaderRowRangeコレクションの引数は1から始まり、数値で指定する。HeadRowRange[”個数”]のように見出し名を直接添え字に使用することはできない。

XlSortOn

並び替えのタイプを指定。

XlSortOnCellColor セルの色
XlSortOnFontColor フォントの色
XlSortOnIcon 条件付き書式のアイコン
XlSortOnValues セルの値
XlSortOrder

並び替えの順序を指定。

XlAscending 昇順
XlDescending 降順
null

ユーザー設定リストに関するものなのだがこの機能は使ったことがないのでわからない。とりあえず動くのでnullを与えている。

XlSortDataOption
XlSortNormal 通常の並び替え
XlSortTextAsNumbers 文字列を数値とみなして並べ替え

この機能は田中さんのところに詳しい説明があったが、同じく使ったことがないのでとりあえず既定値のXLSortNomalを与えている。

Sort.Apply

table1.Sort.Apply();

ひととおり条件を指定し終わったらSort.Apply()でソートを実行する。

実装例

        static void Main()
        {
            var xlapp = new Application();
            var xlbook = xlapp.Workbooks.Open(”hoge.xlsx");
            xlapp.Visible = true;
            try
            {
                var tablename = "テーブル1";

                Worksheet xlsheet = xlbook.Worksheets["Sheet1"];
                ListObject table1 = xlsheet.ListObjects[tablename];

                table1.Sort.SortFields.Clear();
                table1.Sort.SortFields.Add(table1.HeaderRowRange[3], XlSortOn.xlSortOnValues, XlSortOrder.xlAscending, null, XlSortDataOption.xlSortNormal);
                table1.Sort.SortFields.Add(table1.HeaderRowRange[2], XlSortOn.xlSortOnValues, XlSortOrder.xlAscending, null, XlSortDataOption.xlSortNormal);

                table1.Sort.Apply();

            }
            finally
            {

                Console.ReadLine();
                xlapp.DisplayAlerts = false;
                xlapp.Quit();
            }
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlapp);

        }

複数バージョンのoffice共存環境で365を既定にする。

色々あって、一台のマシンに365と2007と2010を共存させている。
古いExcelは立ち上がりが早いのでCOM参照しながら.NETアプリを開発するようなシチュでは便利なのと、何より後方互換性を保つには古いofficeのほうが都合が良かったりで。ただし普段はセキュリティ諸々の理由で最新のofficeを利用したい。

やり方はまあ、概ねここに書いてある通りなのだけれど。
Office 共存環境で、既定のプログラムの変更は可能なのか? - Microsoft コミュニティ

設定→アプリ→アプリと機能からMicrosoft 365を選んで修復する。クイック修復で十分。
Officeを修復してもうまく行かなかった。

【C#】Excelで複数の選択範囲を取得/選択する。

こういうのに対する操作。

$B$2:$D$3,$B$6:$D$7みたいなデータが欲しい、あるいはこの範囲を選択させたい。

f:id:Hamukoro:20180927011219p:plain

 

using Microsoft.Office.Interop.Excel;

var xlapp=new Application();

 COM解放については省略。

Excelから選択範囲をList<Range>で取得

複数にまたがった選択範囲はxlapp.Selection.Areasで取得できる。

Selectionプロパティは現在選択されているオブジェクトを返す。選択されているのがセルならRangeで返ってくるのでいいのだが、オートシェイプなんかが選択されているとそっちが返ってくるので注意されたい。

 

Selection.Areasプロパティはforeachで回せる。varだと受けられないので、Rangeで受ける。

Areas areas = xlapp.Selection.Areas;

foreach (Range item in areas)

{

    Console.WriteLine(item.Address);

}

Range型でぎっしりつまっているのがわかる。

このままでは使いにくいのでリストにしまう。

Areas areas = xlapp.Selection.Areas;

var selectionList = new List<Range>();

foreach(Range item in areas)

{

    selectionList.Add(item);  

}

item.AddressをList<string>で持ってもいい。この辺は好み。

selectionList内の値をExcel上で選択する。

Unionメソッドを使う。UnionメソッドはRange型の引数が2つ以上必要で、その割に配列とかで渡すオーバーロードがないクソメソッド。なのでちょっと工夫が必要。

 

//先にひとつだけ選択しておく。

xlapp.ActiveSheet.Range(selectionList[0].Address).Select();

for (int i = 1; i < selectionList.Count; i++)

{

    //型が合わないので直接Unionの中には入れられないので一旦変数にとって渡す。

    Range selection = xlapp.Selection;

    xlapp.Union(selection,xlapp.ActiveSheet.Range(selectionList[i].Address)).Select();

}

ActiveSheet.selectionList[i]とすると何故か怒られるのでRangeに渡す。

 

SellectionもUnion()もApplication直下にしか使えないので、操作したいシートをアクティブにしておく必要がある。

xlapp.Worksheets[1].Selection;

みたいなことはできない。

 

参考

VBAで選択範囲を追加したい - VBAでA~Hの1行を選択して、違う行も同じ範囲... - Yahoo!知恵袋

Range.Areas Property (Microsoft.Office.Interop.Excel) | Microsoft Docs

_Global.Union(Range, Range, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object) Method (Microsoft.Office.Interop.Excel) | Microsoft Docs