迭代通过DataBounded Canvas项目来源就好像它们是画布的孩子一样

时间:2017-01-25 15:12:50

标签: c# wpf canvas mvvm data-binding

我目前正在使用C#和WPF构建一个图表设计器。我已阅读this文章,但发现缺少mvvm驱动的方法对我的要求不够好(而this文章恰好对我的需求来说太复杂了。)

我有一个基本数据类 - SimpleDataEntry.cs (包含颜色,名称,左侧位置和顶部位置)。 在我的 ViewModel 中,我有以下属性 -

public class ViewModel : INotifyPropertyChanged
{
    public ObservableCollection<SimpleDataEntry> SimpleDataEntryCollection{ get; set; }
}

在我的MainWindow.xaml中,我有一个画布,其中ItemsControl ItemSource绑定到ViewModel中的SimpleDataEntryCollection:

<Window ...>
<Window.Resources>
    <local:ViewModel x:Key="ViewModel"/>

    <ControlTemplate x:Key="MoveThumbTemplate"  TargetType="{x:Type local:MoveThumb}">
        <Rectangle Fill="Transparent"/>
    </ControlTemplate>

    <ControlTemplate x:Key="DesignerItemControlTemplate" TargetType="ContentControl">
        <Grid>
            <local:MoveThumb DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" 
                             Template="{StaticResource MoveThumbTemplate}" 
                             Cursor="SizeAll"/>
            <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
        </Grid>
    </ControlTemplate>
</Window.Resources>

<Grid>

<ScrollViewer Name="DesignerScrollViewer"
Background="Transparent"
    HorizontalScrollBarVisibility="Auto"
    VerticalScrollBarVisibility="Auto">

<local:DesignerCanvas x:Name="MyDesignerCanvas"
    MinHeight="800"
    MinWidth="1000"
    AllowDrop="True"
    Background="Beige">

    <ItemsControl ItemsSource="{Binding Source={StaticResource ViewModel}, Path=SimpleDataEntryCollection}">

        ...A Template for the itmes...

    </ItemsControl>
</local:DesignerCanvas>
</ScrollViewer>
</Grid>

画布源绑定到的项目模板显示为可以使用鼠标拖动的椭圆(使用 MoveThumb 类)。 让我们调用这里描述的每个项目

  

&#34;图表项目&#34;

以下是图表项的模板化方法:

<ItemsControl.ItemTemplate>
        <DataTemplate>
            <Canvas>
                <ContentControl Template="{StaticResource DesignerItemControlTemplate}" Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}">
                    <Grid>
                        <Ellipse Fill="{Binding Fill}" Width="50" Height="50" IsHitTestVisible="False"/>
                        <TextBlock Text="{Binding Data}" IsHitTestVisible="False"/>
                    </Grid>
                </ContentControl>
            </Canvas>
        </DataTemplate>
    </ItemsControl.ItemTemplate>

现在我想要我的画布,每次我将一个图表项移出其边界时,要调整大小以便它可以在其新位置包含图表项。为了达到这个目的,我使用sukram的一个类(来自这里提到的第一篇文章)称为&#34; DesignerCanvas &#34;覆盖方法&#34; MeasureOverride &#34;。通过这样做,DesignerCanvas会判断其边框是否能够包含其子位置,如果不能 - 相应地拉伸其边框。

        protected override Size MeasureOverride(Size constraint)
    {
        Size size = new Size();
        foreach (UIElement element in Children)
        {
            double left = Canvas.GetLeft(element);
            double top = Canvas.GetTop(element);
            left = double.IsNaN(left) ? 0 : left;
            top = double.IsNaN(top) ? 0 : top;

            element.Measure(constraint);

            Size desiredSize = element.DesiredSize;
            if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
            {
                size.Width = Math.Max(size.Width, left + desiredSize.Width);
                size.Height = Math.Max(size.Height, top + desiredSize.Height);
            }
        }

        // add some extra margin
        size.Width += 10;
        size.Height += 10;
        return size;
    }

这对我的方法不起作用。我的问题如下 - 在基于我的工作的示例中,Designer Itmes是Visual Only对象,后面没有逻辑表示,而在我的项目中,Designer Items是生活在viewmodel中的集合中的逻辑实体,我的DesignerCanvas作为项目源。 调试时我发现我的 DesignerCanvas 只有一个类型的子项:

  

{System.Windows.Controls.ItemsControl Items.Count:3}

     

//我创建了3个Designer项目并添加到viewModel中的SimpleDataEntryCollection

我认为这是因为我只是在viewmodel中添加一个新的Designer Item,而不是将生成的模板化Designer作为子项添加到Designer Canvas中。如果是这样,我应该在哪里插入画布部分的子项?(有没有办法让这个过程自动化?项目源中的任何元素都会自动添加为 DesignerCanvas 的子项?) 否则,是否有方法可以直接在 DesignerCanvas itemSource上作为集合进行迭代(就好像它是 DesignerCanvas 的子集合一样)?

更新2

我尝试过使用itemspanel作为DesignerCanvas。现在我的设计师画布上有ContentPresenter类型的子画面。当我拖动和移动DesignerItems时,只有当一个项目被添加到SimpleDataEntryCollection时,MeasureOverride()函数才会触发。 可能是什么原因?

<ItemsControl ItemsSource="{Binding Source={StaticResource ViewModel}, Path=SimpleDataEntryCollection}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <local:DesignerCanvas/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Canvas>
                            <ContentControl Template="{StaticResource DesignerItemControlTemplate}" Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}">
                                <Grid>
                                <Ellipse Fill="{Binding Fill}" Width="50" Height="50" IsHitTestVisible="False"/>
                                <TextBlock Text="{Binding Data}" IsHitTestVisible="False"/>
                            </Grid>
                        </ContentControl>
                        </Canvas>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

0 个答案:

没有答案