我有一个Textblock
,当鼠标悬停在它上面时,我想打开Popup
。我已使用IsOpen
将MultiBinding
属性绑定到Popup
的{{1}}和IsMouseOver
的{{1}},但除此之外当鼠标从文本移动到弹出窗口时,弹出窗口会闪烁。
闪烁的原因是幕后事件的执行顺序:
鼠标从TextBlock
移至IsMouseOver
- > textblock
popup
设置为IsMouseOver
- >转换器称为,两个参数均为false - >只有textblock
的{{1}}设置为false
- >转换器执行,两个参数都为假,弹出消失 - >转换器再次调用和执行,因为之前为IsMouseOver
弹出窗口引发了另一个事件,这次{em> IsMouseOver popup
true
- >弹出窗口再次出现。我尝试添加IsMouseOver
,但它从未关闭/表现与预期不同。
问题:如何避免闪烁?
代码:
Popup
转换器代码
True
答案 0 :(得分:1)
感谢this post,我能够使用延迟多重绑定解决问题。请注意,多重绑定转换器是通用的,可以接受任何常规的多重绑定转换器加上延迟。
我的XAML:
<Popup.IsOpen>
<local:DelayedMultiBindingExtension Converter="{StaticResource PopupIsOpenConverter}" Delay="0:0:0.01">
<Binding ElementName="PopupX" Path="IsMouseOver" Mode="OneWay" />
<Binding ElementName="RecipientsTextBlock" Path="IsMouseOver" Mode="OneWay" />
</local:DelayedMultiBindingExtension>
</Popup.IsOpen>
我的多重绑定转换器:
[ContentProperty("Bindings")]
internal sealed class DelayedMultiBindingExtension : MarkupExtension, IMultiValueConverter, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Collection<BindingBase> Bindings { get; }
public IMultiValueConverter Converter { get; set; }
public object ConverterParameter { get; set; }
public CultureInfo ConverterCulture { get; set; }
public BindingMode Mode { get; set; }
public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
private object _undelayedValue;
private object _delayedValue;
private DispatcherTimer _timer;
public object CurrentValue
{
get { return _delayedValue; }
set
{
_delayedValue = _undelayedValue = value;
_timer.Stop();
}
}
public int ChangeCount { get; private set; } // Public so Binding can bind to it
public TimeSpan Delay
{
get { return _timer.Interval; }
set { _timer.Interval = value; }
}
public DelayedMultiBindingExtension()
{
this.Bindings = new Collection<BindingBase>();
_timer = new DispatcherTimer();
_timer.Tick += Timer_Tick;
_timer.Interval = TimeSpan.FromMilliseconds(10);
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (valueProvider == null) return null;
var bindingTarget = valueProvider.TargetObject as DependencyObject;
var bindingProperty = valueProvider.TargetProperty as DependencyProperty;
var multi = new MultiBinding { Converter = this, Mode = Mode, UpdateSourceTrigger = UpdateSourceTrigger };
foreach (var binding in Bindings) multi.Bindings.Add(binding);
multi.Bindings.Add(new Binding("ChangeCount") { Source = this, Mode = BindingMode.OneWay });
var bindingExpression = BindingOperations.SetBinding(bindingTarget, bindingProperty, multi);
return bindingTarget.GetValue(bindingProperty);
}
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var newValue = Converter.Convert(values.Take(values.Length - 1).ToArray(),
targetType,
ConverterParameter,
ConverterCulture ?? culture);
if (Equals(newValue, _undelayedValue)) return _delayedValue;
_undelayedValue = newValue;
_timer.Stop();
_timer.Start();
return _delayedValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return Converter.ConvertBack(value, targetTypes, ConverterParameter, ConverterCulture ?? culture)
.Concat(new object[] { ChangeCount }).ToArray();
}
private void Timer_Tick(object sender, EventArgs e)
{
_timer.Stop();
_delayedValue = _undelayedValue;
ChangeCount++;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ChangeCount)));
}
}
答案 1 :(得分:0)
我偶然发现了同样的闪烁问题,并且很想使用你的解决方案,但首先寻找更轻量级的东西。
我用另一种方式解决了它(我通常会像瘟疫一样避免):背后的代码。 在这种情况下,只是根据MouseOver在几个控件上打开/关闭一个弹出窗口,没有更改模型,这是好的,虽然imho。
这是我的解决方案:
public class CodebehindOfSomeView
{
private readonly DispatcherTimer m_ClosePopupTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1) };
public CodebehindOfSomeView()
{
InitializeComponent();
m_ClosePopupTimer.Tick += ClosePopupTimer_Tick;
}
private void ClosePopupTimer_Tick(object _sender, EventArgs _e)
{
SomePopup.IsOpen = false;
}
private void PopupMouseOverControls_MouseEnter(object _sender, System.Windows.Input.MouseEventArgs _e)
{
m_ClosePopupTimer.Stop();
SomePopup.IsOpen = true;
}
private void PopupMouseOverControls_MouseLeave(object _sender, System.Windows.Input.MouseEventArgs _e)
{
m_ClosePopupTimer.Start();
}
}
没有使用转换器。 在视图中,只需将PopupMouseOverControls_MouseEnter和PopupMouseOverControls_MouseLeave添加到每个所需控件的MouseEnter和MouseLeave事件中。多数民众赞成。
如果控制器相互接触,则毫秒的时间跨度实际上足以彻底摆脱闪烁。
如果您想给用户一点时间将鼠标从一个控件移动到另一个控件(在其他控件的像素上),只需提高时间长度即可。