簡體   English   中英

在 WPF 中,我需要有一組繪圖對象的集合

[英]in WPF I need to have a collection of a collection of drawing objects

我有一個 WPF 項目,它在面板中繪制了幾件東西。 對於下一個版本,除了現有的東西之外,我還需要添加另一種類型的東西來繪制。 目前我有一個包含 ItemsControl 的網格,其中包含一個 ItemsPanel 和一個 ItemsSource。 現有的 ItemsSource 看起來像這樣:

                    <ItemsControl.ItemsSource>
                    <CompositeCollection>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=DottedLines,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=BarrierLines,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=ProjectedLines,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=ProjectedCranes,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=CraneConfigs,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=Sightlines,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=CraneCenters,
                                    Mode=OneWay}"/>
                    </CompositeCollection>
                </ItemsControl.ItemsSource>

大多數集合都是線或多邊形。 我定義了 DataTemplates 以將繪圖對象的屬性綁定到支持對象。 BarrierLine 對象的示例如下所示:

        <DataTemplate DataType="{x:Type c:BarrierLineArt}">
        <Line
            X1="{Binding Path=AX}"
            Y1="{Binding Path=AY}"
            X2="{Binding Path=BX}"
            Y2="{Binding Path=BY}"
            Stroke="{Binding Path=LineColor}"
            StrokeThickness="{Binding Path=ScaledWeight}"
            StrokeEndLineCap="Round"
            StrokeStartLineCap="Round">
        </Line>
    </DataTemplate>

這一切都很好。 現在,除了現有的東西之外,我還需要添加一組要繪制的東西。 這個新東西有一個線條集合和一個平移和旋轉值。 不幸的是,我需要繪制這些新事物的集合。 每個實例都有自己的平移和旋轉以及線條集合。 實際上,我現在有一系列行的集合。 有沒有辦法嵌套 CollectionContainers? 我應該嘗試向 DataTemplate 添加一個集合嗎? 我不知該往哪個方向走。

編輯:

好的,我創建了一個概念驗證程序,希望它符合 Peter 的要求。 我將在下面提供代碼。 它由五個文件組成: LineArt.cs ObstacleArt.cs MainWindowResource.cs MainWindow.xaml.cs MainWindow.xaml

LineArt 對象表示繪制單條線所需的數據。 ObstacleArt 對象表示一組線條以及這些線條的平移和旋轉。 我希望能夠繪制一組線(沒有平移或旋轉)加上一組障礙物,每個障礙物都有一組線。

我嘗試使用 Peter 的建議將 CompositeCollection 放在 CompositeCollection 內的 CollectionContainer 的 Collection 屬性中。 您可以在底部附近的 xaml 文件中看到它。

在集合的 Path 屬性中,當我放置“Obstacles[0].Lines”時,它將繪制第一個障礙物的線條,但是當我取出索引並說“Obstacles.Lines”時,它不會繪制任何東西。 我需要的是能夠畫出所有障礙的所有線條。

除此之外,我需要為每組線設置平移和旋轉。

這是文件,您應該能夠編譯並運行它。

藝術線條

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;

namespace POC_WPF_nestedDrawingObjects
{
    public class LineArt : INotifyPropertyChanged
    {
        private Int32 _id = int.MinValue;
        private double _ax = 0;
        private double _ay = 0;
        private double _bx = 0;
        private double _by = 0;
        private SolidColorBrush _lineColor = Brushes.Black;
        private double _weight = 1;
        private double _scaledWeight = 1;

        public LineArt(
            Int32 id,
            double ax,
            double ay,
            double bx,
            double by,
            SolidColorBrush lineColor)
        {
            _id = id;
            _ax = ax;
            _ay = ay;
            _bx = bx;
            _by = by;
            _lineColor = lineColor;
            _weight = 1;
            _scaledWeight = _weight;
        }

        public Int32 Id { get { return _id; } }
        public double AX
        {
            get { return _ax; }
            set
            {
                _ax = value;
                SetPropertyChanged("AX");
            }
        }
        public double AY
        {
            get { return _ay; }
            set
            {
                _ay = value;
                SetPropertyChanged("AY");
            }
        }
        public double BX
        {
            get { return _bx; }
            set
            {
                _bx = value;
                SetPropertyChanged("BX");
            }
        }
        public double BY
        {
            get { return _by; }
            set
            {
                _by = value;
                SetPropertyChanged("BY");
            }
        }
        public SolidColorBrush LineColor
        {
            get { return _lineColor; }
            set
            {
                _lineColor = value;
                SetPropertyChanged("LineColor");
            }
        }
        public double Weight
        {
            get { return _weight; }
            set
            {
                _weight = value;
                SetPropertyChanged("Weight");
            }
        }
        public double ScaledWeight
        {
            get { return _scaledWeight; }
            set
            {
                _scaledWeight = value;
                SetPropertyChanged("ScaledWeight");
            }
        }

        #region INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged;
        private void SetPropertyChanged(string prop)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(
                    this,
                    new PropertyChangedEventArgs(prop));
            }
        }

        #endregion INotifyPropertyChanged implementation
    }
}

障礙藝術.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace POC_WPF_nestedDrawingObjects
{
    public class ObstacleArt : INotifyPropertyChanged
    {
        private Int32 _id = int.MinValue;
        private ObservableCollection<LineArt> _lines
            = new ObservableCollection<LineArt>();
        private double _translateX = 0;
        private double _translateY = 0;
        private double _rotateAngle = 0;

        public ObstacleArt(
            Int32 id,
            double translateX,
            double translateY,
            double rotateAngle)
        {
            _id = id;
            _translateX = translateX;
            _translateY = translateY;
            _rotateAngle = rotateAngle;
        }

        public Int32 Id
        {
            get { return _id; }
        }
        public double TranslateX
        {
            get { return _translateX; }
            set
            {
                _translateX = value;
                SetPropertyChanged("TranslateX");
            }
        }
        public double TranslateY
        {
            get { return _translateX; }
            set
            {
                _translateX = value;
                SetPropertyChanged("TranslateX");
            }
        }
        public double RotateAngle
        {
            get { return _rotateAngle; }
            set
            {
                _rotateAngle = value;
                SetPropertyChanged("RotateAngle");
            }
        }
        public ObservableCollection<LineArt> Lines
        {
            get { return _lines; }
        }

        public void NotifyLinesChanged()
        {
            SetPropertyChanged("Lines");
        }

        public void LinesAddPropertyChangedHandlers()
        {
            foreach (LineArt line in _lines)
            {
                line.PropertyChanged += line_PropertyChanged;
            }
        }

        private void line_PropertyChanged(
            object sender,
            PropertyChangedEventArgs e)
        {
            SetPropertyChanged(e.PropertyName);
        }

        public void LinesDelPropertyChangedHandlers()
        {
            foreach (LineArt line in _lines)
            {
                line.PropertyChanged -= line_PropertyChanged;
            }
        }

        #region INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged;
        private void SetPropertyChanged(string prop)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(
                    this,
                    new PropertyChangedEventArgs(prop));
            }
        }

        #endregion INotifyPropertyChanged implementation
    }
}

主窗口資源.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace POC_WPF_nestedDrawingObjects
{
    public class MainWindowResource : INotifyPropertyChanged
    {
        private ObservableCollection<LineArt> _lines
            = new ObservableCollection<LineArt>();
        private ObservableCollection<ObstacleArt> _obstacles
            = new ObservableCollection<ObstacleArt>();

        public ObservableCollection<LineArt> Lines
        {
            get
            {
                return _lines;
            }
        }

        public ObservableCollection<ObstacleArt> Obstacles
        {
            get
            {
                return _obstacles;
            }
        }

        public void NotifyLinesChanged()
        {
            SetPropertyChanged("Lines");
        }

        public void NotifyObstaclesChanged()
        {
            SetPropertyChanged("Obstacles");
        }

        public void LinesAddPropertyChangedHandlers()
        {
            foreach (LineArt line in _lines)
            {
                line.PropertyChanged += line_PropertyChanged;
            }
        }

        public void LinesDelPropertyChangedHandlers()
        {
            foreach (LineArt line in _lines)
            {
                line.PropertyChanged -= line_PropertyChanged;
            }
        }

        private void line_PropertyChanged(
            object sender,
            PropertyChangedEventArgs e)
        {
            SetPropertyChanged(e.PropertyName);
        }

        #region INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged;
        private void SetPropertyChanged(string prop)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(
                    this,
                    new PropertyChangedEventArgs(prop));
            }
        }

        #endregion INotifyPropertyChanged implementation
    }
}

主窗口.xaml.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace POC_WPF_nestedDrawingObjects
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private MainWindowResource _mainWindowResource = null;
        private SolidColorBrush _brush
            = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));

        public MainWindow()
        {
            LineArt line = null;
            ObstacleArt obstacle = null;

            InitializeComponent();

            Application app = Application.Current;
            _mainWindowResource
                = (MainWindowResource)this.Resources["MainWindowResource"];

            // initialize four lines, they will form a rectangle
            _mainWindowResource.LinesDelPropertyChangedHandlers();
            line = new LineArt(1, 10, 10, 30, 10, _brush);
            _mainWindowResource.Lines.Add(line);
            line = new LineArt(2, 30, 10, 30, 20, _brush);
            _mainWindowResource.Lines.Add(line);
            line = new LineArt(2, 30, 20, 10, 20, _brush);
            _mainWindowResource.Lines.Add(line);
            line = new LineArt(2, 10, 20, 10, 10, _brush);
            _mainWindowResource.Lines.Add(line);
            _mainWindowResource.LinesAddPropertyChangedHandlers();
            _mainWindowResource.NotifyLinesChanged();

            // initialize an obstacle made of four lines.
            // the lines form a 20x20 square around 0,0.
            // the obstacle should be trastlated to 50,50
            // and not rotated
            obstacle = new ObstacleArt(1, 50, 50, 0);
            obstacle.LinesDelPropertyChangedHandlers();
            line = new LineArt(1, -10, 10, 10, 10, _brush);
            obstacle.Lines.Add(line);
            line = new LineArt(2, 10, 10, 10, -10, _brush);
            obstacle.Lines.Add(line);
            line = new LineArt(3, 10, -10, -10, -10, _brush);
            obstacle.Lines.Add(line);
            line = new LineArt(4, -10, -10, -10, 10, _brush);
            obstacle.Lines.Add(line);
            obstacle.LinesAddPropertyChangedHandlers();
            _mainWindowResource.Obstacles.Add(obstacle);

            // initialize an obstacle made of four lines.
            // the lines form a 20x20 square around 0,0.
            // the obstacle should be trastlated to 100,100
            // and rotated to a 45 degree angle
            obstacle = new ObstacleArt(1, 100, 100, 45);
            obstacle.LinesDelPropertyChangedHandlers();
            line = new LineArt(1, -10, 10, 10, 10, _brush);
            obstacle.Lines.Add(line);
            line = new LineArt(2, 10, 10, 10, -10, _brush);
            obstacle.Lines.Add(line);
            line = new LineArt(3, 10, -10, -10, -10, _brush);
            obstacle.Lines.Add(line);
            line = new LineArt(4, -10, -10, -10, 10, _brush);
            obstacle.Lines.Add(line);
            obstacle.LinesAddPropertyChangedHandlers();
            _mainWindowResource.Obstacles.Add(obstacle);

            _mainWindowResource.NotifyObstaclesChanged();
            _mainWindowResource.NotifyLinesChanged();
            foreach(ObstacleArt obstacleArt in _mainWindowResource.Obstacles)
            {
                obstacleArt.NotifyLinesChanged();
            }
        }
    }
}

主窗口.xaml

<Window x:Class="POC_WPF_nestedDrawingObjects.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:c="clr-namespace:POC_WPF_nestedDrawingObjects"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <c:MainWindowResource x:Key="MainWindowResource"/>
        <DataTemplate DataType="{x:Type c:LineArt}">
            <Line
                X1="{Binding Path=AX}"
                Y1="{Binding Path=AY}"
                X2="{Binding Path=BX}"
                Y2="{Binding Path=BY}"
                Stroke="{Binding Path=LineColor}"
                StrokeThickness="{Binding Path=ScaledWeight}"
                StrokeEndLineCap="Round"
                StrokeStartLineCap="Round">
            </Line>
        </DataTemplate>
        <Style x:Key="ContentCanvasStyle" TargetType="Canvas">
            <Setter Property="RenderTransformOrigin" Value="0,0"/>
        </Style>
    </Window.Resources>
    <Grid>
        <ItemsControl>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas x:Name="ContentCanvas"
                        Style="{StaticResource ContentCanvasStyle}">
                    </Canvas>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemsSource>
                <CompositeCollection>
                    <CollectionContainer
                        Collection="{Binding
                            Source={StaticResource MainWindowResource},
                            Path=Lines,
                            Mode=OneWay}"/>
                    <CollectionContainer>
                        <CollectionContainer.Collection>
                        <CompositeCollection>
                            <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=Obstacles[0].Lines,
                                    Mode=OneWay}"/>
                            </CompositeCollection>
                        </CollectionContainer.Collection>
                    </CollectionContainer>
                </CompositeCollection>
            </ItemsControl.ItemsSource>
        </ItemsControl>
    </Grid>
</Window>

感謝您改進的代碼示例。 從那以后,在我看來,你的目標完全是錯誤的。

也就是說,您試圖讓單個ItemsControl對象呈現您的所有線條。 但這不是您的數據模型的組織方式。 您的數據模型有兩種完全不同的對象: LineArt對象和包含LineArt對象的ObstacleArt對象。

鑒於此,在我看來,更合適的方法是簡單地組合MainWindowResource.LinesMainWindowResource.Obstacles集合,然后使用數據模板將這些集合適當地顯示在一起。

這是您的 XAML 的新版本,顯示了我的意思:

<Window x:Class="POC_WPF_nestedDrawingObjects.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:c="clr-namespace:POC_WPF_nestedDrawingObjects"
        Title="MainWindow" Height="350" Width="525">

  <Window.Resources>
    <c:MainWindowResource x:Key="MainWindowResource"/>
    <Style x:Key="ContentCanvasStyle" TargetType="Canvas">
      <Setter Property="RenderTransformOrigin" Value="0,0"/>
    </Style>
    <DataTemplate DataType="{x:Type c:LineArt}">
      <Line
                X1="{Binding Path=AX}"
                Y1="{Binding Path=AY}"
                X2="{Binding Path=BX}"
                Y2="{Binding Path=BY}"
                Stroke="{Binding Path=LineColor}"
                StrokeThickness="{Binding Path=ScaledWeight}"
                StrokeEndLineCap="Round"
                StrokeStartLineCap="Round">
      </Line>
    </DataTemplate>
    <DataTemplate DataType="{x:Type c:ObstacleArt}">
      <ItemsControl ItemsSource="{Binding Lines, Mode=OneWay}">
        <ItemsControl.ItemsPanel>
          <ItemsPanelTemplate>
            <Canvas x:Name="ContentCanvas"
                        Style="{StaticResource ContentCanvasStyle}">
            </Canvas>
          </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.RenderTransform>
          <TransformGroup>
            <RotateTransform Angle="{Binding RotateAngle}"/>
            <TranslateTransform X="{Binding TranslateX}" Y="{Binding TranslateY}"/>
          </TransformGroup>
        </ItemsControl.RenderTransform>
      </ItemsControl>
    </DataTemplate>
  </Window.Resources>
  <Grid>
    <ItemsControl>
      <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
          <Canvas x:Name="ContentCanvas"
                  Style="{StaticResource ContentCanvasStyle}">
          </Canvas>
        </ItemsPanelTemplate>
      </ItemsControl.ItemsPanel>
      <ItemsControl.ItemsSource>
        <CompositeCollection>
          <CollectionContainer
              Collection="{Binding
                  Source={StaticResource MainWindowResource},
                  Path=Lines,
                  Mode=OneWay}"/>
          <CollectionContainer
              Collection="{Binding
                  Source={StaticResource MainWindowResource},
                  Path=Obstacles,
                  Mode=OneWay}"/>
        </CompositeCollection>
      </ItemsControl.ItemsSource>
    </ItemsControl>
  </Grid>
</Window>

這里的關鍵是第二個DataTemplate ,目標類型為ObstacleArt 這允許主ItemsControl顯示復合集合中的各個ObstacleArt元素。 通過第二數據模板,它通過嵌套了一個全新的這樣做ItemsControl中每個ObstacleArt對象,如該ItemsControl處理所有呈現的的ObstacleArt對象。 請注意,由於該嵌套ItemsControl對象中的實際項目本身就是LineArt項目,因此最終會引用回LineArt類型的DataTemplate

上述工作無需對您的代碼隱藏進行任何更改。 也就是說,我認為最好讓您的類繼承DependencyObject ,然后使可綁定屬性依賴屬性。 當然,WPF 確實支持INotifyPropertyChanged ,但是如果您使用依賴屬性范例,其中有很多顯式屬性通知代碼就會消失。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM