【问题标题】:From code: Change ScrollViewer's scrollbars'-style to touch来自代码:将 ScrollViewer 的滚动条样式更改为触摸
【发布时间】:2017-05-08 14:38:37
【问题描述】:

触摸:

鼠标:

如何告诉ScrollViewer 从代码开始使用触摸式滚动条?

这是一个例子:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ScrollViewer Name="scrollViewer1" HorizontalScrollBarVisibility="Visible" >
        <Image Stretch="UniformToFill">
            <Image.Source>
                <BitmapImage x:Name="bitmapImage1" UriSource="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-icon.png"></BitmapImage>
            </Image.Source>
        </Image>
    </ScrollViewer>
</Grid>

还有:

public sealed partial class MainPage : Page
{
    DispatcherTimer dispatcherTimer1 = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
    bool SE;

    public MainPage()
    {
        this.InitializeComponent();
        dispatcherTimer1.Tick += DispatcherTimer1_Tick;
        dispatcherTimer1.Start();
    }

    private void DispatcherTimer1_Tick(object sender, object e)
    {
        if (SE = !SE) bitmapImage1.UriSource = new Uri("https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/se/se-icon.png");
        else bitmapImage1.UriSource = new Uri("https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-icon.png");
        scrollViewer1.ChangeView(SE ? 1 : 0, SE ? 1 : 0, null);
    }
}

如果您运行此程序(至少在支持触控的 PC 上),滚动条最初将是触控的。如果然后用鼠标将光标移到它上面,它将变为鼠标。如果你再触摸它(在滚动条被隐藏之后)它会恢复触摸。

我想以编程方式告诉它从一个更改为另一个。怎么可能呢?如果唯一的方法是编辑模板 - 没有硬编码the template 怎么办?只是修复需要修复的细节。需要明确的是:我希望能够调用一种可以从一个更改为另一个的方法:void ChangeTo(bool mouse) { ... }。 (不过,如果做不到这一点,只是强制 ScrollViewer 始终处于一种模式,这在某种程度上是一种解决方法。)

【问题讨论】:

    标签: c# xaml uwp win-universal-app uwp-xaml


    【解决方案1】:

    在默认模板中定义了 3 个 VisualStates: 无指标, 触控指示器和 鼠标指示器

    滚动缩略图的样式根据当前设置的状态而有所不同。 要更改控件状态,您可以调用

    VisualStateManager.GoToState(scrollViewer1, "TouchIndicator");
    

    但当此状态可能发生变化时,您需要手动处理所有事件和操作。

    但是,如果您希望 TouchIndicator 始终可见,那么我认为更好的解决方案是实现 CustomVisualStateManager,例如:

    public class MyVisualStateManager : VisualStateManager
    {
        protected override bool GoToStateCore(Control control, FrameworkElement templateRoot, 
            System.String stateName, VisualStateGroup group, VisualState state, System.Boolean useTransitions)
        {
            switch (stateName)
            {
                case "NoIndicator":
                case "TouchIndicator":
                case "MouseIndicator":
                    base.GoToStateCore(control, templateRoot, "TouchIndicator", group, state, useTransitions);
                    break;
            }
            return true;
        }
    }
    

    然后您需要从 MSDN 复制模板,将其设置到您的 ScrollViewer 并将 MyVisualStateManager 放入其中:

    <Style TargetType="ScrollViewer" x:Key="ScrollStyle">
            <Setter Property="HorizontalScrollMode" Value="Auto" />
            <Setter Property="VerticalScrollMode" Value="Auto" />
            <Setter Property="IsHorizontalRailEnabled" Value="True" />
            <Setter Property="IsVerticalRailEnabled" Value="True" />
            <Setter Property="IsTabStop" Value="False" />
            <Setter Property="ZoomMode" Value="Disabled" />
            <Setter Property="HorizontalContentAlignment" Value="Left" />
            <Setter Property="VerticalContentAlignment" Value="Top" />
            <Setter Property="VerticalScrollBarVisibility" Value="Visible" />
            <Setter Property="Padding" Value="0" />
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="BorderBrush" Value="Transparent" />
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ScrollViewer">
                        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                            <VisualStateManager.CustomVisualStateManager>
                                <local:MyVisualStateManager/>
                            </VisualStateManager.CustomVisualStateManager>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="ScrollingIndicatorStates">
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition From="MouseIndicator" To="NoIndicator">
                                            <Storyboard>
                                    (... blabla ...)
    </Style>
    

    样式集:

    <ScrollViewer Name="scrollViewer1"  Style="{StaticResource ScrollStyle}" HorizontalScrollBarVisibility="Visible">
    

    现在,每当您的 ScrollViewer 状态需要更改时,您都会忽略它想要的确切状态,而是设置 TouchIndicator。

    【讨论】:

    • 谢谢!好像就是这样。虽然我不明白你所说的but you would need to manually take care of all events and actions when this state may change. 是什么意思,但既然我没有退订任何事件,为什么要改变任何事情(当然除了状态)?
    • 例如,每次鼠标指针进入和退出 ScrollViewer 时都会触发 ScrollViewer 的更改状态(即使你在我记得的范围内移动鼠标)。因此,如果您使用 VisualStateManager.GoToState(something) 设置它,如果有任何鼠标指针进入它,它将被更改并设置为 IndicatorMouse。似乎与系统争论应该设置哪个状态(我的我的我的!;)使用 CustomiVisualStateManager 更好是一种切割系统的方式,您可以决定何时以及应该设置哪个状态。跨度>
    • 我认为 @maxp 的解决方案是 2020 年的正确解决方案。我已经对其进行了测试并且运行良好
    【解决方案2】:

    Windows 有两种滚动可视化,它们基于用户的输入模式: 滚动指示器 使用触摸或游戏板时;以及用于其他输入设备(包括鼠标、键盘和笔)的交互式滚动条

    Guidelines for panning 中声明

    根据检测到的输入设备有两种平移显示模式:

    • 用于触摸的平移指示器。
    • 其他输入设备的滚动条,包括鼠标、触摸板、键盘和手写笔。

    注意 平移指示器仅在触摸接触位于可平移区域内时可见。同样,只有当鼠标光标、笔/触控笔光标或键盘焦点位于可滚动区域内时,滚动条才可见。

    平移指示器 平移指示器类似于滚动条中的滚动框。它们表示显示内容占总可平移区域的比例以及显示内容在可平移区域中的相对位置。

    注意 与标准滚动条不同,平移指示器只是提供信息。它们不会暴露于输入设备,也无法以任何方式进行操作。

    所以显示模式是基于用户的输入模式,我们不能以编程方式将其从一种更改为另一种。我们可以做的是编辑ScrollViewer's template,这样 ScrollViewer 将只使用一个可视化 UI。

    在默认样式中,我们可以发现ScrollViewer有三个VisualStates:NoIndicatorTouchIndicatorMouseIndicator,它们分别是用于控制显示模式。我们可以更改 TouchIndicatorMouseIndicator 视觉状态,使ScrollViewer 始终处于一种显示模式。

    例如,我们可以将“TouchIndicator”下的Storyboard VisualState 替换为“MouseIndicator”下的Storyboard VisualState 以使ScrollViewer 始终处于滚动条模式,例如:

    <ControlTemplate x:Key="MouseIndicatorTemplate" TargetType="ScrollViewer">
        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="ScrollingIndicatorStates">
                    <VisualStateGroup.Transitions>
                        <VisualTransition From="MouseIndicator" To="NoIndicator">
                            <Storyboard>
                                <FadeOutThemeAnimation BeginTime="0:0:3" TargetName="ScrollBarSeparator" />
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
                                    <DiscreteObjectKeyFrame KeyTime="0:0:3">
                                        <DiscreteObjectKeyFrame.Value>
                                            <ScrollingIndicatorMode>None</ScrollingIndicatorMode>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
                                    <DiscreteObjectKeyFrame KeyTime="0:0:3">
                                        <DiscreteObjectKeyFrame.Value>
                                            <ScrollingIndicatorMode>None</ScrollingIndicatorMode>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualTransition>
                        <VisualTransition From="TouchIndicator" To="NoIndicator">
                            <Storyboard>
                                <FadeOutThemeAnimation TargetName="ScrollBarSeparator" />
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
                                    <DiscreteObjectKeyFrame KeyTime="0:0:0.5">
                                        <DiscreteObjectKeyFrame.Value>
                                            <ScrollingIndicatorMode>None</ScrollingIndicatorMode>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
                                    <DiscreteObjectKeyFrame KeyTime="0:0:0.5">
                                        <DiscreteObjectKeyFrame.Value>
                                            <ScrollingIndicatorMode>None</ScrollingIndicatorMode>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualTransition>
                    </VisualStateGroup.Transitions>
                    <VisualState x:Name="NoIndicator">
                        <Storyboard>
                            <FadeOutThemeAnimation TargetName="ScrollBarSeparator" />
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="TouchIndicator">
                        <Storyboard>
                            <FadeInThemeAnimation TargetName="ScrollBarSeparator" />
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode" Duration="0">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode" Duration="0">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="MouseIndicator">
                        <Storyboard>
                            <FadeInThemeAnimation TargetName="ScrollBarSeparator" />
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode" Duration="0">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode" Duration="0">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <Grid Background="{TemplateBinding Background}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <ScrollContentPresenter x:Name="ScrollContentPresenter"
                                        Grid.RowSpan="2"
                                        Grid.ColumnSpan="2"
                                        Margin="{TemplateBinding Padding}"
                                        ContentTemplate="{TemplateBinding ContentTemplate}" />
                <Grid Grid.RowSpan="2" Grid.ColumnSpan="2" />
                <ScrollBar x:Name="VerticalScrollBar"
                           Grid.Column="1"
                           HorizontalAlignment="Right"
                           IsTabStop="False"
                           Maximum="{TemplateBinding ScrollableHeight}"
                           Orientation="Vertical"
                           ViewportSize="{TemplateBinding ViewportHeight}"
                           Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
                           Value="{TemplateBinding VerticalOffset}" />
                <ScrollBar x:Name="HorizontalScrollBar"
                           Grid.Row="1"
                           IsTabStop="False"
                           Maximum="{TemplateBinding ScrollableWidth}"
                           Orientation="Horizontal"
                           ViewportSize="{TemplateBinding ViewportWidth}"
                           Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
                           Value="{TemplateBinding HorizontalOffset}" />
                <Border x:Name="ScrollBarSeparator"
                        Grid.Row="1"
                        Grid.Column="1"
                        Background="{ThemeResource ScrollViewerScrollBarSeparatorBackground}" />
            </Grid>
        </Border>
    </ControlTemplate>
    

    反之亦然。

    <ControlTemplate x:Key="TouchIndicatorTemplate" TargetType="ScrollViewer">
        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="ScrollingIndicatorStates">
                    <VisualStateGroup.Transitions>
                        <VisualTransition From="MouseIndicator" To="NoIndicator">
                            <Storyboard>
                                <FadeOutThemeAnimation BeginTime="0:0:3" TargetName="ScrollBarSeparator" />
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
                                    <DiscreteObjectKeyFrame KeyTime="0:0:3">
                                        <DiscreteObjectKeyFrame.Value>
                                            <ScrollingIndicatorMode>None</ScrollingIndicatorMode>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
                                    <DiscreteObjectKeyFrame KeyTime="0:0:3">
                                        <DiscreteObjectKeyFrame.Value>
                                            <ScrollingIndicatorMode>None</ScrollingIndicatorMode>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualTransition>
                        <VisualTransition From="TouchIndicator" To="NoIndicator">
                            <Storyboard>
                                <FadeOutThemeAnimation TargetName="ScrollBarSeparator" />
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
                                    <DiscreteObjectKeyFrame KeyTime="0:0:0.5">
                                        <DiscreteObjectKeyFrame.Value>
                                            <ScrollingIndicatorMode>None</ScrollingIndicatorMode>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
                                    <DiscreteObjectKeyFrame KeyTime="0:0:0.5">
                                        <DiscreteObjectKeyFrame.Value>
                                            <ScrollingIndicatorMode>None</ScrollingIndicatorMode>
                                        </DiscreteObjectKeyFrame.Value>
                                    </DiscreteObjectKeyFrame>
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualTransition>
                    </VisualStateGroup.Transitions>
                    <VisualState x:Name="NoIndicator">
                        <Storyboard>
                            <FadeOutThemeAnimation TargetName="ScrollBarSeparator" />
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="TouchIndicator">
                        <Storyboard>
                            <FadeOutThemeAnimation TargetName="ScrollBarSeparator" />
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode" Duration="0">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <ScrollingIndicatorMode>TouchIndicator</ScrollingIndicatorMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode" Duration="0">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <ScrollingIndicatorMode>TouchIndicator</ScrollingIndicatorMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="MouseIndicator">
                        <Storyboard>
                            <FadeOutThemeAnimation TargetName="ScrollBarSeparator" />
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode" Duration="0">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <ScrollingIndicatorMode>TouchIndicator</ScrollingIndicatorMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode" Duration="0">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <ScrollingIndicatorMode>TouchIndicator</ScrollingIndicatorMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <Grid Background="{TemplateBinding Background}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <ScrollContentPresenter x:Name="ScrollContentPresenter"
                                        Grid.RowSpan="2"
                                        Grid.ColumnSpan="2"
                                        Margin="{TemplateBinding Padding}"
                                        ContentTemplate="{TemplateBinding ContentTemplate}" />
                <Grid Grid.RowSpan="2" Grid.ColumnSpan="2" />
                <ScrollBar x:Name="VerticalScrollBar"
                           Grid.Column="1"
                           HorizontalAlignment="Right"
                           IsTabStop="False"
                           Maximum="{TemplateBinding ScrollableHeight}"
                           Orientation="Vertical"
                           ViewportSize="{TemplateBinding ViewportHeight}"
                           Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
                           Value="{TemplateBinding VerticalOffset}" />
                <ScrollBar x:Name="HorizontalScrollBar"
                           Grid.Row="1"
                           IsTabStop="False"
                           Maximum="{TemplateBinding ScrollableWidth}"
                           Orientation="Horizontal"
                           ViewportSize="{TemplateBinding ViewportWidth}"
                           Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
                           Value="{TemplateBinding HorizontalOffset}" />
                <Border x:Name="ScrollBarSeparator"
                        Grid.Row="1"
                        Grid.Column="1"
                        Background="{ThemeResource ScrollViewerScrollBarSeparatorBackground}" />
            </Grid>
        </Border>
    </ControlTemplate>
    

    一旦我们有了这两个模板,我们就可以使用ScrollViewer.Template 属性将显示模式从一种更改为另一种,如下所示(“MouseIndicatorTemplate”和“TouchIndicatorTemplate”放置在Page.Resources):

    void ChangeTo(bool mouse)
    {
        if (mouse)
        {
            scrollViewer1.Template = (ControlTemplate)Resources["MouseIndicatorTemplate"];
        }
        else
        {
            scrollViewer1.Template = (ControlTemplate)Resources["TouchIndicatorTemplate"];
        }
    }
    

    【讨论】:

      【解决方案3】:

      我希望恢复人们可能期望从 WPF 应用程序中获得的简单滚动条行为(完全删除触摸指示器)。 @Jay Zuo 给出的示例不起作用,所以我创建了自己的示例。可以在以下存储库中查看模板:

      https://github.com/jsymon/UWP-Classic-ScrollBar-Template

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-25
        • 1970-01-01
        • 1970-01-01
        • 2013-08-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多