2014-10-01 129 views
1

我想放大scrollviewer內的一些內容。WPF縮放+滾動條?

我正在尋找的縮放行爲是RenderTransform + ScaleTransform。但這不適用於ScrollViewer。

使用LayoutTransform + ScaleTransform時,scrollviewer會受到影響(僅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

要使縮放的元素得到精確的RenderTransform外觀,我們可以堅持RenderTransform,而是通過實現我們自己的滾動邏輯告訴ScrollViewer如何行爲。這種方法是基於這個優秀的教程:

http://tech.pro/tutorial/907/wpf-tutorial-implementing-iscrollinfo

我們創建自己的自定義"ZoomableContentControl"它實現IScrollInfo並告訴ScrollViewer從那裏(ScrollViewer.CanContentScroll = TRUE)得到它的滾動的邏輯。魔術發生在ArrangeOverride(),我們在那裏玩ExtentWidth/ExtentHeightRenderTransformOrigin

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. 

    ... 

用法:

<TabItem Header="Content 1"> 
    <ScrollViewer CanContentScroll="True" 
        HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> 
      <v:ZoomableContentControl ContentTemplate="{StaticResource ContentTemplate1}" Margin="10" > 
      <FrameworkElement.RenderTransform> 
       <StaticResource ResourceKey="ScaleTransform" /> 
      </FrameworkElement.RenderTransform> 
     </v:ZoomableContentControl> 
    </ScrollViewer> 
</TabItem> 
<TabItem Header="Content 2"> 
    <ScrollViewer CanContentScroll="True" 
        HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> 
     <v:ZoomableContentControl ContentTemplate="{StaticResource ContentTemplate2}" Margin="10" > 
      <FrameworkElement.RenderTransform> 
       <StaticResource ResourceKey="ScaleTransform" /> 
      </FrameworkElement.RenderTransform> 
     </v:ZoomableContentControl> 
    </ScrollViewer> 
</TabItem> 
+0

使用示例代碼瞭解它。完全按照我的意願行事。並且在手動實現IScrollInfo方面也有很好的學習經驗。 – jayars 2014-10-06 16:03:17

1

如果我理解正確:

  • 您是否希望ZoomSlider滑塊放大?
  • 如果內容太大而無法在其選項卡中顯示,您希望滾動條出現?

如果是這樣,它是LayoutTransform你想要的。在所有元素被測量和佈置之前完成這個轉換,並且ScrollViewer將能夠確定是否需要滾動條。

在我的機器中,「內容1」選項卡按預期工作,如果你只是切換到LayoutTransform(請注意,您收到「權利」放大了很多會消失於屏幕,切換滾動條):

Content 1, un-zoomed Content 1, zoomed

「內容2」需要更多一點的工作。首先,該選項卡中沒有ScrollViewer,因此需要添加。其次,ContentTemplate2使用默認拉伸的ViewBox,所以縮放在放大非常接近之前不會產生效果。要禁用視框內置的‘縮放’,您可以居中ContentControl容器(使用HorizontalAlignment/VerticalAlignment),這迫使它佔用較小的空間儘可能:

<TabItem Header="Content 2"> 
    <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> 
     <ContentControl ContentTemplate="{StaticResource ContentTemplate2}" ... 
         HorizontalAlignment="Center" VerticalAlignment="Center" > 
      <FrameworkElement.LayoutTransform> 
       ... 

Content 2, un-zoomed Content 2, zoomed

+0

感謝您的回覆。糟糕,錯過了第二個滾動查看器。正如您在ContentTemplate1的屏幕截圖中注意到的那樣,在LayoutTransform比例之後,右側窗格比左側窗格更小。相反,我希望它完全像RenderTransform一樣縮放,但不會丟失scrollviewer。 ContentTemplate2是一個視圖框的原因是,即使在縮放開始之前,它仍然可以適應。再一次,我希望它能夠像RenderTransform + ScaleTransform一樣縮放/行爲,但仍然可以與ScrollViewer一起使用。 – jayars 2014-10-01 12:27:42

+0

好的。我可能找到了一些東西。我會爲它做一個新的答案。 – Sphinxxx 2014-10-01 15:37:15

0

我的第一個建議是要檢查什麼樣的縮放功能,你可以使用一個第三方商業zoom control已經擁有的ScrollViewer支持,同時也有許多額外的縮放和平移特徵。

現在你的問題的解決方案:

您可以使用LayoutTransform你的代碼的工作,但你需要將ScrollViewer中的內容的大小設置爲一個固定值。

目前,您在ScrollViewer中有一個Grid。網格沒有定義它的大小,所以它佔用了它可以獲得的所有空間。因此,如果您現在通過例如因子2對網格進行縮放,這意味着網格的內容按比例因子2縮放,但網格仍將佔用可獲得的所有空間。如果將網格的寬度指定爲500,則將其縮小2,將使網格的寬度爲1000.但是,如果您說:網格,則可以獲取父節點給出的所有空間,然後縮放網格,將仍然是一樣的。這意味着縮放ScrollViewer的自動調整內容將不會顯示滾動條。

在您的示例中,這是真實的,直到網格的內容(寬度= 150的第一列+第二列中的「右」文本的寬度)超過可用大小 - 此時網格的DesiredSize將大於ScrollViewer可以提供的大小,而ScrollViewer將顯示滾動條。

例如:

1)讓我們說,當你啓動應用程序,比例設置爲1的ScrollViewer水平提供了500分的網格。網格顯示寬度爲150點的第一列,並顯示沒有任何比例的「正確」文本。第二列設置爲填充剩餘空間 - 因此:第二列使用500-150 = 350點。

2)現在用戶將比例設置爲2.網格將第一列標定爲300點。這意味着第二列現在只需要200分。網格還縮放「正確」文本,但內容(第一列的300個字符+文本寬度)仍不超過由ScrollViewer提供的500個點。

3)用戶現在將比例設置爲3.現在網格內容的總寬度超過了500分,這意味着ScrollViewer將顯示滾動條。

因此擁有自動化控件,ScrollViewer和縮放效果不佳。

但是,如果您要將網格的大小修改爲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控件的作者),這個任務很快就會變得非常複雜。