我目前在WPF中制作MP3播放器,我想制作一个滑块,允许用户通过向左或向右滑动滑块来寻找MP3中的特定位置.
我已尝试使用该ValueChanged事件,但每次更改值时都会触发,因此如果将其拖动,事件将多次触发,我希望事件仅在用户完成拖动滑块时触发然后获取新事件价值.
我怎样才能做到这一点?
[更新]
我在MSDN上发现这篇文章基本上讨论了同样的事情,他们提出了两个"解决方案"; 要么继承Slider,要么在一个时间跨度后调用动作DispatcherTimer的ValueChanged事件中调用a.
你能想出比上面提到的更好的东西吗?
Ala*_*lan 70
除了使用Thumb.DragCompleted 事件,您还可以同时使用ValueChanged 和Thumb.DragStarted,这样,当用户通过按下方向键或单击滑动条上修改了价值,你不会失去功能.
XAML:
<Slider ValueChanged="Slider_ValueChanged"
Thumb.DragStarted="Slider_DragStarted"
Thumb.DragCompleted="Slider_DragCompleted"/>
Run Code Online (Sandbox Code Playgroud)
代码背后:
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);
}
Run Code Online (Sandbox Code Playgroud)
Yot*_*aXP 51
你可以使用拇指的'DragCompleted'事件.不幸的是,这只是在拖动时触发,因此您需要单独处理其他点击和按键.如果您只希望它可拖动,则可以通过将LargeChange设置为0并将Focusable设置为false来禁用这些移动滑块的方法.
例:
<Slider Thumb.DragCompleted="MySlider_DragCompleted" />
Run Code Online (Sandbox Code Playgroud)
Pet*_*ter 13
<Slider PreviewMouseUp="MySlider_DragCompleted" />
Run Code Online (Sandbox Code Playgroud)
适合我.
您想要的值是mousup事件之后的值,无论是侧面点击还是拖动句柄后.
由于MouseUp没有隧道传输(它可以在它之前进行处理),因此您必须使用PreviewMouseUp.
另一个MVVM友好的解决方案(我对答案不满意)
视图:
<Slider Maximum="100" Value="{Binding SomeValue}"/>
Run Code Online (Sandbox Code Playgroud)
视图模型:
public class SomeViewModel : INotifyPropertyChanged
{
private readonly object _someValueLock = new object();
private int _someValue;
public int SomeValue
{
get { return _someValue; }
set
{
_someValue = value;
OnPropertyChanged();
lock (_someValueLock)
Monitor.PulseAll(_someValueLock);
Task.Run(() =>
{
lock (_someValueLock)
if (!Monitor.Wait(_someValueLock, 1000))
{
// do something here
}
});
}
}
}
Run Code Online (Sandbox Code Playgroud)
它被延迟(1000在给定示例中为ms)操作.为滑块(通过鼠标或键盘)完成的每个更改创建新任务.在开始任务之前,它发出信号(通过使用Monitor.PulseAll,甚至Monitor.Pulse可能就足够了)来运行已经完成的任务(如果有的话)来停止.只有在超时内没有获得信号时才会发生某些事情Monitor.Wait.
为什么这个解决方 我不喜欢产生行为或在视图中进行不必要的事件处理.所有代码都在一个地方,不需要额外的事件,ViewModel可以选择对每个值的变化做出反应,或者在用户操作结束时做出反应(这增加了大量的灵活性,特别是在使用绑定时).
我的实现基于@Alan 和@SandRock 的回答:
public class SliderValueChangeByDragBehavior : Behavior<Slider>
{
private bool hasDragStarted;
/// <summary>
/// On behavior attached.
/// </summary>
protected override void OnAttached()
{
AssociatedObject.AddHandler(Thumb.DragStartedEvent, (DragStartedEventHandler)Slider_DragStarted);
AssociatedObject.AddHandler(Thumb.DragCompletedEvent, (DragCompletedEventHandler)Slider_DragCompleted);
AssociatedObject.ValueChanged += Slider_ValueChanged;
base.OnAttached();
}
/// <summary>
/// On behavior detaching.
/// </summary>
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.RemoveHandler(Thumb.DragStartedEvent, (DragStartedEventHandler)Slider_DragStarted);
AssociatedObject.RemoveHandler(Thumb.DragCompletedEvent, (DragCompletedEventHandler)Slider_DragCompleted);
AssociatedObject.ValueChanged -= Slider_ValueChanged;
}
private void updateValueBindingSource()
=> BindingOperations.GetBindingExpression(AssociatedObject, RangeBase.ValueProperty)?.UpdateSource();
private void Slider_DragStarted(object sender, DragStartedEventArgs e)
=> hasDragStarted = true;
private void Slider_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
{
hasDragStarted = false;
updateValueBindingSource();
}
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!hasDragStarted)
updateValueBindingSource();
}
}
Run Code Online (Sandbox Code Playgroud)
您可以这样应用它:
...
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:myWhateverNamespace="clr-namespace:My.Whatever.Namespace;assembly=My.Whatever.Assembly"
...
<Slider
x:Name="srUserInterfaceScale"
VerticalAlignment="Center"
DockPanel.Dock="Bottom"
IsMoveToPointEnabled="True"
Maximum="{x:Static localLibraries:Library.MAX_USER_INTERFACE_SCALE}"
Minimum="{x:Static localLibraries:Library.MIN_USER_INTERFACE_SCALE}"
Value="{Binding Source={x:Static localProperties:Settings.Default}, Path=UserInterfaceScale, UpdateSourceTrigger=Explicit}">
<i:Interaction.Behaviors>
<myWhateverNamespace:SliderValueChangeByDragBehavior />
</i:Interaction.Behaviors>
</Slider>
Run Code Online (Sandbox Code Playgroud)
我已将 UpdateSourceTrigger 设置为显式,正如行为所做的那样。并且您需要 nuget 包 Microsoft.Xaml.Behaviors(.Wpf/.Uwp.Managed)。