使用响应式扩展更改不透明度
Changing Opacity With Reactive Extensions
<rant>Yes, I know this would be easier to implement in WPF. I hear that a lot. Sadly, It is not possible.
</rant>
我正在编写一个 WinForms 应用程序,我需要 "fade" 控件输入输出。透明度在 WinForms 中几乎是不可能的,所以我尝试使用不透明度:想法是在一段时间内更改每个子控件的 ForeColor 属性 的 Alpha 通道。这似乎是处理我的 Reactive Extensions 的最佳时机!
我可能的解决方案是:
private void FadeOut()
{
// our list of values for the alpha channel (255 to 0)
var range = Enumerable.Range(0,256).Reverse().ToList();
// how long between each setting of the alpha (huge value for example only)
var delay = Timespan.FromSeconds(0.5);
// our "trigger" sequence, every half second
Observable.Interval(delay)
// paired with the values from the range - we just keep the range
.Zip(range, (lhs, rhs) => rhs)
// make OnNext changes on the UI thread
.ObserveOn(SynchronizationContext.Current)
// do this every time a value is rec'd from the sequence
.Subscribe(
// set the alpha value
onNext:ChangeAlphaValues,
// when we're done, really hide the control
onCompleted: () => Visible = false,
// good citizenry
onError: FailGracefully);
}
// no need to iterate the controls more than once - store them here
private IEnumerable<Control> _controls;
private void ChangeAlphaValues(int alpha)
{
// get all the controls (via an extension method)
var controls = _controls ?? this.GetAllChildControls(typeof (Control));
// iterate all controls and change the alpha
foreach (var control in controls)
control.ForeColor = Color.FromArgb(alpha, control.ForeColor);
}
...这看起来令人印象深刻,但它不起作用。真正令人印象深刻的部分是它无法以 两种方式 工作!如果我坚持下去,我的下一次绩效评估会得到 "Far Exceed"。 :-)
- (不是真正的问题的 Rx 部分)alpha 值实际上似乎没有任何区别。尽管设置了这些值,显示看起来还是一样。
- 如果我在序列完成之前关闭 window,我会收到以下错误:
System.InvalidOperationException was unhandled
Message: An unhandled exception of type 'System.InvalidOperationException' occurred in System.Reactive.Core.dll
Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
我想这就是取消令牌派上用场的地方 - 但我不知道如何实现它。
我需要指导:
如果序列仍然是 运行,如何优雅地关闭 window(即不抛出错误),以及如何使颜色的 alpha 值实际 更改后显示。
...或者这可能是完全错误的方法。
我愿意接受其他建议。
我无法在 WinForms 透明度部分帮助您。也许你应该把问题分成两部分。
但对于Rx部分,您只需在window关闭时取消订阅即可。 Subscribe
returns一个IDisposable
。您应该在 Closed
活动中处理它。
而且,由于我假设在 window 关闭之前可能会多次调用此淡入淡出动画,我们可以使用 Rx 助手 SerialDisposable
.
最后,Interval
居然returns算了。我们可以使用它来通过计算所需的 alpha 来简化您的 observable。
private SerialDisposable _fadeAnimation = new SerialDisposable();
private void FadeOut()
{
// how long between each setting of the alpha (huge value for example only)
var delay = Timespan.FromSeconds(0.5);
_fadeAnimation.Disposable = Observable
.Interval(delay)
// 256 animation steps
.Take(256)
// calculate alpha
.Select(i => 255 - i)
.ObserveOn(SynchronizationContext.Current)
.Subscribe(
// set the alpha value
onNext:ChangeAlphaValues,
// when we're done, really hide the control
onCompleted: () => Visible = false,
// good citizenry
onError: FailGracefully);
}
private void Form1_Closed()
{
_fadeAnimation.Dispose();
}
<rant>Yes, I know this would be easier to implement in WPF. I hear that a lot. Sadly, It is not possible. </rant>
我正在编写一个 WinForms 应用程序,我需要 "fade" 控件输入输出。透明度在 WinForms 中几乎是不可能的,所以我尝试使用不透明度:想法是在一段时间内更改每个子控件的 ForeColor 属性 的 Alpha 通道。这似乎是处理我的 Reactive Extensions 的最佳时机!
我可能的解决方案是:
private void FadeOut()
{
// our list of values for the alpha channel (255 to 0)
var range = Enumerable.Range(0,256).Reverse().ToList();
// how long between each setting of the alpha (huge value for example only)
var delay = Timespan.FromSeconds(0.5);
// our "trigger" sequence, every half second
Observable.Interval(delay)
// paired with the values from the range - we just keep the range
.Zip(range, (lhs, rhs) => rhs)
// make OnNext changes on the UI thread
.ObserveOn(SynchronizationContext.Current)
// do this every time a value is rec'd from the sequence
.Subscribe(
// set the alpha value
onNext:ChangeAlphaValues,
// when we're done, really hide the control
onCompleted: () => Visible = false,
// good citizenry
onError: FailGracefully);
}
// no need to iterate the controls more than once - store them here
private IEnumerable<Control> _controls;
private void ChangeAlphaValues(int alpha)
{
// get all the controls (via an extension method)
var controls = _controls ?? this.GetAllChildControls(typeof (Control));
// iterate all controls and change the alpha
foreach (var control in controls)
control.ForeColor = Color.FromArgb(alpha, control.ForeColor);
}
...这看起来令人印象深刻,但它不起作用。真正令人印象深刻的部分是它无法以 两种方式 工作!如果我坚持下去,我的下一次绩效评估会得到 "Far Exceed"。 :-)
- (不是真正的问题的 Rx 部分)alpha 值实际上似乎没有任何区别。尽管设置了这些值,显示看起来还是一样。
- 如果我在序列完成之前关闭 window,我会收到以下错误:
System.InvalidOperationException was unhandled Message: An unhandled exception of type 'System.InvalidOperationException' occurred in System.Reactive.Core.dll Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
我想这就是取消令牌派上用场的地方 - 但我不知道如何实现它。
我需要指导:
如果序列仍然是 运行,如何优雅地关闭 window(即不抛出错误),以及如何使颜色的 alpha 值实际 更改后显示。
...或者这可能是完全错误的方法。
我愿意接受其他建议。
我无法在 WinForms 透明度部分帮助您。也许你应该把问题分成两部分。
但对于Rx部分,您只需在window关闭时取消订阅即可。 Subscribe
returns一个IDisposable
。您应该在 Closed
活动中处理它。
而且,由于我假设在 window 关闭之前可能会多次调用此淡入淡出动画,我们可以使用 Rx 助手 SerialDisposable
.
最后,Interval
居然returns算了。我们可以使用它来通过计算所需的 alpha 来简化您的 observable。
private SerialDisposable _fadeAnimation = new SerialDisposable();
private void FadeOut()
{
// how long between each setting of the alpha (huge value for example only)
var delay = Timespan.FromSeconds(0.5);
_fadeAnimation.Disposable = Observable
.Interval(delay)
// 256 animation steps
.Take(256)
// calculate alpha
.Select(i => 255 - i)
.ObserveOn(SynchronizationContext.Current)
.Subscribe(
// set the alpha value
onNext:ChangeAlphaValues,
// when we're done, really hide the control
onCompleted: () => Visible = false,
// good citizenry
onError: FailGracefully);
}
private void Form1_Closed()
{
_fadeAnimation.Dispose();
}