В итоге я поддержал это с помощью пользовательского поведения, код которого приведен ниже. Я более чем рад переключиться на более чистую реализацию (например, такую, которая не требует добавления поведения к кнопке), если кто-то может подумать о ней.
Несколько замечаний по реализации (основанной на Rx):
- Предполагается, что нажатие кнопки отмены всегда приведет к закрытию окна. Если ваша кнопка отмены может быть отменена (так сказать), это приведет к тому, что кнопка не будет нажата при закрытии окна.
- Если кнопка каким-то образом удалена из окна (в нашем случае другой контент переместится в область), это приведет к отключению поведения.
- Все обработчики событий удаляются в конце последовательности, независимо от того, было ли окно закрыто, была ли нажата кнопка или была удалена кнопка.
А вот и код:
public class DialogCancelButtonBehavior : Behavior<Button>
{
protected override void OnAttached()
{
base.OnAttached();
Button button = AssociatedObject;
GetWindowAsync(button)
.SelectMany(window => GetWindowClosed(window))
.Where(_ => button.IsCancel)
.TakeUntil(GetButtonClicked(button))
.TakeUntil(GetButtonUnloaded(button))
.Subscribe(_ => ClickButton(button));
}
private IObservable<Window> GetWindowAsync(Button button)
{
var buttonLoaded = Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
h => new RoutedEventHandler(h),
h => button.Loaded += h,
h => button.Loaded -= h);
return button.IsLoaded
? Observable.Return(Window.GetWindow(button))
: buttonLoaded.Take(1).Select(_ => Window.GetWindow(button));
}
private IObservable<IEvent<EventArgs>> GetWindowClosed(Window window)
{
return Observable.FromEvent<EventHandler, EventArgs>(
h => new EventHandler(h),
h => window.Closed += h,
h => window.Closed -= h);
}
private IObservable<IEvent<RoutedEventArgs>> GetButtonClicked(Button button)
{
return Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
h => new RoutedEventHandler(h),
h => button.Click += h,
h => button.Click -= h);
}
private IObservable<IEvent<RoutedEventArgs>> GetButtonUnloaded(Button button)
{
return Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
h => new RoutedEventHandler(h),
h => button.Unloaded += h,
h => button.Unloaded -= h);
}
private void ClickButton(Button button)
{
ButtonAutomationPeer peer =
(ButtonAutomationPeer)UIElementAutomationPeer.CreatePeerForElement(button);
IInvokeProvider invokeProv =
peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
invokeProv.Invoke();
}
}