2014年5月31日土曜日

RibbonControlsLibraryで、タイプ初期化子が例外をスロー

NuGetでRibbonControlsLibrary.dllを入手し、添付できるようになっています。

ところが残念なことに、本番環境では次のような例外を起こす場合が有ります。

『'Microsoft.Windows.Controls.Ribbon.RibbonWindow' のタイプ初期化子が例外をスローしました。』

$exception.InnerExceptionを見れば理由が分かるのですが、別途、Microsoft.Windows.Shell.dllが必要です。

場所例:
C:\Program Files (x86)\Microsoft Ribbon for WPF\V3.5\Microsoft.Windows.Shell.dll

入手はこちらなどから:

Microsoft Ribbon for WPF October 2010
http://www.microsoft.com/en-us/download/details.aspx?id=11877

2014年5月27日火曜日

SAHクラス, SQL Auto Hourglass, WaitCursor的な物

SQLのクエリ中に砂時計を表示する。usingで囲い込みできればGoodと思い、WPFへ移植。

    public class SAH : IDisposable {
        Cursor c;
        readonly static Cursor a = new Cursor(new MemoryStream(Resources.SAH, false));

        public SAH() {
            c = Mouse.OverrideCursor;
            Mouse.OverrideCursor = a;
        }

        #region IDisposable メンバ

        public void Dispose() {
            Mouse.OverrideCursor = c;
        }

        #endregion
    }

リソースSAHを定義してください。
カーソルファイル(SAH.cur等)を、
バイナリ形式(System.Byte[])で取り込んでおけば、
Okです。

DataContextには、DataTable or DataView?

CollectionViewSource.Source経由で、DataContextにレコードを設定するのですが、、、

DataView →→→ WPFに変更が通知されない!

DataTable →→→ WPFに変更が通知される。

(5/27追記) しかし問題点が。。。

DataTableはフィルタ・並び替えが使えません。。。

(5/30追記) すべて気のせいでした。CollectionViewSource.SourceDataTableを突っ込むと、DataTable.DefaultViewが使用される動きを確認しました。

(10/16追記) 変更が通知されない!点に関しては、私の勘違いであったようです。DataRowViewの編集中(IsEditがtrue)の場合は、変更の通知が抑制されるだけで、最後にEndEdit/CancelEditを使えば、変更が伝わりました。

2014年5月16日金曜日

RunはMode=OneWayでご利用を

Hiramituというアプリを作っています。
Excelで作っている見積書の作成&印刷を、SQL Server&WPFでやろう、という算段です。

印刷も根性書きのXAMLでやっています。

Runを使うときの注意事項…

Mode=OneWayにして使う。何故かバインド先に書き戻そうとする動きが有り、プログラムがハングする為です。

    <Label Grid.Column="1" Grid.ColumnSpan="3" HorizontalContentAlignment="Right" BorderBrush="Black" BorderThickness="0,0,1,1" Padding="{StaticResource TablePadding}">
        <TextBlock>
            <Run>小計:</Run>
            <Run Text="{Binding 金額,StringFormat='#,##0',TargetNullValue=0,Mode=OneWay}" />
        </TextBlock>
    </Label>

こうやって書きますと、Gridの中に、Labelを一つ置くだけで済みます。

2014年5月15日木曜日

WPF で使うデータベース技術について

※ Entity Framework 6 + INotifyPropertyChanged の悩みでしたらこちらへ。

様々な方向性から検討した結果、次の構成に決めました:
  • データベースは、PostgreSQL 9.2を利用。
  • 画面・フロントエンドの構築は、WPFを利用。
  • データベースとの接続には、Npgsqlと、型指定の無いDataSet技術を利用。

Entity Frameworkとの決別

DataSet技術を使う前は、Entity Frameworkを使っていました。しかし、止めました。

Entity Frameworkを止めた理由とは:
  • テーブルの主キーを後から変更することができない
    『後から担当者IDを変更できないのは困る…』
  • 外部キー制約の制限。主キー以外でリレーションを構築できない。改善の要望が上がっています。
    『serial型の主キーを別途追加し、担当者IDはUnique制約にしておいて、それでリレーションを組もうか、ってできない!?』

そこで、Entity Frameworkの利用を断念致しました。

その後の模索等

VS Express版で開発しています。無条件に、金銭コストが掛からない方法を選んでいました。

どうしても手抜きがしたくて、SELECT/UPDATE/INSERT/DELETEの生成を自動化してくださるORMの手が欠かせません。

dapperも試しました。これはDBの更新ができないので、断念致しました。

VSのDataSet Designerも試しました。これは、DBの更新が簡略化できます。しかし、nullの扱いに難があるので、断念致しました。(Generatorが、Nullableを使ったコードを生成できないので、意図的にIsNull/SetNullを呼び出す必要が有る為)

DataSetとの邂逅

Visual Studioが如何様なコードを出力するのか、興味が有りました。DataSetとWPFがバインディングする際に。

すると、非常に気の利いたコードが生成されたのです。

こういう感じの事を実現するコードが生成されます。

(DataContext = CollectionViewSource)⇔DataTable

具体的には、次のコードが生成されました。
    <Window.Resources>
        <local:DataSet1 x:Key="dataSet1"/>
        <CollectionViewSource x:Key="dataTable1ViewSource" Source="{Binding DataTable1, Source={StaticResource dataSet1}}"/>
    </Window.Resources>

    <Grid DataContext="{StaticResource dataTable1ViewSource}">

    </Grid>

今回出現しましたCollectionViewSourceは、ナビゲーション機能付きです!

DataViewの、
  • 先頭(MoveCurrentToFirst)
  • 前(MoveCurrentToPrevious)
  • 次(MoveCurrentToNext)
  • 最後(MoveCurrentToLast)
  • 所定のDataRowViewに移動(MoveCurrentToPosition)
移動する機能が付いています。

これはいい!

具体的に、良いと思った点:
  • DataContextを変更しなくても良い。
    • ナビゲーション機能を実装しようとすると、DataContextを変更することを考えがちです。しかし、それをしなくても良い。
    • ずっと同じCollectionViewSourceを指して置くだけで良いのです。
  • 双方向Bindingができる。
    • MoveCurrentXXXした後、{Binding 担当者ID}と書いている部分は自動で更新されます。
    • TextBoxで、Text="{Binding 担当者ID}"にしている場合、Textを変更したらデータソース側も自動で更新されます。

※途中からDataTable系→DataView系に変化しています。そのような動きをしているので、そのように書いています ^^

という訳で、DataSet技術CollectionViewSourceの組み合わせて乗り切ることに決めました。

しかし、CollectionViewSource.Viewには、考慮が必要な難点もあります。ついでに:
  • 先頭よりも前に行くことができる。IsCurrentBeforeFirstで判定。
  • 最後よりも後に行くことができる。IsCurrentAfterLastで判定。
  • 先頭かどうかは、CurrentPosition == 0 で判定できるが、
  • 最後かどうかは、CurrentPosition == cvsItems.View.Cast<Object>().Count() 等、列挙してみないと数を勘定できない。

WPFで業務アプリ開発!

前期から、生産管理的なシステムを、近代的なWindows環境で動くように、移植しています。

その奮闘によって得られた知見・知識を発信していきたい。