ScrollViewer 不只是捲動容器,更內建觸控優化的五大特性:Chaining(鏈式捲動)、Rail(軌道鎖定)、Inertia(慣性滾動)、Snap(吸附對齊)、Zoom(縮放)。這些特性讓觸控體驗媲美原生系統 App。本文將逐一解析並提供互動範例。
當巢狀 ScrollViewer 內容捲動超出邊界,若啟用 Chaining,多餘的捲動位移會自動傳遞給父層 ScrollViewer。
IsHorizontalScrollChainingEnabled(預設 true)IsVerticalScrollChainingEnabled(預設 true)<ScrollViewer> <!-- 父層 -->
<StackPanel Margin="120 0 0 0">
<TextBlock Text="我是參照物" FontSize="14.667"/>
<ScrollViewer x:Name="scrollViewer" Width="400" Height="400"
HorizontalAlignment="Left"
HorizontalScrollMode="Enabled" VerticalScrollMode="Enabled"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
IsHorizontalScrollChainingEnabled="{Binding IsChecked, ElementName=chkH}"
IsVerticalScrollChainingEnabled="{Binding IsChecked, ElementName=chkV}">
<Image Source="/Assets/Logo.png" Width="1000"/>
</ScrollViewer>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="chkH" Content="IsHorizontalScrollChainingEnabled" IsChecked="True" Margin="10 0 0 0"/>
<CheckBox x:Name="chkV" Content="IsVerticalScrollChainingEnabled" IsChecked="True" Margin="10 0 0 0"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
💡 測試方法:觸控模式下拖曳內層 ScrollViewer 至邊界並繼續拖曳,觀察外層「我是參照物」是否跟著捲動。
觸控捲動時,若啟用 Rail,手指初始移動方向決定鎖定軸向(水平或垂直),之後僅能沿該軸捲動,避免誤觸斜向捲動。
IsHorizontalRailEnabled(預設 true)IsVerticalRailEnabled(預設 true)<ScrollViewer x:Name="scrollViewer2" Width="400" Height="400"
HorizontalScrollMode="Enabled" VerticalScrollMode="Enabled"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"
IsHorizontalRailEnabled="{Binding IsChecked, ElementName=chkHRail}"
IsVerticalRailEnabled="{Binding IsChecked, ElementName=chkVRail}">
<Image Source="/Assets/Logo.png" Width="1000"/>
</ScrollViewer>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="chkHRail" Content="IsHorizontalRailEnabled" IsChecked="True"/>
<CheckBox x:Name="chkVRail" Content="IsVerticalRailEnabled" IsChecked="True" Margin="10 0 0 0"/>
</StackPanel>
觸控快速滑動鬆手後,內容以減速動畫繼續捲動,模擬物理慣性。
IsScrollInertiaEnabled(預設 true)<ScrollViewer x:Name="scrollViewer2" Width="400" Height="400"
HorizontalScrollMode="Enabled" VerticalScrollMode="Enabled"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"
IsScrollInertiaEnabled="{Binding IsChecked, ElementName=chkInertia}">
<Image Source="/Assets/Logo.png" Width="1000"/>
</ScrollViewer>
<CheckBox x:Name="chkInertia" Content="IsScrollInertiaEnabled" IsChecked="True"/>
捲動結束時自動對齊到最近的子元素,類似相簿翻頁、輪播圖效果。
| 屬性 | 說明 |
|---|---|
HorizontalSnapPointsType / VerticalSnapPointsType | 對齊行為:None(預設無)、Optional(接近才對齊)、Mandatory(強制對齊)、OptionalSingle/MandatorySingle(僅供 Zoom) |
HorizontalSnapPointsAlignment / VerticalSnapPointsAlignment | 對齊基準:Near(左/上對齊,預設)、Center(置中)、Far(右/下對齊) |
BringIntoViewOnFocusChange | 子元素獲取焦點時自動捲動到可見區(預設 true) |
<ComboBox x:Name="comboBox" SelectedIndex="0" SelectionChanged="ComboBox_SelectionChanged"
HorizontalAlignment="Left">
<ComboBoxItem>HorizontalSnapPointsType = SnapPointsType.None</ComboBoxItem>
<ComboBoxItem>HorizontalSnapPointsType = SnapPointsType.Optional</ComboBoxItem>
<ComboBoxItem>HorizontalSnapPointsType = SnapPointsType.Mandatory</ComboBoxItem>
</ComboBox>
<ScrollViewer x:Name="scrollViewer" Width="400" Height="200"
HorizontalAlignment="Left" Margin="0 10 0 0"
HorizontalScrollMode="Enabled" VerticalScrollMode="Enabled"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
<StackPanel Orientation="Horizontal">
<Image Width="400" Height="200" Stretch="Fill" Source="/Assets/Logo.png"/>
<Image Width="400" Height="200" Stretch="Fill" Source="/Assets/NineGrid/Demo.png"/>
<Image Width="350" Height="200" Stretch="Fill" Source="/Assets/Logo.png"/>
<Image Width="450" Height="200" Stretch="Fill" Source="/Assets/NineGrid/Demo.png"/>
<Image Width="400" Height="200" Stretch="Fill" Source="/Assets/Logo.png"/>
<TextBox x:Name="txtMsg" Width="400" Height="200" FontSize="26.67" Text="message"/>
<TextBox x:Name="txtMsg2" Width="400" Height="200" FontSize="26.67" Text="message2"/>
<TextBox x:Name="txtMsg3" Width="400" Height="200" FontSize="26.67" Text="message3"/>
</StackPanel>
</ScrollViewer>
<Button Content="捲動到 txtMsg2" Click="Button_Click" Margin="0 10 0 0"/>
public sealed partial class Snap : Page
{
public Snap()
{
this.InitializeComponent();
this.Loaded += Snap_Loaded;
}
void Snap_Loaded(object sender, RoutedEventArgs e)
{
scrollViewer.HorizontalSnapPointsAlignment = SnapPointsAlignment.Near;
scrollViewer.BringIntoViewOnFocusChange = true;
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch (comboBox.SelectedIndex)
{
case 0: scrollViewer.HorizontalSnapPointsType = SnapPointsType.None; break;
case 1: scrollViewer.HorizontalSnapPointsType = SnapPointsType.Optional; break;
case 2: scrollViewer.HorizontalSnapPointsType = SnapPointsType.Mandatory; break;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// 利用 BringIntoViewOnFocusChange 自動捲動
txtMsg2.Focus(FocusState.Programmatic);
}
}
啟用 ZoomMode="Enabled" 後,支援雙指縮放、雙擊縮放,並可定義吸附縮放比例。
ZoomMode:Disabled/Enabled(預設 Enabled)MinZoomFactor / MaxZoomFactor:縮放範圍(預設 0.1 ~ 10)ZoomSnapPoints:吸附比例集合(如 0.5, 1.0, 1.5, 2.0)ZoomSnapPointsType:None/Optional/Mandatory/OptionalSingle/MandatorySingleIsZoomChainingEnabled、IsZoomInertiaEnabled:縮放鏈式/慣性ZoomFactor:目前縮放倍數ZoomToFactor(factor):程式碼縮放到指定倍數<ScrollViewer x:Name="scrollViewer" Width="400" Height="400"
HorizontalAlignment="Left"
ZoomMode="Enabled" MaxZoomFactor="2" MinZoomFactor="0.5">
<Image Source="/Assets/Logo.png" Width="400" Height="400"/>
</ScrollViewer>
<Button Content="縮放到 0.1 倍" Click="Button_Click"/>
void Zoom_Loaded(object sender, RoutedEventArgs e)
{
scrollViewer.ZoomSnapPointsType = SnapPointsType.OptionalSingle;
scrollViewer.ZoomSnapPoints.Add(0.5f);
scrollViewer.ZoomSnapPoints.Add(0.8f);
scrollViewer.ZoomSnapPoints.Add(1.0f);
scrollViewer.ZoomSnapPoints.Add(1.5f);
scrollViewer.ZoomSnapPoints.Add(2.0f);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
scrollViewer.ZoomToFactor(0.1f);
}
ScrollViewer 五大觸控優化特性:
SnapPointsType 控制強度,SnapPointsAlignment 控制基準ZoomSnapPoints 定義吸附比例,ZoomToFactor 程式碼控制根據 App 情境(電子書、相簿、地圖、儀表板)靈活組合啟用/停用,即可打造頂級觸控體驗。