2015-09-10 1 views
1

У меня есть проект WPF, который рисует несколько вещей в панели. Для следующего выпуска мне нужно добавить еще один тип вещей, чтобы рисовать в дополнение к существующим вещам. В настоящее время у меня есть сетка, которая содержит элемент ItemsControl, который содержит ItemsPanel и ItemSource. Существующий элемент ItemsSource выглядит примерно так:в WPF Мне нужно иметь коллекцию коллекции объектов рисования

    <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? Я не понимаю, в каком направлении я должен идти.

EDIT:

Хорошо, я создал программу корректуры из концепции, что я надеюсь, что отвечает требованиям Питера. Я предоставлю код ниже. Она состоит из пяти файлов: LineArt.cs ObstacleArt.cs MainWindowResource.cs MainWindow.xaml.cs MainWindow.xaml

Объект LineArt представляет данные, необходимые, чтобы нарисовать одну линию. Объект ObstacleArt представляет собой набор строк и перевод и поворот для этих строк. Я хочу иметь возможность нарисовать коллекцию линий (без трансляции или вращения) плюс набор препятствий, каждый из которых имеет набор линий.

Я попытался использовать предложение Питера, чтобы поместить CompositeCollection внутри свойства Collection CollectionContainer, который находится внутри CompositeCollection. Вы можете увидеть его в файле xaml внизу.

В свойстве Path коллекции, когда я помещаю «Препятствия [0] .Lines», он нарисует линии для первого препятствия. Но когда я вынимаю индекс и говорю «Obstacles.Lines», он не будет рисуй что угодно. Мне нужна способность рисовать все линии всех препятствий.

В дополнение к этому мне нужно будет установить перевод и поворот для каждого набора строк.

Вот файлы, вы должны быть способны их скомпилировать и запустить.

LineArt.cs

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 
    } 
} 

ObstacleArt.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 
    } 
} 

MainWindowResource.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 
    } 
} 

MainWindow.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(); 
      } 
     } 
    } 
} 

MainWindow.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> 
+0

Да, есть способ вложить коллекции. Это называется 'CompositeCollection', и вы используете это прямо здесь. «CompositeCollection» на самом деле представляет собой коллекцию и может быть коллекцией, используемой в «CollectionContainer». Из вашего вопроса неясно, пробовали ли вы это и/или что такое ваша _специфическая проблема. Это поможет, если вы предоставите [хороший, _minimal_, _complete_ пример кода] (https://stackoverflow.com/help/mcve), который четко показывает, что вы пробовали, а также подробное объяснение того, что делает код и как это отличается от того, что вы хотите. –

+0

Питер, Спасибо за ответ. Я думал, что я представил то, о чем вы просите. Пример, который я предоставил из того, что я пробовал, является минимальным и полным. Я также представил подробное объяснение того, что делает код и как это отличается от того, что я хочу. Как вы можете видеть в коде, который я предоставил, я уже пользуюсь CompositeCollection. Проблема в том, что мне нужно иметь коллекцию объектов, которые имеют дополнительный уровень коллекции. Другими словами, мне нужно поставить CollectionContainer внутри существующего CollectionContainer. Это невозможно, так как подойти к этому? – dtaylor

+0

К сожалению, приведенный вами пример не является ни минимальным, ни полным. Если бы он был минимальным, он показывал бы только одну вложенную коллекцию; ваш пример показывает не менее _seven_ коллекций в составной коллекции. Если бы это было полно, можно было бы скопировать/вставить код в пустой проект VS, скомпилировать его и увидеть, как он запускается. Ни то, ни другое не верны.Прочтите приведенную мной ссылку для получения подробной информации о том, какой пример кода действительно подходит. –

ответ

1

Спасибо вашего для улучшенного примера коды. Из-за этого мне кажется, что вы идете по своей цели неправильно.

То есть вы пытаетесь создать один объект ItemsControl, который отобразит все ваши линии. Но это не так, как организована ваша модель данных. Ваша модель данных имеет два совершенно разных типа объектов: LineArt объектов и объект ObstacleArt, содержащий LineArt объектов.

Учитывая, что мне кажется, что более подходящим подходом было бы просто составить коллекции MainWindowResource.Lines и MainWindowResource.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 пунктов, это завершается ссылкой на DataTemplate для LineArt.

Вышеуказанные работы без каких-либо изменений для вашего кода. Тем не менее, я считаю, что вам было бы лучше сделать ваши классы наследовать DependencyObject, а затем создать свойства зависимых свойств свойств. WPF действительно поддерживает INotifyPropertyChanged, но у вас есть много явного кода уведомления о свойствах, который просто исчезнет, ​​если вы используете парадигму зависимости-свойства.

+0

Питер, Большое вам спасибо. Это был мой первоначальный план Б. Должен был мой план А. Я сбился с пути, не понимая, что мне нужно использовать другую панель и холст. Теперь это имеет смысл. Кроме того, я возьму ваше предложение об DependancyObject. Мне стало больно известно о явных уведомлениях о свойствах. Еще раз спасибо. – dtaylor

Смежные вопросы