[解決済み] WPFで画像のグリッドを描画する
質問
WPFで画像やアイコンのグリッドを描画しようとしています。グリッドの寸法は様々ですが、通常10x10~200x200の範囲になると思います。ユーザーはセルをクリックすることができ、いくつかのセルは1秒間に10-20回更新(画像を変更)する必要があります。グリッドは4方向に拡大・縮小でき、それが表す3次元構造の別のquot;slice"に切り替えられる必要があります。私の目標は、これらの要件を満たした上で、グリッドを描画するための適切な効率的な方法を見つけることです。
現在の私の実装では、WPFの
Grid
. 実行時に行と列の定義を生成し、グリッドの入力に
Line
(グリッドライン用)と
Border
(セルは現在オン/オフだけなので) オブジェクトを適切な行/列に配置してください。(この
Line
オブジェクトは横方向に広がっています)。
<イグ
グリッドの拡大中(Num6を押しながら)、操作のたびに再描画するには描画が遅すぎることがわかったので、単純に新規に
ColumnDefinition
,
Line
のセットと
Border
オブジェクトを、成長の各列に対して作成しました。これによって成長の問題が解決されましたが、同じような戦術で縮小も高速に行うことができます。シミュレーションの途中で個々のセルを更新するには、単にセルオブジェクトへの参照を保存して、表示される画像を変更すればよいのです。新しいZレベルに変更する場合でも、グリッド全体を再構築するのではなく、セルの内容のみを更新することで改善されます。
しかし、こうした最適化を行う前に、別の問題が発生しました。グリッドにマウスオーバーすると(低速/通常速度でも)、アプリケーションのCPU使用率が急上昇するのです。グリッドの子要素のイベントハンドラをすべて削除してみましたが、効果はありませんでした。最終的に、CPU使用率を抑制する唯一の方法は
IsHitTestVisible = false
に対して
Grid
. (のすべての子要素に設定します)。
Grid
は何もしなかった!)
私は、グリッドを構築するために個々のコントロールを使用することは集中的でこのアプリケーションには不適切であり、WPFの2D描画機構を使用することがより効率的であると信じています。しかし、私はWPFの初心者なので、これを達成するための最善の方法についてアドバイスを求めています。私が読んだ範囲では、私は
DrawingGroup
を使い、各セルの画像を1枚の画像に合成して表示します。そして、画像全体のクリックイベントハンドラーを使い、クリックされたセルの座標をマウスの位置で計算することができます。でも、これって面倒くさいし、もっといい方法がないかなぁ。
いかがでしょうか?
アップデート1:
友人のアドバイスで、"Space "を使用するように変更しました。
Canvas
を使用し
Rectangle
を各セルのために使用します。最初にグリッドを描画するときに、すべての
Rectangle
を2次元配列に格納しておき、グリッドの内容を更新するときに、単純にその参照にアクセスするだけでよい。
private void UpdateGrid()
{
for (int x = simGrid.Bounds.Lower.X; x <= simGrid.Bounds.Upper.X; x++)
{
for (int y = simGrid.Bounds.Lower.Y; y <= simGrid.Bounds.Upper.Y; y++)
{
CellRectangles[x, y].Fill = simGrid[x, y, ZLevel] ? Brushes.Yellow : Brushes.White;
}
}
}
グリッドの初期描画は高速化され、その後の更新も確実に高速化されているように見えますが、まだいくつかの問題が残っています。
-
マウスオーバーする領域がどんなに小さくても、グリッドが数百セル以上になると、マウスオーバーするたびにCPU使用率が急上昇します。
-
更新がまだ遅すぎるため、上矢印キーを押しながらZレベルを変更すると(よくある使用例)、プログラムが数秒間フリーズし、その後一度に50Zレベルにジャンプするように見えます。
-
グリッドが5000セル程度になると、更新に1秒程度の時間がかかるようになります。これは法外に遅く、5000セルが典型的なユースケースに収まります。
をまだ試していません。
UniformGrid
というのは、私がすでに遭遇したのと同じ問題が発生する可能性があると思うからです。しかし、もう少しオプションを使い切ったら、試してみるかもしれません。
どのように解決するのか?
ご質問の内容
質問を言い換えましょう。 これらは、あなたの問題の制約です。
- 動的な大きさのグリッドを描画したい
- 各セルのオン/オフが高速に変化
- グリッドサイズの高速変更
- セル数が多い(=グリッドの寸法が些細なものではない)
- 高速フレームレート(例:30fps)でこれらすべての変化を発生させたい場合
- グリッドとセルの位置とレイアウトは決定論的で、単純で、あまりインタラクティブではない
これらの制約から判断すると、間違ったアプローチをしていることがすぐにわかるでしょう。
Reqruiement。決定論的ポジションの高速リフレッシュと少ないインタラクション
高速更新フレームレート + フレームあたりの変更回数が多い + セル数が多い + セルごとにWPFオブジェクトが1つ = 不具合。
非常に高速なグラフィックハードウェアと非常に高速なCPUを持っていない限り、グリッドの寸法が大きくなると、フレームレートは常に低下します。
あなたの問題は、ビデオゲームやダイナミックズームを使用したCAD製図プログラムに近いものです。 通常のデスクトップアプリケーションのようなものではありません。
イミディエイトモードとリテインドモードの図面
つまり、quot;immediate mode"での描画が必要で、quot;retained mode"での描画は必要ない(WPFはretained mode)、ということです。 これは、あなたの制約が、各セルを個別のWPFオブジェクトとして扱うことで提供される機能をあまり必要としないからです。
例えば、各セルの位置は決定論的であるため、レイアウトのサポートは必要ないでしょう。 また、ヒットテストのサポートも必要ありません。 各セルは単純な矩形(または画像)なので、コンテナのサポートは必要ありません。 重なり合うものがないので、複雑な書式設定(透明度、丸みを帯びた枠線など)のサポートも不要です。 言い換えれば、Grid(またはUniformGrid)を使用して、セルごとに1つのWPFオブジェクトを使用するメリットはないのです。
バッファビットマップへのイミディエイトモード描画の考え方
必要なフレームレートを実現するために、基本的には大きなビットマップ(画面全体をカバーするもの)、つまりスクリーンバッファに描画することになります。 セルについては、このビットマップ/バッファに描画するだけです (おそらく GDI を使用します)。 セルの位置はすべて決定論的であるため、ヒットテストは簡単です。
この方法は、オブジェクトが1つ(スクリーンバッファのビットマップ)しかないため、高速に処理することができます。 フレームごとにビットマップ全体を更新するか、変化した画面位置のみを更新するか、あるいはこれらのインテリジェントな組み合わせが可能です。
ここではグリッドを描いていますが、グリッドエレメントを使っていないことに注意してください。 アルゴリズムとデータ構造は、問題の制約条件に基づいて選択します。 見た目 言い換えれば、quot;Grid"はquot;grid"を描くための正しい解決策ではないかもしれないのです。
WPFのイミディエイトモード描画
WPFはDirectXをベースにしているので、基本的にはすでにスクリーンバッファのビットマップ(バックバッファと呼ばれます)を裏で使っています。
WFPでイミディエイトモード描画を使うには、セルをGeometryDrawingのものとして作成します(リテインモードであるShapeのものではありません)。 GemoetryDrawing は通常、非常に高速です。GemoetryDrawing オブジェクトは DirectX プリミティブに直接マッピングされ、Framework Elements のように個別にレイアウトやトラッキングを行わないので、非常に軽量です。
このGeometryDrawingをDrawingImage(これは本質的にバックバッファです)に選択すると、画面上で高速に変化する画像を得ることができます。 裏側では、WPFはあなたが期待したとおりのことをします。つまり、変化するそれぞれの矩形をイメージバッファに描画するのです。
繰り返しになりますが、Shapeは使わないでください--これらはフレームワーク要素であり は は、レイアウトに関与するため、大きなオーバーヘッドが発生します。 例えば 長方形は使用しないでください を使用しますが RectangleGeometry の代わりに
最適化
さらにいくつかの最適化を検討することができます。
- GeometryDrawingオブジェクトの再利用 -- 位置とサイズを変更するだけ
- グリッドに最大サイズがある場合、オブジェクトを事前に作成する
- 変更されたGeometryDrawingオブジェクトのみを修正し、WPFが不必要にリフレッシュしないようにする。
- つまり、異なるズームレベルに対して、常に前のグリッドよりはるかに大きいグリッドに更新し、それを縮小するためにスケーリングを使用します。 例えば、10x10のグリッドから直接20x20のグリッドに移動し、それを55%スケールバックして11x11の正方形を表示します。 この方法では、11x11 から 20x20 にズームしても、GeometryDrawing オブジェクトは変更されず、ビットマップのスケーリングのみが変更され、更新が非常に高速になります。
EDIT:フレーム・バイ・フレームレンダリングを行う
オーバーライド
OnRender
この質問に対する懸賞金の回答で提案されているように。 そうすると、基本的にシーン全体がキャンバスに描かれます。
DirectXを使った絶対制御
また、各フレームを絶対的に制御したい場合は、生のDirectXを使用することを検討してください。
関連
-
[解決済み】指定されたキャストが有効でない?
-
[解決済み】スクリプトクラスが見つからないので、スクリプトコンポーネントを追加できない?
-
[解決済み】C#におけるtypedefの等価性
-
[解決済み】"The ConnectionString property has not been initialized "を修正する方法
-
[解決済み】SmtpException: トランスポート接続からデータを読み取れません:net_io_connectionclosed
-
[解決済み】WPFでXamlファイルにコメントを追加する方法は?
-
[解決済み】Entity FrameworkからのSqlException - セッション内で他のスレッドが動作しているため、新しいトランザクションは許可されません。
-
[解決済み] ...基礎となる接続は閉じられました。予期しないエラーが受信で発生しました
-
[解決済み】URLから画像をダウンロードする方法
-
[解決済み】WPFのコントロールが利用可能なスペースを埋めるようにするにはどうすればいいですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】"出力タイプがクラスライブラリのプロジェクトは直接起動できない"
-
[解決済み】SmtpException: トランスポート接続からデータを読み取れません:net_io_connectionclosed
-
[解決済み】クロススレッド操作が有効でない。作成されたスレッド以外のスレッドからアクセスされたコントロール
-
[解決済み】取り消せないメンバはメソッドのように使えない?
-
[解決済み] 'IEnumerable<SelectListItem>' 型の ViewData アイテムで、キーが国であるものは存在しない。
-
[解決済み] EntityTypeにキーが定義されていないエラー
-
[解決済み】Unity 「関連するスクリプトを読み込むことができません」「Win32Exception: システムは指定されたファイルを見つけることができません"
-
[解決済み】インデックスが範囲外でした。コレクションパラメータname:indexのサイズより小さく、非負でなければなりません。
-
[解決済み】「namespace」なのに「type」のように使われる。
-
[解決済み】プロセスが実行されているかどうかを知るには?