WPF绘制集合中的矩形列表

huangapple go评论59阅读模式
英文:

WPF Drawing a list of rectangles in a collection

问题

XAML:

<Grid>
    <ItemsControl Name="Items" ItemsSource="{Binding Items, Mode=TwoWay}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <local:ItemView Visibility="Visible">
                    <local:ItemView.Background>
                        <SolidColorBrush Color="{Binding ItemColor}"/>
                    </local:ItemView.Background>
                </local:ItemView>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="{x:Type ContentPresenter}">
                <Setter Property="Canvas.Left" Value="{Binding Left}"/>
                <Setter Property="Canvas.Top" Value="{Binding Top}"/>
                <Setter Property="FrameworkElement.Width" Value="{Binding Width}"/>
                <Setter Property="FrameworkElement.Height" Value="{Binding Height}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Grid>

请注意,XAML部分的更改是将属性绑定从 ScaledRectangle.Left, ScaledRectangle.Top, ScaledRectangle.WidthScaledRectangle.Height 更改为 Left, Top, WidthHeight。这样做是因为您要更改 ScaledRectangle 为一个集合,而不再是单个属性。在视图模型中,您将需要在新的对象中使用 Left, Top, WidthHeight 属性,以便XAML可以正确绑定并绘制每个矩形。

英文:

My WPF app has a ViewModel that has an ObservableCollection that holds objects of type Item. Each Item has a color and a Rect that is drawn on the canvas:

Item Class:

public class Item
{
    public Color ItemColor {get; set;}
    public Rect ScaledRectangle {get; set;}
}


XAML:

&lt;Grid&gt;
	&lt;ItemsControl Name=&quot;Items&quot; ItemsSource=&quot;{Binding Items, Mode=TwoWay}&quot;&gt;
		&lt;ItemsControl.ItemTemplate&gt;
		    &lt;DataTemplate&gt;
				&lt;local:ItemView  Visibility=&quot;Visible&quot;&gt;
					&lt;local:ItemView.Background&gt;
						&lt;SolidColorBrush Color=&quot;{Binding ItemColor}&quot;/&gt;
						&lt;/local:ItemView.Background&gt;
					&lt;/local:ItemView&gt;
				&lt;/DataTemplate&gt;
			&lt;/ItemsControl.ItemTemplate&gt;
			&lt;ItemsControl.ItemContainerStyle&gt;
				&lt;Style TargetType=&quot;{x:Type ContentPresenter}&quot;&gt;
					&lt;Setter Property=&quot;Canvas.Left&quot; Value=&quot;{Binding ScaledRectangle.Left}&quot;/&gt;
					&lt;Setter Property=&quot;Canvas.Top&quot; Value=&quot;{Binding ScaledRectangle.Top}&quot;/&gt;
					&lt;Setter Property=&quot;FrameworkElement.Width&quot; Value=&quot;{Binding ScaledRectangle.Width}&quot;/&gt;
					&lt;Setter Property=&quot;FrameworkElement.Height&quot; Value=&quot;{Binding ScaledRectangle.Height}&quot;/&gt;
				&lt;/Style&gt;
			&lt;/ItemsControl.ItemContainerStyle&gt;
			&lt;ItemsControl.ItemsPanel&gt;
				&lt;ItemsPanelTemplate&gt;
					&lt;Canvas /&gt;
				&lt;/ItemsPanelTemplate&gt;
			&lt;/ItemsControl.ItemsPanel&gt;
		&lt;/ItemsControl&gt;
	&lt;/Grid&gt;

In my ViewModel, all I have to do is add a new Item to the ObservableCollection to draw it on the screen.

This works really well but now I find I need to change the ScaledRectangle property to some kind of collection. I want to modify this XAML to draw each rectangle in the ScaledRectangles collection. Can I modify this XAML so I can keep the ViewModel functionality to something like viewModel.AddNewItem(newItem)?

答案1

得分: 0

你必须修改你的 ItemsView 以支持处理 Rect 的集合,而不是单个 Rect

ItemsView.cs

public class ItemsView : Control
{
  public IEnumerable<Rect> DataSources
  {
    get => (IEnumerable<Rect>)GetValue(DataSourcesProperty);
    set => SetValue(DataSourcesProperty, value);
  }

  public static readonly DependencyProperty DataSourcesProperty = DependencyProperty.Register(
    "DataSources",
    typeof(IEnumerable<Rect>),
    typeof(ItemsView),
    new PropertyMetadata(default(IEnumerable<Rect>), OnDataSourcesChanged));

  private Panel ItemsHost { get; set; }
  private Dictionary<Rect, int> ContainerIndexTable { get; }

  static ItemsView() 
    => DefaultStyleKeyProperty.OverrideMetadata(typeof(ItemsView), new FrameworkPropertyMetadata(typeof(ItemsView)));

  public ItemsView() 
    => this.ContainerIndexTable = new Dictionary<Rect, int>();

  private static void OnDataSourcesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
    var this_ = d as ItemsView;
    this_.UnloadRectangles(e.OldValue as IEnumerable<Rect>);
    this_.LoadRectangles(e.NewValue as IEnumerable<Rect>);
  }

  public override void OnApplyTemplate()
  {
    base.OnApplyTemplate();      
    this.ItemsHost = GetTemplateChild("PART_ItemsHost") as Panel;
    LoadRectangles(this.DataSources);
  }

  private void UnloadRectangles(IEnumerable<Rect> items)
  {
    if (items is null || this.ItemsHost is null)
    {
      return;
    }

    foreach (Rect rectangleDefinition in items)
    {
      if (this.ContainerIndexTable.TryGetValue(rectangleDefinition, out int containerIndex))
      {
        this.ItemsHost.Children.RemoveAt(containerIndex);
      }
    }
  }

  private void LoadRectangles(IEnumerable<Rect> items)
  {
    if (items is null || this.ItemsHost is null)
    {
      return;
    }

    foreach (Rect rectangleDefinition in items)
    {
      var container = new Rectangle()
      {
        Height = rectangleDefinition.Height,
        Width = rectangleDefinition.Width,
        Fill = new SolidColorBrush(item.ItemColor)
      };

      Canvas.SetLeft(container, rectangleDefinition.Left);
      Canvas.SetTop(container, rectangleDefinition.Top);

      int containerIndex = this.ItemsHost.Children.Add(container);
      _ = this.ContainerIndexTable.TryAdd(rectangleDefinition, containerIndex);
    }
  }
}

Gernic.xaml

<Style TargetType="local:ItemsView">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ItemsView">
<Canvas x:Name="PART_ItemsHost" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

MainWindow.xaml

<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:Item}">
<local:ItemsView DataSources="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
英文:

You must modify your ItemsView to support handling of a collection of Rect instead of a single Rect:

ItemsView.cs

public class ItemsView : Control
{
  public Item DataSource
  {
    get =&gt; (Item)GetValue(DataSourceProperty);
    set =&gt; SetValue(DataSourceProperty, value);
  }

  public static readonly DependencyProperty DataSourceProperty = DependencyProperty.Register(
    &quot;DataSource&quot;,
    typeof(Item),
    typeof(ItemsView),
    new PropertyMetadata(default(Item), OnDataSourceChanged));

  private Panel ItemsHost { get; set; }
  private Dictionary&lt;Rect, int&gt; ContainerIndexTable { get; }
 
  static ItemsView() 
    =&gt; DefaultStyleKeyProperty.OverrideMetadata(typeof(ItemsView), new FrameworkPropertyMetadata(typeof(ItemsView)));

  public ItemsView() 
    =&gt; this.ContainerIndexTable = new Dictionary&lt;Rect, int&gt;();

  private static void OnDataSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
    var this_ = d as ItemsView;
    this_.UnloadRectangles(e.OldValue as Item);
    this_.LoadRectangles(e.NewValue as Item);
  }

  public override void OnApplyTemplate()
  {
    base.OnApplyTemplate();      
    this.ItemsHost = GetTemplateChild(&quot;PART_ItemsHost&quot;) as Panel;
    LoadRectangles(this.DataSource);
  }

  private void UnloadRectangles(Item item)
  {
    if (item is null
      || this.ItemsHost is null)
    {
      return;
    }

    foreach (Rect rectangleDefinition in item.ScaledRectangles)
    {
      if (this.ContainerIndexTable.TryGetValue(rectangleDefinition, out int containerIndex))
      {
        this.ItemsHost.Children.RemoveAt(containerIndex);
      }
    }
  }

  private void LoadRectangles(Item item)
  {
    if (item is null
      || this.ItemsHost is null)
    {
      return;
    }

    foreach (Rect rectangleDefinition in item.ScaledRectangles)
    {
      var container = new Rectangle()
      {
        Height = rectangleDefinition.Height,
        Width = rectangleDefinition.Width,
        Fill = new SolidColorBrush(item.ItemColor)
      };

      Canvas.SetLeft(container, rectangleDefinition.Left);
      Canvas.SetTop(container, rectangleDefinition.Top);

      int containerIndex = this.ItemsHost.Children.Add(container);
      _ = this.ContainerIndexTable.TryAdd(rectangleDefinition, containerIndex);
    }
  }
}

Gernic.xaml

&lt;Style TargetType=&quot;local:ItemsView&quot;&gt;
&lt;Setter Property=&quot;Template&quot;&gt;
&lt;Setter.Value&gt;
&lt;ControlTemplate TargetType=&quot;local:ItemsView&quot;&gt;
&lt;Canvas x:Name=&quot;PART_ItemsHost&quot; /&gt;
&lt;/ControlTemplate&gt;
&lt;/Setter.Value&gt;
&lt;/Setter&gt;
&lt;/Style&gt;

MainWindow.xaml

&lt;ItemsControl&gt;
&lt;ItemsControl.ItemTemplate&gt;
&lt;DataTemplate DataType=&quot;{x:Type local:Item}&quot;&gt;
&lt;local:ItemsView DataSource=&quot;{Binding}&quot; /&gt;
&lt;/DataTemplate&gt;
&lt;/ItemsControl.ItemTemplate&gt;
&lt;/ItemsControl&gt;

huangapple
  • 本文由 发表于 2023年2月10日 03:26:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/75403502.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定