Slider \ ScrollViewer в сенсорном интерфейсе не работает должным образом - PullRequest
7 голосов
/ 03 декабря 2011

В WPF у меня есть следующий XAML:

<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>

Это ScrollViewer, содержащий Slider.Я использую следующее на сенсорном экране, и я использую панорамирование даже для прокрутки ScrollViewer по вертикали.Когда PanningMode = "VerticalOnly" установлен, ползунок перестает работать!

Я предполагаю, что ScollViewer использует событие touch \ slide и обрабатывает его раньше, чем ползунок (но я думаю, что я не правспереди).

Есть ли обходной путь для этого?

Ответы [ 4 ]

11 голосов
/ 20 января 2012

Я только что решил эту проблему в нашем приложении.

Что происходит, так это то, что ScrollViewer захватывает TouchDevice в своем обработчике PreviewTouchMove, который «крадет» TouchDevice из других элементов управления и не позволяет им получать PreviewTouchMove илиСобытия TouchMove.

Чтобы обойти эту проблему, необходимо реализовать пользовательский элемент управления Thumb, который захватывает TouchDevice в событии PreviewTouchDown и сохраняет ссылку на него до тех пор, пока не произойдет событие PreviewTouchUp.Затем элемент управления может «украсть» перехват обратно в свой обработчик LostTouchCapture, когда это необходимо.Вот некоторый краткий код:

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;
        }
    }
}

Затем вам нужно будет перенастроить слайдер, чтобы использовать CustomThumb вместо стандартного элемента управления Thumb.

2 голосов
/ 07 августа 2012

я боролся с подобной проблемой. Обходной путь был таким (ни один из остальных не работал для меня): я создал собственный большой палец, а затем использовал его в стиле полосы прокрутки в xaml как большой палец 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 голосов
/ 17 февраля 2015

Это приятно, просто и сработало для меня - хотя стоит обернуть его в универсальную функцию и расширить для обработки минимального значения ползунка, так как оно может не быть нулевым. Какую боль делать, хотя. В WPF есть много замечательных вещей, но многие простые вещи требуют дополнительных шагов, которые действительно могут нанести ущерб производительности.

0 голосов
/ 04 декабря 2014

Следующее сработало для меня. Я долго искал что-то, что сработало бы. Я адаптировал это для касания из Как заставить WPF Slider Thumb следовать за курсором из любой точки . Это гораздо более простое исправление, позволяющее избежать создания настраиваемого ползунка / элемента управления большим пальцем.

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

Для IsMoveToPointEnable должно быть установлено значение true, чтобы это работало.

 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;
 }
...