我试图缩放scrollviewer中的一些内容。
我正在寻找的缩放行为是RenderTransform + ScaleTransform。但这不适用于ScrollViewer。
使用LayoutTransform + ScaleTransform,滚动查看器会受到影响(仅限ContentTemplate1),但行为不像缩放。
假设无法更改ContentTemplate1 / ContentTemplate2(即第三方控件),如何缩放以使用滚动查看器?
<Grid>
<Grid.Resources>
<!-- Content type 1 -->
<DataTemplate x:Key="ContentTemplate1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Background="DodgerBlue" Text="Left"/>
<TextBlock Grid.Column="1" Background="DarkGray" Text="Right"/>
</Grid>
</DataTemplate>
<!-- Content type 2 -->
<DataTemplate x:Key="ContentTemplate2">
<Viewbox>
<TextBlock Background="DodgerBlue" Text="Scale to fit" Width="100" Height="70" Foreground="White" TextAlignment="Center"/>
</Viewbox>
</DataTemplate>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TabControl>
<!-- Content 1 -->
<TabControl.Resources>
<ScaleTransform x:Key="ScaleTransform"
ScaleX="{Binding ElementName=ZoomSlider,Path=Value}"
ScaleY="{Binding ElementName=ZoomSlider,Path=Value}" />
</TabControl.Resources>
<TabItem Header="Content 1">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ContentControl ContentTemplate="{StaticResource ContentTemplate1}" Margin="10" RenderTransformOrigin=".5,.5">
<!-- Affects scrollviewer, but does not behave like a zoom -->
<!--<FrameworkElement.LayoutTransform>
<StaticResource ResourceKey="ScaleTransform" />
</FrameworkElement.LayoutTransform>-->
<!-- Expected zoom behavior, but doesn't affect scrollviewer -->
<FrameworkElement.RenderTransform>
<StaticResource ResourceKey="ScaleTransform" />
</FrameworkElement.RenderTransform>
</ContentControl>
</ScrollViewer>
</TabItem>
<!-- Content 2 -->
<TabItem Header="Content 2">
<ContentControl ContentTemplate="{StaticResource ContentTemplate2}" Margin="10" RenderTransformOrigin=".5,.5">
<!-- Affects scrollviewer, but does not behave like a zoom -->
<!--<FrameworkElement.LayoutTransform>
<StaticResource ResourceKey="ScaleTransform" />
</FrameworkElement.LayoutTransform>-->
<!-- Expected zoom behavior, but doesn't affect scrollviewer -->
<FrameworkElement.RenderTransform>
<StaticResource ResourceKey="ScaleTransform" />
</FrameworkElement.RenderTransform>
</ContentControl>
</TabItem>
</TabControl>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<!-- Zoom -->
<Slider x:Name="ZoomSlider"
Width="100"
Maximum="5"
Minimum="0.1"
Orientation="Horizontal"
Value="1" />
<!-- Autofit -->
<CheckBox Content="Autofit?" x:Name="AutoFitCheckBox" />
</StackPanel>
</Grid>
答案 0 :(得分:1)
如果我理解正确:
ZoomSlider
滑块进行缩放吗?如果是这样,那就是你想要的LayoutTransform
。在测量和布局所有元素之前完成转换,ScrollViewer
将能够判断是否需要滚动条。
在我的计算机上,如果您只是切换到LayoutTransform
,“内容1”标签会按预期工作(请注意,在“正确”消失后,您必须进行大量缩放,切换滚动条):
“内容2”需要更多工作。首先,该选项卡中没有ScrollViewer
,因此需要添加。其次,ContentTemplate2
使用ViewBox
,默认情况下会延伸,因此缩放效果不会有效,直到您非常接近放大。要禁用ViewBox的内置“缩放”,您可以将ContentControl
容器(使用HorizontalAlignment/VerticalAlignment
)居中,这会迫使它占用尽可能少的空间:
<TabItem Header="Content 2">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ContentControl ContentTemplate="{StaticResource ContentTemplate2}" ...
HorizontalAlignment="Center" VerticalAlignment="Center" >
<FrameworkElement.LayoutTransform>
...
答案 1 :(得分:0)
为了使缩放的元素获得精确的RenderTransform
外观,我们可以坚持使用RenderTransform
,而是通过实现我们自己的滚动逻辑告诉ScrollViewer
如何表现。这种方法基于这个优秀的教程:
http://tech.pro/tutorial/907/wpf-tutorial-implementing-iscrollinfo
我们创建自己的自定义"ZoomableContentControl"
,它实现IScrollInfo
并告诉ScrollViewer
从那里获取其滚动逻辑(ScrollViewer.CanContentScroll = True)。魔术发生在我ArrangeOverride()
和ExtentWidth/ExtentHeight
的{{1}}中。
RenderTransformOrigin
用法:
public class ZoomableContentControl : ContentControl, IScrollInfo
{
public ZoomableContentControl()
{
this.RenderTransformOrigin = new Point(0.5, 0.5);
}
private ScaleTransform _scale = null;
private ScaleTransform Scale
{
get
{
if (_scale == null)
{
_scale = this.RenderTransform as ScaleTransform;
//RenderTransforms don't update the layout, so we need to trigger that ourselves:
_scale.Changed += (s, e) => { InvalidateArrange(); };
}
return _scale;
}
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
Statics.MessageIfDebug("Arranging");
var layout = base.ArrangeOverride(arrangeBounds);
var scale = this.Scale;
if (scale != null)
{
//Because RenderTransforms don't update the layout,
//we need to pretend we're bigger than we are to make room for our zoomed content:
_extent = new Size(layout.Width * scale.ScaleX, layout.Height * scale.ScaleY);
_viewport = layout;
//Coerce offsets..
var maxOffset = new Vector(ExtentWidth - ViewportWidth, ExtentHeight - ViewportHeight);
_offset.X = Math.Max(0, Math.Min(_offset.X, maxOffset.X));
_offset.Y = Math.Max(0, Math.Min(_offset.Y, maxOffset.Y));
//..and move the zoomed content within the ScrollViewer:
var renderOffsetX = (maxOffset.X > 0) ? (_offset.X / maxOffset.X) : 0.5;
var renderOffsetY = (maxOffset.Y > 0) ? (_offset.Y / maxOffset.Y) : 0.5;
this.RenderTransformOrigin = new Point(renderOffsetX, renderOffsetY);
if (ScrollOwner != null)
{
ScrollOwner.InvalidateScrollInfo();
}
}
return layout;
}
#region IScrollInfo
//This is the boilerplate IScrollInfo implementation,
//which can be found in *the first half* of this tutorial:
//http://tech.pro/tutorial/907/wpf-tutorial-implementing-iscrollinfo
//(down to and including SetHorizontalOffset()/SetVerticalOffset()).
//Note the bug reported by "Martin" in the comments.
...
答案 2 :(得分:-1)
我的第一个建议是使用已经支持ScrollViewer的商业第三方zoom control检查您可以使用哪种缩放功能,还有许多其他缩放和平移功能。
现在解决你的问题:
您可以使用LayoutTransform使代码工作,但您需要将ScrollViewer内容的大小设置为固定值。
目前,ScrollViewer中有一个Grid。 Grid没有定义其大小,因此它占用了所有空间。因此,如果您现在缩放网格,例如按因子2缩放,这意味着网格的内容按因子2缩放,但网格仍将占用它可以获得的所有空间。如果你将Grid的宽度指定为500,然后将其缩放为2,则会使Grid的宽度为1000.但是如果你说:Grid,你可以占用父级给你的所有空间,然后缩放Grid,它会是一样的。这意味着缩放ScrollViewer的自动调整大小的内容将不会显示滚动条。
在您的示例中,这是正确的,直到Grid的内容(第一列宽度= 150 +第二列中“右”文本的宽度)超过可用大小 - 此时网格的DesiredSize将大于ScrollViewer可以提供的大小,ScrollViewer将显示滚动条。
例如:
1)假设在启动应用程序时,scale设置为1,ScrollViewer为Grid提供500个水平点。网格显示第一列150宽度,并显示“右”文本,没有任何比例。第二列设置为填充剩余空间 - 因此:第二列使用500 - 150 = 350点。
2)现在用户将比例设置为2.网格将第一列缩放为300磅。这意味着第二列现在只能获得200个点。网格还缩放“右”文本,但内容(第一列300或文本宽度)仍然不超过ScrollViewer提供的500点。
3)用户现在将比例设置为3.现在网格内容的总宽度超过500点,这意味着ScrollViewer将显示滚动条。
因此,使用自动调整控件,ScrollViewer和缩放功能不能很好。
但是,如果您将Grid的大小固定为500,那么在缩放和使用ScrollViewer时,您将获得更可预测的结果。例如,如果你缩放10%,网格的大小将是550并且已经超过ScrollViewer的大小 - 所以ScrollViewer会显示滚动条。当你增加窗口的大小时,这也会给你预期的行为 - 网格的大小将保持不变,并且在某些时候滚动条会消失(当窗口大到足以显示窗口的整个内容时缩放网格。
总结:我的建议是将固定大小设置为ScrollViewer控件的内容。如果您有固定大小的窗口,则可以根据该大小设置宽度和高度。否则,您可以在首次加载控件时动态设置它:
您可以将内容控件的XAML更改为:
<ContentControl Name="ContentControl1"
ContentTemplate="{StaticResource ContentTemplate1}"
Margin="10"
Loaded="ContentControl1_OnLoaded" >
并添加ContentControl1_OnLoaded处理,只需将大小设置为初始大小:
private void ContentControl1_OnLoaded(object sender, RoutedEventArgs e)
{
ContentControl1.Width = ContentControl1.ActualWidth;
ContentControl1.Height = ContentControl1.ActualHeight;
}
缩放和平移似乎是一项非常简单的任务。但是我的经验表明(我是ZoomPanel控件的作者),这项任务很快就会变得非常复杂。