【问题标题】:Frequency of WPF-slider ValueChanged-EventWPF-slider ValueChanged-Event 的频率
【发布时间】:2013-09-01 21:21:17
【问题描述】:

在我的应用程序中,我使用两个滑块来控制某些图像的亮度和对比度,并且当两个滑块中的任何一个更改其值属性时,每次都必须逐个像素地完全重新计算图像。 较小图像的重新计算完全没问题,不会导致任何问题,但是,较大的图像需要更长的时间重新计算,并且滑块的移动与鼠标指针相比略有延迟。我确实需要或多或少地实时重新计算图像,所以仅仅在DragCompleted 或类似地进行事件是不可接受的。

使用滑块的ValueChanged-事件初始化重新计算。 我认为这个问题的一个很好的解决方案是如果事件没有尽快触发,但至少要等 50 毫秒才能再次触发,但是是否有滑块的属性可以控制它?

我正在考虑的另一个解决方案是,在事件被处理时从滑块中删除事件,并在稍后再次添加它,但这可能会导致一些延迟,这也不是这里的首选。

我在任何地方都找不到关于这个主题的任何东西,所以如果有人有任何好的建议或方向我可以使用,我会非常感激。

【问题讨论】:

    标签: c# wpf events slider


    【解决方案1】:

    您还可以使用 WPF 4.5 中引入的BindingBase.Delay property

    只需将 Slider 的值绑定到绑定上的依赖属性设置 Delay。这将仅在特定时间(例如 500 毫秒)后导致值更新,这可以使您的应用程序更流畅。

    【讨论】:

      【解决方案2】:

      如果您认为您的应用程序不需要在每次触发 ValueChanged 事件时进行计算,您可以使用 Thumb 控件中的 DragCompleted 事件来确定用户完成拖动控件后的位置。

      <Slider Thumb.DragCompleted="Slider_DragCompleted_1"  
          Height="27" Margin="132,162,0,0" VerticalAlignment="Top" Width="303"/>
      

      当用户停止拖动时,

      private void Slider_DragCompleted_1(object sender, DragCompletedEventArgs e)
      {
          Slider s = sender as Slider;
          // Your code
          MessageBox.Show(s.Value.ToString());
      }
      

      但请注意,这仅在用户拖动滑块时有效。当用户单击滑块时不会触发。

      请参考this 处理鼠标点击等其他事件。

      如果你想计算一些时间延迟,那么你可以使用计时器。

      编辑: 根据您的要求,您可以这样做。 在“ValueChanged”事件中。

       // Start a new thread only if the thread is stopped 
       // or the thread has not been created yet.
       if (threadPopular == null || threadPopular.ThreadState == ThreadState.Stopped)
       {
                 threadPopular = new Thread(new ThreadStart(Your function));
                 threadPopular.Start();
        }
      

      【讨论】:

      • 感谢您的回答,但正如我在问题中提到的,图像必须在滑块移动时实时重新计算。
      • @phil13131 所以你想减少移动滑块的延迟?
      • 嗯,是的,这就是我在问题中指定的内容。
      • 我有,但问题是ValueChanged 的事件在用户滑动时经常被触发,所以我需要使用计时器继续检查是否应该启动新线程但我担心它本身也会造成很多滞后,而且由于我想等待足够长的时间来完成计算,我想知道后台线程是否真的那么重要......
      • @phil13131 不要使用计时器,而是使用thread.ThreadState == System.Threading.ThreadState.Running 检查线程的状态。如果线程没有运行,请创建一个新线程来进行计算。
      【解决方案3】:

      我可以使用Backgroundworker 来实现这一点,其中图像处理将在Backgroundworker 上异步完成。

      我还建议您可以在此处使用Timer 并将其滴答时间设置为舒适的值。在每个 sliderchanged 事件中,如果未启用,则启动计时器。在计时器滴答事件处理程序中,您可以检查后台工作人员是否正在工作,然后您可以取消先前的操作并将新操作放在上面。在 bacgroundworkerdone 事件处理程序中,只需停止计时器即可。

      谢谢

      【讨论】:

      • 感谢您的回复,虽然我觉得每次在ValueChanged 上启动计时器并不是一个好的解决方案,因为当用户滑动时会经常触发事件,所以您基本上会开始计时器可能有点太频繁了。另外,如果我想查看Backgroundworker是否仍在工作,我不知道是否需要计时器..
      • 定时器只是提供时间间隔,直到下一个处理完成。例如,如果我希望只有当我得到连续事件时,我才会以 250 毫秒的间隔处理图像。只有当它停止时我们才会启动计时器。
      【解决方案4】:

      虽然您可以使用 BindingBase.Delay,但这会导致延迟,即使需要进行一次更改也是如此。另一种方法可能是在 Slider Value 中使用 OneWay 绑定并使用如下异步命令:

      XAML 代码:

      <Slider Value="{Binding MyValue, Mode=OneWay}">
          <i:Interaction.Triggers>
           <i:EventTrigger EventName="ValueChanged">
              <mvvmlight:EventToCommand 
              Command="{Binding SetValueCommand, Mode=OneWay}"
              EventArgsConverter="{StaticResource 
              RoutedPropertyChangedEventArgsToDoubleConverter}"
              PassEventArgsToCommand="True" />
              </i:EventTrigger>
           </i:Interaction.Triggers>
      

      值转换器:

      using GalaSoft.MvvmLight.Command;
      
          public class RoutedPropertyChangedEventArgsToDoubleConverter : IEventArgsConverter
          {
              public object Convert(object value, object parameter)
              {
                  var args = (RoutedPropertyChangedEventArgs<double>)value;
                  var element = (FrameworkElement)parameter;
      
                  return args.NewValue;
              }
          }
      

      以及命令的回调:

          double _updateVal;
          Task _delay;
      
          private async void SetValue(double val)
          {            
              if (_delay != null)
              {
                  // in case of high frequency updates, most updates will return here
                  _updateVal = val; 
                  return;
              }
      
              // only the first update reaches here
      
              // caluclate the image here 
              MyValue = val; // update slider
      
              _delay = Task.Delay(500);
              await _delay;
      
              // in case there are pending updates:
              while (_updateVal.HasValue)
              {
                  // caluclate the image here 
                  MyValue = _updateVal.Value; // update slider
      
                  _updateVal = null;
      
                  _delay = Task.Delay(500);
                  await _delay;
              }
      
              _delay = null;
      
          }
      

      这样,您只能减少图像计算的频率,而不会在第一次值更改时出现明显延迟。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-10-06
        • 1970-01-01
        • 2013-11-06
        • 1970-01-01
        • 2017-02-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多