in WPF I need to have a collection of a collection of drawing objects

前端 未结 1 345
后悔当初
后悔当初 2021-01-24 18:52

I have a WPF project that draws several things in a panel. For the next release I need to add another type of thing to draw in addition to the existing things. Currently I hav

相关标签:
1条回答
  • 2021-01-24 19:42

    Thank your for the improved code example. From that, it appears to me that you are going about your goal the wrong way entirely.

    That is, you are trying to have a single ItemsControl object render all of your lines. But that's not how your data model is organized. Your data model has two completely different kinds of objects: LineArt objects, and the ObstacleArt object that contains LineArt objects.

    Given that, it seems to me that a more appropriate approach would be to simply compose the MainWindowResource.Lines and MainWindowResource.Obstacles collections, and then use data templates to appropriately display these collections together.

    Here is a new version of your XAML that shows what I mean:

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

    The key here is the second DataTemplate, with a target type of ObstacleArt. This allows the main ItemsControl to display the individual ObstacleArt elements in the composite collection. Via the second data template, it does so by nesting an entirely new ItemsControl within for each ObstacleArt object, where that ItemsControl handles all of the rendering for the ObstacleArt object. Note that since the actual items in that nested ItemsControl object are themselves LineArt items, this winds up referring back to the DataTemplate for the LineArt type.

    The above works without any changes at all to your code-behind. That said, it is my opinion that you would be better off making your classes inherit DependencyObject, and then make the bindable properties dependency properties. WPF does support INotifyPropertyChanged of course, but you have a lot of explicit property notification code in there that would just go away if you were using the dependency-property paradigm.

    0 讨论(0)
提交回复
热议问题