为了让我的画布自动调整其大小以便我的ScrollViewer正常工作,我创建了一个派生自Canvas的类,然后覆盖MeasureOverride方法,使画布根据画布上的UIElements调整其大小。
public class DesignerCanvas : Canvas
{
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;
base.InvalidateMeasure();
return size;
}
}
XAML:
<ScrollViewer HorizontalScrollBarVisibility="Visible">
<local:DesignerCanvas x:Name="designerCanvas" AllowDrop="True">
<ItemsControl Name="Shapes">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</local:DesignerCanvas>
</ScrollViewer>
每当我直接向DesignerCanvas添加/删除图形时,scrollviewer都能正常工作!除了我不是。我正在采用MVVM方法,所以我将我的画布数据绑定到ObservableCollection
private ObservableCollection<Shape> shapes = new ObservableCollection<Shape>();
public ObservableCollection<Shape> Shapes
{
get { return shapes; }
set { shapes = value; }
}
只要将项添加到observablecollection,形状就会显示在画布上。但是,画布没有正确调整大小,导致我的滚动查看器不再工作。这是预期的,因为我没有直接操作DesignerCanvas,因此永远不会调用MeasureOverride。
我可以在DesignerCanvas上调用InvalidateMeasure,但这需要我的viewmodel了解视图。如何在不破坏MVVM的情况下解决此问题?
编辑:如果实际上值得为这样的问题保留这种模式,我也想听听任何建议。最近,我觉得MVVM给我的问题多于解决方案。
Edit2:哦......好吧,这个问题比我想象的要复杂得多。根据Florian Gl的建议,我继续在我的viewmodel中创建了一个事件,然后在我的视图中处理该事件以调用designerCanvas.InvalidateMeasure()。
这会在DesignerCanvas中调用我的MeasureOverride方法。除非它没有正确调整我的画布大小,因此scrollviewer仍然无法正常工作。我注意到我不是直接向DesignerCanvas的子项添加形状,而是添加到canvasablecollection,它是数据绑定到Canvas。因此,在我的foreach语句中没有UIElement循环
protected override Size MeasureOverride(Size constraint)
{
Size size = new Size();
ItemsControl itemsControl = Children.OfType<ItemsControl>().First();
foreach (UIElement element in Children) //No UIElements inside Children
{
double left = Canvas.GetLeft(element);
...
现在我真的被卡住了。我该如何规避这个问题呢?
答案 0 :(得分:1)
我的活动 - 想法:
在ViewModel中创建自己的事件:
public delegate void MyCollectionChangedEventArgs();
public event MyCollectionChangedEventArgs MyCollectionChanged;
在ViewModel中调用事件:
if (MyCollectionChanged != null)
{
MyCollectionChanged();
}
让您的视图了解该事件:
MainWindowViewModel vm = new MainWindowViewModel();
vm.MyCollectionChanged += new MainWindowViewModel.MyCollectionChangedEventArgs(MainWindowViewModel_MyCollectionChanged);
或
MainWindowViewModel vm = (MainWindowViewModel)DataContext;
vm.MyCollectionChanged += new MainWindowViewModel.MyCollectionChangedEventArgs(MainWindowViewModel_MyCollectionChanged);
致电方法:
void MainWindowViewModel_MyCollectionChanged()
{
designerCanvas.InvalidateMeasure()
}
编辑: 我不知道我的以下建议是否在MeasureOverride-Method中有效,但您可以尝试:
(MainWindowViewModel) mvm = (MainWindowViewModel)DataContext;
foreach (UIElement element in mvm.Shapes)
{ ... }