測定を実行したり、パスをアレンジしたりせずに、所有者が描いたWPFコントロールを手動で伝えるように、リフレッシュ/再描画するにはどうすればよいですか?
-
22-10-2019 - |
質問
コントロールサブクラスでカスタム描画を行っています OnRender
. 。この描画コードは、外部トリガーとデータに基づいています。そのため、トリガーが発火するたびに、そのデータに基づいてコントロールを再レンダリングする必要があります。私たちがやろうとしているのは、レイアウトパス全体を通過することなく、コントロールに再レンダリングを強制する方法を見つけることです。
上記のように、私が見たほとんどの答えは、 Visual
これにより、特に非常に複雑な視覚木の場合、非常に高価な新しい測定と配置を強制するレイアウトが無効になります。しかし、繰り返しますが、レイアウトはそうします いいえ 変化も、視覚ツリーも変化しません。唯一行うのは、異なる方法でレンダリングされる外部データです。そのため、これは厳密に純粋なレンダリングの問題です。
繰り返しますが、再実行する必要があることをコントロールに伝える簡単な方法を探しています OnRender
. 。私はあなたが新しいものを作成する1つの「ハック」を見ました DependencyProperty
コントロールを更新したいときに何らかの価値に設定した「エフェクトスレンダー」に登録しますが、それらのプロパティのデフォルトの実装内で何が起こっているのか、つまりその動作に影響を与えるために呼ばれるものにもっと興味があります。
アップデート:
まあ、それはそうでさえそのような呼びかけがないようです AffectsRender
フラグは依然として内部的にパスをアレンジします(以下のCodenakedの回答に従って)が、組み込みの動作とワークアラウンドを示す2番目の回答を投稿しました。フラグ。下記参照。
解決 2
わかりました、私はこれに答えて、コードネイクスの答えが正しい理由を人々に示しますが、もしあなたがそうするならアスタリスクを使用し、また仕事を提供します。しかし、いわゆるいわゆる人物では、彼の答えが私をここに導いたので、私はまだ答えられているように彼をマークしています。
更新:それ以来、2つの理由で受け入れられた答えをここに移動しました。一つ、私は人々にそこで知ってほしい は これに対する解決策(ほとんどの人は、受け入れられている答えを読んで先に進むだけです)と25kの担当者を考えると、私はそれを取り戻したのではないかと思いません! :)
これが私がしたことです。これをテストするために、私はこのサブクラスを作成しました...
public class TestPanel : DockPanel
{
protected override Size MeasureOverride(Size constraint)
{
System.Console.WriteLine("MeasureOverride called for " + this.Name + ".");
return base.MeasureOverride(constraint);
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
{
System.Console.WriteLine("ArrangeOverride called for " + this.Name + ".");
return base.ArrangeOverride(arrangeSize);
}
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
System.Console.WriteLine("OnRender called for " + this.Name + ".");
base.OnRender(dc);
}
}
...私はこのようにレイアウトしました(それらがネストされていることに注意してください):
<l:TestPanel x:Name="MainTestPanel" Background="Yellow">
<Button Content="Test" Click="Button_Click" DockPanel.Dock="Top" HorizontalAlignment="Left" />
<l:TestPanel x:Name="InnerPanel" Background="Red" Margin="16" />
</l:TestPanel>
窓をサイズしたとき、私はこれを手に入れました...
MeasureOverride called for MainTestPanel.
MeasureOverride called for InnerPanel.
ArrangeOverride called for MainTestPanel.
ArrangeOverride called for InnerPanel.
OnRender called for InnerPanel.
OnRender called for MainTestPanel.
しかし、私が電話したとき InvalidateVisual
「MaintestPanel」(ボタンの「クリック」イベントで)では、代わりにこれを手に入れました...
ArrangeOverride called for MainTestPanel.
OnRender called for MainTestPanel.
測定上のオーバーライドのいずれも呼び出されず、外側コントロールのアレンジオーバーライドのみが呼び出されたことに注意してください。
内部に非常に重い計算があるかのように完璧ではありません ArrangeOverride
あなたのサブクラス(残念ながら私たちはそうします)では、まだ実行されますが、少なくとも子供たちは同じ運命に落ちません。
ただし、子供のコントロールがいずれにも存在するプロパティがないことがわかっている場合は、PromessParentArrangeビットセット(繰り返しますが、これを行います)を使用して、Nullableを使用することができます。 Size
必要な場合を除いて、再入場からのArrangeOverRideロジックを抑制するフラグとして...
public class TestPanel : DockPanel
{
Size? arrangeResult;
protected override Size MeasureOverride(Size constraint)
{
arrangeResult = null;
System.Console.WriteLine("MeasureOverride called for " + this.Name + ".");
return base.MeasureOverride(constraint);
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
{
if(!arrangeResult.HasValue)
{
System.Console.WriteLine("ArrangeOverride called for " + this.Name + ".");
// Do your arrange work here
arrangeResult = base.ArrangeOverride(arrangeSize);
}
return arrangeResult.Value;
}
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
System.Console.WriteLine("OnRender called for " + this.Name + ".");
base.OnRender(dc);
}
}
現在、何かが具体的にアレンジロジックを再実行する必要がない限り(測定するための呼び出しが行われるように)、OnRenderのみを取得します。アレンジアレンジロジックを明示的に強制したい場合は、サイズをnulえ、nivalidatevisualを呼び出して、ボブのおじさん! :)
お役に立てれば!
他のヒント
残念ながら、電話する必要があります validatevisual, 、呼び出します Invalidatearrange 初めの。 OnRender
メソッドはアレンジフェーズの一部として呼び出されるため、WPFにコントロールを再配置するように指示する必要があります(Invalidatearrangeが行う)。
FrameworkPropertyMetadata.AffectsRender
オプションは単にWPFに電話するように指示します InvalidateVisual
関連するプロパティが変更されたとき。
OnRenderをオーバーライドし、いくつかの子孫コントロールを含むコントロール(このMainControlと呼びましょう)を持っている場合、validatevisualを呼び出すには、子孫のコントロールが再配置されるか、再編成される必要があります。しかし、WPFには、利用可能なスペースが変更されていない場合、子孫の制御が再配置されるのを防ぐための最適化が整っていると思います。
レンダリングロジックを個別のコントロール(NestedControlなど)に移動することでこれを回避できる場合があります。これは、MainControlの視覚的な子供です。 MainControlは、これを視覚的な子供として自動的にまたはそのControlTemplateの一部として追加することができますが、Zオーダーで最低の子供である必要があります。その後、aを公開できます InvalidateNestedControl
NestedControlで無効化ビジュアルを呼び出すMainControlのタイプメソッド。
あなたは電話するべきではありません InvalidateVisual()
コントロールのサイズが変更されない限り、さらには再レイアウトを引き起こす他の方法があります。
サイズを変更せずにコントロールの視覚を効率的に更新する。使う DrawingGroup
. 。描画グループを作成して、それを入れます DrawingContext
その間 OnRender()
そして、その後いつでもできます Open()
DrawingGroup
視覚的な描画コマンドを変更すると、WPFは自動的に 効率的に UIのその部分を再レンダリングします。 (この手法を使用することもできます RenderTargetBitmap
毎回再描画するのではなく、増分変更を行うことができるビットマップが必要な場合)
これがどのように見えるかです:
DrawingGroup backingStore = new DrawingGroup();
protected override void OnRender(DrawingContext drawingContext) {
base.OnRender(drawingContext);
Render(); // put content into our backingStore
drawingContext.DrawDrawing(backingStore);
}
// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {
var drawingContext = backingStore.Open();
Render(drawingContext);
drawingContext.Close();
}
private void Render(DrawingContext drawingContext) {
// put your render code here
}
これが別のハックです:http://geekswithblogs.net/newthingsilearned/archive/2008/08/25/REFRESH-UPDATE-WPF-CONTROLS.ASPX
要するに、優先順位のDispatcherPriority.RenderでDummy Delegateを呼び出します。これにより、その優先順位以上のものが呼び出され、レレンダーが発生します。