我目前正在使用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>