2011-12-03 4 views
6

In WPF ho ottenuto il seguente XAML:Slider ScrollViewer in un'interfaccia touch non funziona correttamente

<ScrollViewer Canvas.Left="2266" Canvas.Top="428" Height="378" Name="scrollViewer1" Width="728" PanningMode="VerticalOnly" PanningRatio="2"> 
    <Canvas Height="1732.593" Width="507.667"> 
     <Slider Height="40.668" x:Name="slider1" Width="507.667" Style="{DynamicResource SliderStyle1}" Canvas.Left="15" Canvas.Top="150" /> 
     </Slider> 
    </Canvas> 
</ScrollViewer> 

E 'uno ScrollViewer contenente un cursore. Sto usando quanto segue su un touch-screen, e sto usando il panning anche per far scorrere ScrollViewer verticalmente. Quando PanningMode = "VerticalOnly" è impostato, il cursore smette di funzionare!

Suppongo che ScollViewer stia consumando l'evento touch \ slide e lo gestisca prima del cursore (ma penso di essermi sbagliato su questo fronte).

C'è qualche soluzione per questo?

risposta

10

Ho appena risolto questo problema nella nostra app.

Quello che sta succedendo è che ScrollViewer cattura il TouchDevice nel suo gestore di PreviewTouchMove, che "ruba" il TouchDevice dagli altri controlli e impedisce loro di ricevere eventi PreviewTouchMove o TouchMove.

Per ovviare a questo problema, è necessario implementare un controllo Thumb personalizzato che acquisisca il TouchDevice nell'evento PreviewTouchDown e memorizzi un riferimento ad esso fino a quando si verifica l'evento PreviewTouchUp. Quindi il controllo può "rubare" la cattura indietro nel suo gestore di LostTouchCapture, quando appropriato. Ecco alcune brevi code:

public class CustomThumb : Thumb 
{ 
    private TouchDevice currentDevice = null; 

    protected override void OnPreviewTouchDown(TouchEventArgs e) 
    { 
     // Release any previous capture 
     ReleaseCurrentDevice(); 
     // Capture the new touch 
     CaptureCurrentDevice(e); 
    } 

    protected override void OnPreviewTouchUp(TouchEventArgs e) 
    { 
     ReleaseCurrentDevice(); 
    } 

    protected override void OnLostTouchCapture(TouchEventArgs e) 
    { 
     // Only re-capture if the reference is not null 
     // This way we avoid re-capturing after calling ReleaseCurrentDevice() 
     if (currentDevice != null) 
     { 
      CaptureCurrentDevice(e); 
     } 
    } 

    private void ReleaseCurrentDevice() 
    { 
     if (currentDevice != null) 
     { 
      // Set the reference to null so that we don't re-capture in the OnLostTouchCapture() method 
      var temp = currentDevice; 
      currentDevice = null; 
      ReleaseTouchCapture(temp); 
     } 
    } 

    private void CaptureCurrentDevice(TouchEventArgs e) 
    { 
     bool gotTouch = CaptureTouch(e.TouchDevice); 
     if (gotTouch) 
     { 
      currentDevice = e.TouchDevice; 
     } 
    } 
} 

allora si avrà bisogno di ri-modello il cursore di utilizzare il CustomThumb al posto del controllo di default del pollice.

+2

Non ho ancora testato questo approccio, ma sembra abbastanza valido. Lo sceglierò come risposta. Il modo in cui ho risolto tutti i miei problemi era usando l'SDK Microsoft Surface 2.0 http://www.microsoft.com/download/en/details.aspx?id=26716 e utilizzato il ScrollViewer dalla loro libreria (che gestisce tutti i problemi sopra). – Bassem

+1

Questa soluzione ha funzionato per me. È stato molto comodo perché avevo già ri-templato lo Slider per usare un Thumb personalizzato con una forma/dimensione più adatta al tocco. – tgr42

+0

L'approccio fornito funziona come un fascino! Ho affrontato il problema durante la rimozione dell'SDK Microsoft Surface 2.0 (per me deprecato). – SSchuette

2

mi sono strugiato con un problema simile. la soluzione era questa (nessuno degli altri ha funzionato per me): ho creato un pollice personalizzato, e poi l'ho usato all'interno di uno stile barra di scorrimento in xaml come il pollice di PART_Track.

public class DragableThumb : Thumb 
{ 
    double m_originalOffset; 
    double m_originalDistance; 
    int m_touchID; 

    /// <summary> 
    /// Get the parent scrollviewer, if any 
    /// </summary> 
    /// <returns>Scroll viewer or null</returns> 
    ScrollViewer GetScrollViewer() 
    { 
     if (TemplatedParent is ScrollBar && ((ScrollBar)TemplatedParent).TemplatedParent is ScrollViewer) 
     { 
      return ((ScrollViewer)((ScrollBar)TemplatedParent).TemplatedParent); 
     } 

     return null; 
    } 

    /// <summary> 
    /// Begin thumb drag 
    /// </summary> 
    /// <param name="e">Event arguments</param> 
    protected override void OnTouchDown(TouchEventArgs e) 
    { 
     ScrollViewer scrollViewer; 

     base.OnTouchDown(e); 

     m_touchID = e.TouchDevice.Id; 

     if ((scrollViewer = GetScrollViewer()) != null) 
     { 
      m_originalOffset = scrollViewer.HorizontalOffset; 
      m_originalDistance = e.GetTouchPoint(scrollViewer).Position.X; 
     } 
    } 

    /// <summary> 
    /// Handle thumb delta 
    /// </summary> 
    /// <param name="e">Event arguments</param> 
    protected override void OnTouchMove(TouchEventArgs e) 
    { 
     ScrollViewer scrollViewer; 
     double actualDistance; 

     base.OnTouchMove(e); 

     if ((scrollViewer = GetScrollViewer()) != null && m_touchID == e.TouchDevice.Id) 
     { 
      actualDistance = e.GetTouchPoint(scrollViewer).Position.X; 
      scrollViewer.ScrollToHorizontalOffset(m_originalOffset + (actualDistance - m_originalDistance) * scrollViewer.ExtentWidth/scrollViewer.ActualWidth); 
     } 
    } 
} 
+0

Grazie, questo mi ha impostato sulla strada giusta per il mio problema. –

0

Quanto segue ha funzionato per me. Ho cercato a lungo per qualcosa che avrebbe funzionato. Ho adattato questo per il tocco da How to make WPF Slider Thumb follow cursor from any point. Questa è una soluzione molto più semplice e consente di evitare la creazione di un controllo cursore/pollice personalizzato.

<Slider TouchMove="OnTouchMove" IsMoveToPointEnabled="True"/> 

IsMoveToPointEnable deve essere impostato su true affinché funzioni.

private void Slider_OnTouchMove(object sender, TouchEventArgs e) 
{ 
    Slider slider = (Slider)sender;   
    TouchPoint point = e.GetTouchPoint (slider); 
    double d = 1.0/slider.ActualWidth * point.Position.X; 
    int p = int(slider.Maximum * d); 
    slider.Value = p; 
} 
0

Questo è bello e semplice e ha funzionato per me - anche se vale la pena avvolgendolo in una funzione generica e che si estende per gestire il valore minimo di scorrimento anche in quanto non può essere zero. Che dolore dover fare però. Ci sono molte cose su WPF che sono interessanti, ma così tante cose semplici richiedono passaggi extra che possono davvero essere dannosi per la produttività.