ScrollViewer中的XAML滑块



对于Windows 10 UWP应用程序,我有这样的XAML结构:

<ScrollViewer>
    <StackPanel>
        <Slider />
        <Slider />
        ...
    </StackPanel>
</ScrollViewer>

我想创造这样的用户体验:

  1. 当用户开始水平滑动手势时,触摸下的滑块应接收输入并开始更改其值,而垂直滚动则完全禁用(即使用户继续画圆圈运动)

  2. 当用户开始垂直滑动手势时,滚动查看器应该接收输入并开始垂直滚动,而触摸下的滑块应该保持不变(即使用户继续画圆圈运动)。

是否可以在纯XAML中实现此行为?我想我已经尝试了与滚动相关的所有可能的属性组合。。。运气不好。有人知道吗?

在操作系统版本为10586的移动模拟器上测试后,我发现当ScrollViewer垂直滚动时,即使我画圆圈也不会影响里面的Slider;当Slider水平滑动时,只有当其值达到最大值时,如果我画圆圈,就会影响ScrollViewer的垂直滚动。

是否可以在纯XAML中实现此行为?

是的,这是可能的。

当用户开始水平滑动手势时,触摸下的滑块应接收输入并开始更改其值,而垂直滚动则完全禁用(即使用户继续画圆圈运动)

您可以在项目中安装Microsoft.Xaml.Behaviors.Uwp.Managed包,然后使用其DataTriggerBehavior,例如:

<ScrollViewer x:Name="scrollViewer" HorizontalScrollMode="Disabled" VerticalScrollMode="Auto">
    <Interactivity:Interaction.Behaviors>
        <Core:DataTriggerBehavior Binding="{Binding ElementName=slider1,Path=FocusState}" ComparisonCondition="NotEqual" Value="Unfocused">
            <Core:ChangePropertyAction PropertyName="VerticalScrollMode" Value="Disabled" />
        </Core:DataTriggerBehavior>
        <Core:DataTriggerBehavior Binding="{Binding ElementName=slider1,Path=FocusState}" ComparisonCondition="Equal"  Value="Unfocused">
            <Core:ChangePropertyAction PropertyName="VerticalScrollMode" Value="Auto" />
        </Core:DataTriggerBehavior>
        <Core:DataTriggerBehavior Binding="{Binding ElementName=slider2,Path=FocusState}" ComparisonCondition="NotEqual" Value="Unfocused">
            <Core:ChangePropertyAction PropertyName="VerticalScrollMode" Value="Disabled" />
        </Core:DataTriggerBehavior>
        <Core:DataTriggerBehavior Binding="{Binding ElementName=slider2,Path=FocusState}" ComparisonCondition="Equal"  Value="Unfocused">
            <Core:ChangePropertyAction PropertyName="VerticalScrollMode" Value="Auto" />
        </Core:DataTriggerBehavior>
    </Interactivity:Interaction.Behaviors>
    <StackPanel Height="1300">
        <Slider Margin="0,200" x:Name="slider1" />
        <Slider x:Name="slider2" />
    </StackPanel>
</ScrollViewer>

在xaml中使用此包时,您需要在标头中声明它,例如:

xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
xmlns:Media="using:Microsoft.Xaml.Interactions.Media"

正如您在我的代码中看到的,我比较了SliderFocusState属性,如果它的值是Unfocused,那么ScrollViewer的垂直滚动模式就被启用了。因此,当用户与该布局交互时,在Slider上滑动后,他需要先点击空白部分,使Slider失去焦点,然后才能启用垂直滚动模式。

当用户开始垂直滑动手势时,滚动查看器应该接收输入并开始垂直滚动,而触摸下的滑块应该保持不变(即使用户继续画圆圈运动)。

根据我的测试,我认为这个手势是默认的,所以我没有为此编写代码。如果它不在你身边,请留下评论,并提供你的设备类型和操作系统版本,这样我就可以进行测试了。

我遇到了一个非常类似的问题,并通过以下自定义控件解决了这个问题。这是一个CommandSlider,它还允许您在滑动完成后像按钮一样发送命令(不是您需要的),但ScrollViewer操作代码也在其中。看看这是否有助于你的处境。它允许您按照要求在完整的XAML中完成所有工作。

注意:滑块必须具有ManipulationMode="TranslateX" or "TranslateY",具体取决于方向,这样才能正常工作。

public sealed class CommandSlider : Slider
{
    public CommandSlider()
    {
        IsThumbToolTipEnabled = false;
        PointerCaptureLost += (s, e) => (Command?.CanExecute(CommandParameter)).GetValueOrDefault().Switch(() => Command?.Execute(CommandParameter));
        Loaded += (s, e) => ParentScrollViewer = this.GetParent<ScrollViewer>();
    }
    private ScrollViewer ParentScrollViewer { get; set; }
    protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e)
    {
        base.OnManipulationDelta(e);
        if (ParentScrollViewer != null)
        {                
            var scrollX = Orientation == Orientation.Vertical
                        ? e.Position.X * -1 + ParentScrollViewer.HorizontalOffset
                        : ParentScrollViewer.HorizontalOffset;
            var scrollY = Orientation == Orientation.Horizontal
                        ? e.Position.Y * -1 + ParentScrollViewer.VerticalOffset
                        : ParentScrollViewer.VerticalOffset;
            var zoom = ParentScrollViewer.ZoomFactor;
            ParentScrollViewer.ChangeView(scrollX, scrollY, zoom);
        }
    }
    public object CommandParameter
    {
        get => GetValue(CommandParameterProperty);
        set => SetValue(CommandParameterProperty, value);
    }
    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register(nameof(CommandParameter), typeof(object), typeof(CommandSlider), new PropertyMetadata(null));
    public ICommand Command
    {
        get => (ICommand)GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register(nameof(Command), typeof(ICommand), typeof(CommandSlider), new PropertyMetadata(null));
    public double SourceValue
    {
        get => (double)GetValue(SourceValueProperty);
        set => SetValue(SourceValueProperty, value);
    }
    public static readonly DependencyProperty SourceValueProperty =
        DependencyProperty.Register(nameof(SourceValue), typeof(double), typeof(CommandSlider), new PropertyMetadata(0d,
            (s, e) => (s as CommandSlider).Value = (double)e.NewValue));
}

自定义扩展方法

public static class XAMLExtensions
{
    public static T GetParent<T>(this DependencyObject dependencyObject) where T : DependencyObject
    {
        var parentDependencyObject = VisualTreeHelper.GetParent(dependencyObject);
        switch (parentDependencyObject)
        {
            case null:
                return null;
            case T parent:
                return parent;
            default:
                return GetParent<T>(parentDependencyObject);
        }        
    }
}

相关内容

  • 没有找到相关文章

最新更新