0

ItemsPanelがCanvasであるItemsControlがあります。データテンプレートは、境界線でラップされたキャンバスであり、追加の長方形が含まれています。これはItemsControlXAMLです。

<ItemsControl x:Name="Items" ItemsSource="{Binding TileResources, ElementName=ResourceWindow}" >
                <ItemsControl.ItemsPanel >
                    <ItemsPanelTemplate>
                        <Canvas Background="SkyBlue" Width="500" Height="500"  IsItemsHost="True" Loaded="Canvas_Loaded_1" MouseDown="Canvas_MouseDown" MouseUp="Canvas_MouseUp" MouseMove="Canvas_MouseMove"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <local:ResourceBorderControl x:Name="ResRect" BorderThickness="2"
                                MouseDown="selectionBox_MouseDown" MouseUp="selectionBox_MouseUp" >
                            <Canvas Width="{Binding Width, ElementName=ResRect}"  Height="{Binding Height, ElementName=ResRect}" Background="White" Opacity="0.1">
                                <Rectangle Stroke="Red" StrokeThickness="2"
                                    Canvas.Left="{Binding CollisionX, ElementName=ResRect}" 
                                    Canvas.Top="{Binding CollisionY, ElementName=ResRect}"
                                    Width="{Binding CollisionWidth, ElementName=ResRect}"
                                    Height="{Binding CollisionHeight, ElementName=ResRect}"
                                    />

                            </Canvas>

                        </local:ResourceBorderControl>


                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding IsSelected}" Value="true">
                                <Setter Property="BorderBrush" Value="Purple" TargetName="ResRect"/>
                            </DataTrigger>

                            <DataTrigger Binding="{Binding IsSelected}" Value="false">
                                <Setter Property="BorderBrush" Value="SeaGreen" TargetName="ResRect"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <ItemsControl.ItemContainerStyle>
                    <Style>
                        <Setter Property="Canvas.Left" Value="{Binding X}" />
                        <Setter Property="Canvas.Top" Value="{Binding Y}" />
                        <Setter Property="FrameworkElement.Width" Value="{Binding Width}" />
                        <Setter Property="FrameworkElement.Height" Value="{Binding Height}" />
                        <Setter Property="local:ResourceBorderControl.CollisionX" Value="{Binding CollideX}" />
                        <Setter Property="local:ResourceBorderControl.CollisionY" Value="{Binding CollideY}" />
                        <Setter Property="local:ResourceBorderControl.CollisionWidth" Value="{Binding CollideWidth}" />
                        <Setter Property="local:ResourceBorderControl.CollisionHeight" Value="{Binding CollideHeight}" />
                    </Style>
                </ItemsControl.ItemContainerStyle>
            </ItemsControl>

ご覧のとおり、私はカスタムボーダーコントロール--ResourceBorderControlを使用しています。内側の長方形を表す4つのプロパティが添付されています:CollisionX CollisionY CollisionWidth CollisionHeight

アタッチされたプロパティ(CollisionXなど)をXAMLでハードコーディングするだけで、正常に機能します。CanvasのRectangleは、希望どおりにレンダリングされます。

ItemsSourceからのレンダリングされたオブジェクトからの値で埋める必要があるため、スタイルを使用して(または可能であれば他の方法で)設定する必要があります。

そして、それはうまくいきません。WPFツリービジュアライザーでデバッグすることもできません。ResourceBorderControlオブジェクトを確認しようとすると、Collision値が表示されません。プログラムを実行すると、Border with Canvasのレンダリングが表示されます(不透明度が表示されます)が、何も表示されません。これらの値は、スタイルセッターによって設定された後に失われると思います。

だから私の質問は-私はここで何か間違ったことをしていますか?または、スタイルを使用してカスタムの添付値を設定できないのはWPFだけであり、別のアプローチを使用する必要がありますか?後者の場合、他にどのようにそれを行うことができますか?

編集:これは、ItemsSourceにバインドされているObservableCollectionにあるインスタンスのクラスのコードです。

public class ResourceModelBase : IResourceModel
{
    private String _name, _defaultLayer;
    private double _x, _y, _width, _height, _collideX, _collideY, _collideWidth, _collideHeight;
    private bool _isSelected;

    public ResourceModelBase(double x, double y, double width, double height)
    {
        X = x;
        Y = y;
        Width = width;
        Height = height;

        CollideX = 1;
        CollideY = 1;
        CollideWidth = 100;
        CollideHeight = 100; 

        IsSelected = false;

        Name = "RES_" + width.ToString() + height.ToString();
    }



    public void Select()
    {
        IsSelected = true;
    }

    public void Deselect()
    {
        IsSelected = false;
    }


    public new String ToString()
    {
        return Name;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public String Name
    {
        get
        {
            return _name;
        }

        set
        {
            if (value != _name)
            {
                _name = value;
                NotifyPropertyChanged();
            }
        }
    }

    public String DefaultLayer
    {
        get
        {
            return _defaultLayer;
        }

        set
        {
            if (value != _defaultLayer)
            {
                _defaultLayer = value;
                NotifyPropertyChanged();
            }
        }
    }

    public double X
    {
        get
        {
            return _x;
        }

        set
        {
            if (value != _x)
            {
                _x = value;
                NotifyPropertyChanged();
            }
        }
    }

    public double Y
    {
        get
        {
            return _y;
        }

        set
        {
            if (value != _y)
            {
                _y = value;
                NotifyPropertyChanged();
            }
        }
    }

    public double Width
    {
        get
        {
            return _width;
        }

        set
        {
            if (value != _width)
            {
                _width = value;
                NotifyPropertyChanged();
            }
        }
    }

    public double Height
    {
        get
        {
            return _height;
        }

        set
        {
            if (value != _height)
            {
                _height = value;
                NotifyPropertyChanged();
            }
        }
    }

    public bool IsSelected
    {
        get
        {
            return _isSelected;
        }

        set
        {
            if (value != _isSelected)
            {
                _isSelected = value;
                NotifyPropertyChanged();
            }
        }
    }

    public double CollideX
    {
        get
        {
            return _collideX;
        }

        set
        {
            if (value != _collideX)
            {
                _collideX = value;
                NotifyPropertyChanged();
            }
        }
    }

    public double CollideY
    {
        get
        {
            return _collideY;
        }

        set
        {
            if (value != _collideY)
            {
                _collideY = value;
                NotifyPropertyChanged();
            }
        }
    }

    public double CollideWidth
    {
        get
        {
            return _collideWidth;
        }

        set
        {
            if (value != _collideWidth)
            {
                _collideWidth = value;
                NotifyPropertyChanged();
            }
        }
    }

    public double CollideHeight
    {
        get
        {
            return _collideHeight;
        }

        set
        {
            if (value != _collideHeight)
            {
                _collideHeight = value;
                NotifyPropertyChanged();
            }
        }
    }
}

ご覧のとおり、ほとんどの場合、パブリックプロパティに加えて、INotifyPropertyChangedインターフェイスの実装があります。

添付プロパティの定義:

public static readonly DependencyProperty CollisionX = DependencyProperty.RegisterAttached(
      "CollisionX",
      typeof(double),
      typeof(ResourceBorderControl),
      new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender)
    );

    public static void SetCollisionX(UIElement element, object value)
    {
        element.SetValue(CollisionX, (double)value);
    }

    public static double GetCollisionX(UIElement element)
    {
        return (double)element.GetValue(CollisionX);
    }
4

2 に答える 2

2

アタッチされたプロパティは問題ないように見えますが、ItemTemplateとItemContainerStyleの関係を誤解していると思います。ItemContainerStyleでバインドしているプロパティを設定し、アイテムコンテナに挿入されるDataTemplate内のResourceBorderControlにそれらを表示しようとしているようです。現在持っているもので実際に得ているのは、次のようなビジュアルツリーです。

ItemsControl
 ItemsPresenter
  Canvas
   ContentPresenter for item 1 (ResourceBorderControl.CollisionX = bound value, etc)
    ResourceBorderControl
     Canvas
      ...
   ContentPresenter for item 2 (ResourceBorderControl.CollisionX = bound value, etc)
    ResourceBorderControl
     Canvas
      ...

最終的な目標に応じて、これを処理する方法は2つあります。プロパティをResourceBorderControlに表示するだけの場合は、DP定義を変更して追加の継承フラグを含めることにより、プロパティの継承を利用できます。

public static readonly DependencyProperty CollisionX = DependencyProperty.RegisterAttached(
      "CollisionX",
      typeof(double),
      typeof(ResourceBorderControl),
      new FrameworkPropertyMetadata(0.0,
          FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender)
    );

これにより、オーバーライドまたはクリアされない限り、ContentPresenterItemContainerの下の各子にプロパティが設定されます。

もう1つの選択肢は、ResourceBorderControlをコンテナーとして使用するカスタムItemsControlを派生させることです。これには、いくつかのメソッドをオーバーライドするだけで済みます。これは最小限の方法だGetContainerForItemOverrideと思います。IsItemItsOwnContainerOverrideこれにより、カスタムコントロールがItemsPanelのCanvasの直接の子であるという利点が得られ、さまざまなレイアウトオプションを開くことができます。

于 2013-01-26T02:28:14.617 に答える
0

回避策を見つける方法を見つけましたが、これは醜いハックなので、誰かが私を正しい方向に向けることができれば、それをいただければ幸いです:)。DataTriggersを追加したので、DataTemplate.TriggersXAMLノードは次のようになります。

<DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding IsSelected}" Value="true">
                                <Setter Property="BorderBrush" Value="Purple" TargetName="ResRect"/>
                            </DataTrigger>

                            <DataTrigger Binding="{Binding IsSelected}" Value="false">
                                <Setter Property="BorderBrush" Value="SeaGreen" TargetName="ResRect"/>
                            </DataTrigger>

                            <!-- very very ugly ugly hack :(-->
                            <DataTrigger Binding="{Binding IsSelected}" Value="true">
                                <Setter Property="Canvas.Left" Value="{Binding CollideX}" TargetName="CollideRect"/>
                                <Setter Property="Canvas.Top" Value="{Binding CollideY}" TargetName="CollideRect"/>
                                <Setter Property="FrameworkElement.Width" Value="{Binding CollideWidth}" TargetName="CollideRect"/>
                                <Setter Property="FrameworkElement.Height" Value="{Binding CollideHeight}" TargetName="CollideRect"/>
                            </DataTrigger>

                            <DataTrigger Binding="{Binding IsSelected}" Value="false">
                                <Setter Property="Canvas.Left" Value="{Binding CollideX}" TargetName="CollideRect"/>
                                <Setter Property="Canvas.Top" Value="{Binding CollideY}" TargetName="CollideRect"/>
                                <Setter Property="FrameworkElement.Width" Value="{Binding CollideWidth}" TargetName="CollideRect"/>
                                <Setter Property="FrameworkElement.Height" Value="{Binding CollideHeight}" TargetName="CollideRect"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>

私はそれについて満足していませんが、少なくともそれは機能します。

于 2013-01-25T23:49:17.683 に答える