1

私のアプリケーションには、要素として Wpf コントロールを含むグラフ (カスタム Wpf パネル) があります。要素は、Wpf コントロールから派生したカスタム コントロールです。DataTemplate は、コントロールをビュー モデルに関連付けます。ビュー モデル コレクション「GraphElements」は、以下に示すように itemsControl にバインドされます。

<ItemsControl x:Name="PART_GraphItemsControl" Grid.Column="1"
              VerticalAlignment="Stretch" 
              HorizontalAlignment="Left"
              ItemsSource="{Binding Path=GraphElements}"
              VirtualizingStackPanel.IsVirtualizing="True"
              VirtualizingStackPanel.VirtualizationMode="Recycling">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <local:GraphDesignerPanel HorizontalAlignment="Stretch"
                                      VerticalAlignment="Top" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

グラフの要素は 2 ~ 500 の範囲で変化します。アプリケーションの特定のモードでは、要素の値が表示されます。値を表示するために、要素の ViewModel はINotifyPropertyChanged.PropertyChanged("VariableValue")

問題: グラフに 100 個以上の要素がある場合、各要素ビュー モデルは INotifyPropertyChanged.PropertyChanged を起動して、要素の値を表示します。これにより、MeasureOverride が 100 回以上呼び出され、メモリとパフォーマンスの問題が発生します。

MeasureOverride 呼び出しの数を減らすにはどうすればよいですか?

グラフ要素の値表示の XAML:

 <TextBlock  
    Text="{Binding Path=VariableValue, StringFormat={}({0})}" Width="60"
    FontSize="11" Name="txtBlk">
</TextBlock>

VariableValue が null の場合、上記の TextBlock は折りたたまれます

<DataTrigger Binding="{Binding Path=VariableValue}" Value="{x:Null}">
    <Setter TargetName="txtBlk"  Property="Visibility" Value="Collapsed"/>
</DataTrigger>

更新:この問題は、以下のリンクのサンプルで再現可能です。ダウンロード、ビルド、デバッグ。アプリが開いたら、Window.xaml.cs MeasureOverride にブレークポイントを設定します。アプリに戻り、「Click Me」ボタンを押します。ブレークポイントは 11 回ヒットします。

http://sivainfotech.co.uk/measureoverrideissue.zip

どんなアイデアでも大歓迎です。

4

1 に答える 1

3

問題をより明確にする必要があるため、正確な答えはありません。とにかく、PropertyChanged イベントは、サブスクライバーへの変更をトリガーするためだけに生まれました。

要素が何であるか、パネルでどのようにホストされているか、非常に多くの MeasureOverride を起動するトリガーの種類を指定しません。別のアプローチを選択することもできます: すべての要素のバインドを解除してから、再度バインドします。もう 1 つの解決策は、多くのトリガーにつながる可能性のあるバインドを回避し、ティックごとに InmvalidateVisual を呼び出す DispatcherTimer を介して UI を更新することです。

それが役に立てば幸い。

編集:私はあなたの情報源にトリックを作りました. 私はそれが私が使用したことがないものであることを認めなければなりません. ただし、使用するかどうかは自由に決めてください。

1) MeasureOverride 内にインジケーターを配置します (ブレークポイントを避けます)。

    protected override Size MeasureOverride(Size availableSize)
    {
        System.Console.WriteLine("measure-override");
        return base.MeasureOverride(availableSize);
    }

2) MyViewModel クラス内にこれを追加します。

    private bool _keepSync = true;
    public bool KeepSync
    {
        get { return this._keepSync; }
        set
        {
            if (this._keepSync != value)
            {
                this._keepSync = value;
                this.Sync();
            }
        }
    }

3) MyProp セットを変更します (幅と同じ):

        set
        {
            if (value != prop)
            {
                this.prop = value;
                if (this.KeepSync)
                    this.NotifyPropertyChanged("MyProp");
            }
        }

4) Window1 クラス内に、次の DependencyProperty を追加します (デモに役立ちます)。

    public static readonly DependencyProperty KeepSyncProperty = DependencyProperty.Register(
        "KeepSync",
        typeof(bool),
        typeof(Window1),
        new PropertyMetadata(
            true,
            (obj, args) =>
            {
                var ctl = (Window1)obj;
                ctl.KeepSyncChanged(args);
            }));


    public bool KeepSync
    {
        get { return (bool)GetValue(KeepSyncProperty); }
        set { SetValue(KeepSyncProperty, value); }
    }


    private void KeepSyncChanged(DependencyPropertyChangedEventArgs args)
    {
        foreach (var vm in this.ViewModelCollection)
            vm.KeepSync = (bool)args.NewValue;
    }

5) ボタンのクリック ハンドラを次のように変更します。

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        for (var i = 0; i < ViewModelCollection.Count; i++)
        {
            ViewModelCollection[i].Width += 10;
            ViewModelCollection[i].MyProp = string.Format("Item #{0}: {1}", i, ViewModelCollection[i].Width);
        }
    }

アプリケーションを実行しようとすると、ボタンをクリックすると、Visual Studio の出力ウィンドウにいくつかの "measure-override" が表示されることに気付くでしょう。それが本来の振る舞いです。(true とマークされている ckeckbox にも注意してください。ただし、マークを外さないでください!)

ここで、アプリをシャットダウンして再起動します。次に、チェックボックスのマークを外して、ボタンをクリックします。「メジャー オーバーライド」呼び出しがはるかに少ないことに気付くでしょう。

お役に立てれば。

乾杯

EDIT2: くそー!...私は作品を忘れて!...申し訳ありません!

private void Sync()
{
  if (this.KeepSync == false)
    return;

  this.NotifyPropertyChanged("MyProp");
  this.NotifyPropertyChanged("Width");
}

再乾杯

于 2011-08-05T16:09:03.380 に答える