0

ScrollViewer が適切に動作するようにキャンバスのサイズを自動的に調整するために、Canvas から派生するクラスを作成し、MeasureOverride メソッドをオーバーライドして、キャンバスに存在する UIElements に基づいてキャンバスのサイズを調整します。

public class DesignerCanvas : Canvas
{
    protected override Size MeasureOverride(Size constraint)
    {
        Size size = new Size();
        foreach (UIElement element in Children)
        {
            double left = Canvas.GetLeft(element);
            double top = Canvas.GetTop(element);
            left = double.IsNaN(left) ? 0 : left;
            top = double.IsNaN(top) ? 0 : top;

            element.Measure(constraint);

            Size desiredSize = element.DesiredSize;
            if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
            {
                size.Width = Math.Max(size.Width, left + desiredSize.Width);
                size.Height = Math.Max(size.Height, top + desiredSize.Height);
            }
        }

        // add some extra margin
        size.Width += 10;
        size.Height += 10;
        base.InvalidateMeasure();
        return size;
    }
}

XAML:

<ScrollViewer HorizontalScrollBarVisibility="Visible">
        <local:DesignerCanvas x:Name="designerCanvas"  AllowDrop="True">
            <ItemsControl Name="Shapes">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        </local:DesignerCanvas>
    </ScrollViewer>

また、DesignerCanvas にグラフィックを直接追加/削除するたびに、スクロールビューアが適切に動作します! 私がそうでないことを除いて。私はMVVMアプローチを取っているので、キャンバスをObservableCollectionにデータバインドしています

    private ObservableCollection<Shape> shapes = new ObservableCollection<Shape>();
    public ObservableCollection<Shape> Shapes 
    {
        get { return shapes; }
        set { shapes = value; }
    }

オブザーバブル コレクションに項目が追加されるたびに、形状がキャンバスに表示されます。ただし、キャンバスのサイズが適切に変更されないため、スクロールビューアーが機能しなくなります。DesignerCanvas を直接操作していないため、これは予想されることであり、MeasureOverride は呼び出されません。

DesignerCanvas で InvalidateMeasure を呼び出すこともできますが、それにはビューモデルがビューに関する知識を持っている必要があります。MVVM を壊さずにこの問題を解決するにはどうすればよいですか?

編集:このような問題に対してこのパターンを維持する価値があるかどうかの推奨事項も聞きたいです。最近、MVVM は解決策よりも多くの問題を引き起こしているように感じます。

Edit2: ああ..わかりました、この問題は私が思っていたよりもずっと複雑でした。Florian Gl の提案から、ビューモデルでイベントを作成し、ビューでそのイベントを処理して、designerCanvas.InvalidateMeasure() を呼び出しました。

これにより、DesignerCanvas の MeasureOverride メソッドが呼び出されます。ただし、キャンバスのサイズが適切に調整されないため、scrollviewer はまだ機能しません。DesignerCanvas の子に直接図形を追加するのではなく、Canvas にデータバインドされている observablecollection に図形を追加していることに気付きました。したがって、 foreach ステートメントでループする UIElement はありません

    protected override Size MeasureOverride(Size constraint)
    {
        Size size = new Size();

        ItemsControl itemsControl = Children.OfType<ItemsControl>().First();


        foreach (UIElement element in Children) //No UIElements inside Children
        {
            double left = Canvas.GetLeft(element);
            ...

今、私は本当に立ち往生しています。この問題を回避するにはどうすればよいですか?

4

1 に答える 1

1

私のイベントのアイデア:

  1. ViewModel で独自のイベントを作成します。

    public delegate void MyCollectionChangedEventArgs();
    public event MyCollectionChangedEventArgs MyCollectionChanged;
    
  2. ViewModel でイベントを呼び出す:

    if (MyCollectionChanged != null)
    {
        MyCollectionChanged();
    }
    
  3. ビューにイベントについて知らせます。

        MainWindowViewModel vm = new MainWindowViewModel();
        vm.MyCollectionChanged += new MainWindowViewModel.MyCollectionChangedEventArgs(MainWindowViewModel_MyCollectionChanged);
    

    また

        MainWindowViewModel vm = (MainWindowViewModel)DataContext;
        vm.MyCollectionChanged += new MainWindowViewModel.MyCollectionChangedEventArgs(MainWindowViewModel_MyCollectionChanged);
    
  4. 通話方法:

    void MainWindowViewModel_MyCollectionChanged()
    {
        designerCanvas.InvalidateMeasure()
    }
    

編集: 私の次の提案が MeasureOverride-Method で機能しているかどうかはわかりませんが、試すことができます:

    (MainWindowViewModel) mvm = (MainWindowViewModel)DataContext; 

    foreach (UIElement element in mvm.Shapes)
    { ... }
于 2012-09-25T09:20:54.593 に答える