61

私は現在、WPF で MP3 プレーヤーを作成しています。スライダーを左右にスライドさせることで、ユーザーが MP3 の特定の位置をシークできるようにするスライダーを作成したいと考えています。

イベントを使用してみValueChangedましたが、値が変更されるたびにトリガーされるため、ドラッグするとイベントが複数回発生します。ユーザーがスライダーのドラッグを終了したときにのみイベントを発生させ、新しい値

どうすればこれを達成できますか?


[アップデート]

基本的に同じことを議論しているMSDNのこの投稿を見つけました。彼らは2つの「解決策」を考え出しました。Slider をサブクラス化するか、タイムスパンの後にアクションを呼び出すイベントでaDispatcherTimerを呼び出します。ValueChanged

上記の2つよりも優れたものを思いつきますか?

4

11 に答える 11

80

Besides using the Thumb.DragCompleted event you can also use both ValueChanged and Thumb.DragStarted, this way you don’t lose functionality when the user modifies the value by pressing the arrow keys or by clicking on the slider bar.

Xaml:

<Slider ValueChanged="Slider_ValueChanged"
    Thumb.DragStarted="Slider_DragStarted"
    Thumb.DragCompleted="Slider_DragCompleted"/>

Code behind:

private bool dragStarted = false;

private void Slider_DragCompleted(object sender, DragCompletedEventArgs e)
{
    DoWork(((Slider)sender).Value);
    this.dragStarted = false;
}

private void Slider_DragStarted(object sender, DragStartedEventArgs e)
{
    this.dragStarted = true;
}

private void Slider_ValueChanged(
    object sender,
    RoutedPropertyChangedEventArgs<double> e)
{
    if (!dragStarted)
        DoWork(e.NewValue);
}
于 2009-11-03T07:51:35.957 に答える
59

これには、サムの「DragCompleted」イベントを使用できます。残念ながら、これはドラッグ時にのみ発生するため、他のクリックとキーの押下を個別に処理する必要があります。ドラッグ可能にしたい場合は、LargeChange を 0 に設定し、Focusable を false に設定して、スライダーを移動するこれらの手段を無効にすることができます。

例:

<Slider Thumb.DragCompleted="MySlider_DragCompleted" />
于 2009-04-06T22:46:38.393 に答える
20
<Slider PreviewMouseUp="MySlider_DragCompleted" />

私のために働きます。

必要な値は、側面のクリック時またはハンドルのドラッグ後のいずれかのマウスアップ イベントの後の値です。

MouseUp はトンネル ダウンしないため (処理される前に処理されます)、PreviewMouseUp を使用する必要があります。

于 2009-06-30T17:07:29.173 に答える
1

これは、この問題に加えてキーボードで同じことを処理する動作です。https://gist.github.com/4326429

Command プロパティと Value プロパティを公開します。値は、コマンドのパラメーターとして渡されます。value プロパティにデータバインドできます (そしてビューモデルで使用できます)。コード ビハインド アプローチのイベント ハンドラーを追加できます。

<Slider>
  <i:Interaction.Behaviors>
    <b:SliderValueChangedBehavior Command="{Binding ValueChangedCommand}"
                                  Value="{Binding MyValue}" />
  </i:Interaction.Behaviors>
</Slider>
于 2012-12-18T09:11:12.300 に答える
0

私の解決策は基本的にSantoの解決策であり、さらにいくつかのフラグがあります。私の場合、スライダーは、ストリームの読み取りまたはユーザー操作(マウスのドラッグまたは矢印キーの使用など)のいずれかから更新されています。

最初に、ストリームの読み取りからスライダー値を更新するコードを記述しました。

    delegate void UpdateSliderPositionDelegate();
    void UpdateSliderPosition()
    {
        if (Thread.CurrentThread != Dispatcher.Thread)
        {
            UpdateSliderPositionDelegate function = new UpdateSliderPositionDelegate(UpdateSliderPosition);
            Dispatcher.Invoke(function, new object[] { });
        }
        else
        {
            double percentage = 0;  //calculate percentage
            percentage *= 100;

            slider.Value = percentage;  //this triggers the slider.ValueChanged event
        }
    }

次に、ユーザーがマウスドラッグでスライダーを操作しているときにキャプチャしたコードを追加しました。

<Slider Name="slider"
        Maximum="100" TickFrequency="10"
        ValueChanged="slider_ValueChanged"
        Thumb.DragStarted="slider_DragStarted"
        Thumb.DragCompleted="slider_DragCompleted">
</Slider>

そして、背後にコードを追加しました:

/// <summary>
/// True when the user is dragging the slider with the mouse
/// </summary>
bool sliderThumbDragging = false;

private void slider_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
{
    sliderThumbDragging = true;
}

private void slider_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
{
    sliderThumbDragging = false;
}

ユーザーがマウスドラッグでスライダーの値を更新しても、ストリームが読み取られて呼び出されるため、値は変更されますUpdateSliderPosition()。競合を防ぐために、UpdateSliderPosition()変更する必要がありました:

delegate void UpdateSliderPositionDelegate();
void UpdateSliderPosition()
{
    if (Thread.CurrentThread != Dispatcher.Thread)
    {
        UpdateSliderPositionDelegate function = new UpdateSliderPositionDelegate(UpdateSliderPosition);
        Dispatcher.Invoke(function, new object[] { });
    }
    else
    {
        if (sliderThumbDragging == false) //ensure user isn't updating the slider
        {
            double percentage = 0;  //calculate percentage
            percentage *= 100;

            slider.Value = percentage;  //this triggers the slider.ValueChanged event
        }
    }
}

これにより競合を防ぐことができますが、値がユーザーによって更新されているのか、への呼び出しによって更新されているのかを判断することはできませんUpdateSliderPosition()。これは、今度は内から設定されるさらに別のフラグによって修正されますUpdateSliderPosition()

    /// <summary>
    /// A value of true indicates that the slider value is being updated due to the stream being read (not by user manipulation).
    /// </summary>
    bool updatingSliderPosition = false;
    delegate void UpdateSliderPositionDelegate();
    void UpdateSliderPosition()
    {
        if (Thread.CurrentThread != Dispatcher.Thread)
        {
            UpdateSliderPositionDelegate function = new UpdateSliderPositionDelegate(UpdateSliderPosition);
            Dispatcher.Invoke(function, new object[] { });
        }
        else
        {
            if (sliderThumbDragging == false) //ensure user isn't updating the slider
            {
                updatingSliderPosition = true;
                double percentage = 0;  //calculate percentage
                percentage *= 100;

                slider.Value = percentage;  //this triggers the slider.ValueChanged event

                updatingSliderPosition = false;
            }
        }
    }

最後に、スライダーがユーザーによって更新されているのか、次の呼び出しによって更新されているのかを検出できますUpdateSliderPosition()

    private void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        if (updatingSliderPosition == false)
        {
            //user is manipulating the slider value (either by keyboard or mouse)
        }
        else
        {
            //slider value is being updated by a call to UpdateSliderPosition()
        }
    }

それが誰かを助けることを願っています!

于 2012-12-20T15:15:29.863 に答える
0

このスライダーのサブクラス化されたバージョンは、必要に応じて機能します。

public class NonRealtimeSlider : Slider
{
    static NonRealtimeSlider()
    {
        var defaultMetadata = ValueProperty.GetMetadata(typeof(TextBox));

        ValueProperty.OverrideMetadata(typeof(NonRealtimeSlider), new FrameworkPropertyMetadata(
        defaultMetadata.DefaultValue,
        FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
        defaultMetadata.PropertyChangedCallback,
        defaultMetadata.CoerceValueCallback,
        true,
        UpdateSourceTrigger.Explicit));
    }

    protected override void OnThumbDragCompleted(DragCompletedEventArgs e)
    {
        base.OnThumbDragCompleted(e);
        GetBindingExpression(ValueProperty)?.UpdateSource();
    }
}
于 2015-12-16T14:59:26.560 に答える
0

ユーザーが値を変更するために親指を使用していない (つまり、トラック バーのどこかをクリックした) 場合でも、操作が終了した情報を取得したい場合は、押されたポインターのスライダーにイベント ハンドラーをアタッチし、失われたイベントをキャプチャできます。キーボードイベントについても同じことができます

var pointerPressedHandler   = new PointerEventHandler(OnSliderPointerPressed);
slider.AddHandler(Control.PointerPressedEvent, pointerPressedHandler, true);

var pointerCaptureLostHandler   = new PointerEventHandler(OnSliderCaptureLost);
slider.AddHandler(Control.PointerCaptureLostEvent, pointerCaptureLostHandler, true);

var keyDownEventHandler = new KeyEventHandler(OnSliderKeyDown);
slider.AddHandler(Control.KeyDownEvent, keyDownEventHandler, true);

var keyUpEventHandler   = new KeyEventHandler(OnSliderKeyUp);
slider.AddHandler(Control.KeyUpEvent, keyUpEventHandler, true);

ここでの「魔法」は、スライダーの「内部」イベントを取得できるようにする true パラメータを末尾に持つ AddHandler です。イベント ハンドラー:

private void OnKeyDown(object sender, KeyRoutedEventArgs args)
{
    m_bIsPressed = true;
}
private void OnKeyUp(object sender, KeyRoutedEventArgs args)
{
    Debug.WriteLine("VALUE AFTER KEY CHANGE {0}", slider.Value);
    m_bIsPressed = false;
}

private void OnSliderCaptureLost(object sender, PointerRoutedEventArgs e)
{
    Debug.WriteLine("VALUE AFTER CHANGE {0}", slider.Value);
    m_bIsPressed = false;
}
private void OnSliderPointerPressed(object sender, PointerRoutedEventArgs e)
{
    m_bIsPressed = true;
}

m_bIsPressed メンバーは、ユーザーが現在スライダーを操作している (クリック、ドラッグ、またはキーボード) 場合に true になります。完了すると false にリセットされます。

private void OnValueChanged(object sender, object e)
{
    if(!m_bIsPressed) { // do something }
}
于 2015-08-07T14:39:52.450 に答える
-1
<Slider x:Name="PositionSlider" Minimum="0" Maximum="100"></Slider>

PositionSlider.LostMouseCapture += new MouseEventHandler(Position_LostMouseCapture);
PositionSlider.AddHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(Position_DragCompleted));
于 2009-07-29T22:10:34.607 に答える