2016-07-19 39 views
2

對於Windows 10 UWP應用程序,我有這樣的XAML結構:XAML滑塊內部的ScrollViewer

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

我想創建這樣的用戶體驗:

  1. 當用戶開始橫向輕掃手勢時,觸摸滑塊下應接收輸入,並開始改變其值,而垂直滾動完全禁用(即使當用戶繼續繪製圓運動)

  2. 當用戶開始垂直滑動手勢時,滾動查看器應接收輸入並開始垂直滾動,而觸摸下的滑塊應保持完整(即使用戶繼續繪製圓圈運動)。

是否可以在純XAML中實現此行爲?我想我已經嘗試了與滾動相關的所有可能的屬性組合...沒有運氣。任何人都有想法?

+0

有趣,但爲什麼用戶做圓形運動平局時唯一可用的相互作用X滑閥(RS)和Y滾動? –

+0

用戶沒有理由做出圓形運動,重要的是一旦初始交互已經開始(垂直滾動或滑塊改變),不應該切換到不同的行爲,而不管用戶用手指畫什麼。 – lenin

+0

我會在滑塊中觀看Thumb的焦點以禁用滾動,但我沒有立即回答您,也沒有做一些修補。對不起,我希望有人能在UWP比賽中獲得比我更多的機會。 +1方式。 –

回答

0

與操作系統版本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失去焦點,然後可以啓用垂直滾動模式。

當用戶開始垂直滑動手勢時,滾動查看器應接收輸入並開始垂直滾動,而觸摸下的滑塊應保持不動(即使用戶繼續繪製圓圈運動時)。

基於我的測試,我認爲這個手勢是默認保證的,所以我沒有爲此編碼。如果不在您身邊,請留下評論並提供您的設備類型和操作系統版本,以便我可以進行測試。

+0

謝謝Grace,我已經在Lumia1520 10.0.10586.494上試過了你的代碼,但是它沒有解決問題:如果我從滑塊開始垂直滾動,我沒有運氣 - 滑塊抓住我的所有手勢,並且沒有發生垂直滾動。在你的示例代碼中,你有''這樣可以在兩個滑塊周圍留出很多空間來與scrollviewer直接交互。在實際應用中根本沒有這樣的空白空間。所有區域都充滿滑塊,這就是爲什麼需要scrollviewer。目標是以某種方式猜測初始滑動方向並相應採取行動。 – lenin

+0

我已經有一個相當醜陋的實現:它'預覽'滑動的初始20px,然後決定它是水平還是垂直。如果是垂直的 - 連續恢復初始滑塊值並用scrollviewer手動垂直滾動。如果水平 - 當滑塊完全控制時不做任何事情。如果存在一些純粹的XAML解決方案,那將是非常好的... – lenin

+0

@lenin,在我的代碼中,我確保只有在滑塊失去焦點時,scrollviewer才能垂直滾動。因此,如果scrollviewer中沒有空白區域使滑塊失去焦點,我不知道滾動查看器如何獲取操作手勢,除非您禁用某些滑塊,否則所有手勢都將由滑塊處理。有趣的是,我不知道。 –

0

我有一個非常類似的問題,並能夠解決它與下面的自定義控件。這是一個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); 
     }   
    } 
}