Silverlight Confirm Dialog to Pause Thread

2019-02-10 04:47发布

问题:

I'm trying to do a confirmation dialog using Silverlight's ChildWindow object.

Ideally, I'd like it to work like MessageBox.Show(), where the entire application halts until an input is received from the user.

For example:

for (int i = 0; i < 5; i++)
{
    if (i==3 && MessageBox.Show("Exit early?",
        "Iterator", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
    {
        break;
    }
}

Would stop the iteration at 3 if the user hits OK...

However, if I were to do something along the lines:

ChildWindow confirm = new ChildWindow();
confirm.Title = "Iterator";
confirm.HasCloseButton = false;
Grid container = new Grid();

Button closeBtn = new Button();
closeBtn.Content = "Exit early";
closeBtn.Click += delegate { confirm.DialogResult = true; confirm.Close(); };
container.Children.Add(closeBtn);

Button continueBtn = new Button();
continueBtn.Content = "Continue!";
continueBtn.Click += delegate { confirm.DialogResult = false; confirm.Close(); };
container.Children.Add(continueBtn);

confirm.Content = container;

for(int i=0;i<5;i++) {
  if (i==3) {
    confirm.Show();
    if (confirm.DialogResult.HasResult && (bool)confirm.DialogResult) {
      break;
    }
  }
}

This clearly would not work, as the thread isn't halted... confirm.DialogResult.HasResult would be false, and the loop would continue past 3.

I'm just wondering, how I could go about this properly. Silverlight is single-threaded, so I can't just put the thread to sleep and then wake it up when I'm ready, so I'm just wondering if there's anything else that people could recommend?

I've considered reversing the logic - ie, passing the actions I want to occur to the Yes/No events, but in my specific case this wouldn't quite work.

Thanks in advance!

回答1:

I don't think you'll be able to block your code in a message loop the way you can with WinForms' ShowDialog.

However, you can misuse iterators to achieve the same effect:

interface IAction { void Execute(Action callback); }

public static void ExecAction(IEnumerator<IAction> enumerator) {
    if (enumerator.MoveNext())
        enumerator.Current.Execute(() => ExecAction(enumerator));
}

class DialogAction : ChildWindow, IAction {
    void IAction.Execute(Action callback) {
       //Show the window, then call callback when it's closed
    }
}

IEnumerator<IAction> YourMethod() { 
    ...
    var confirm = new DialogAction();
    yield return confirm;
    if (confirm.DialogResult.HasResult && (bool)confirm.DialogResult)
        yield break;
    ...
}

To use this system, you would write ExecAction(YourMethod());. Note that this would be a semi-blocking call, and that I haven't tested this at all.

C#5's new async features work exactly the same way (in fact, the initial versions of the async compiler code were heavily based on the existing iterator implementation), but with nicer syntactic support.



回答2:

You can achieve this quiet easily with RX Framework:

var continued = Observable.FromEvent<RoutedEventArgs>(continueBtn, "Click");

var iter = new Subject<int>();

var ask = iter.Where(i => i == 3).Do(_ => confirm.Show());

iter.Where(i => i != 3 && i < 10)
    .Merge(ask.Zip(continued, (i, _) => i))
    .Do(i => Debug.WriteLine("Do something for iteration {0}", i))
    .Select(i => i + 1)
    .Subscribe(iter);

iter.OnNext(0);

The solution easily scales for any rule determining when to show a dialog. E.g. suppose we want to block the iteration and request user confirmation every 3 iterations. All you have to do is to replace condition i == 3 with i % 3 == 0 (and i != 3 with i % 3 != 0).



回答3:

Check out this project http://silverlightmsgbox.codeplex.com/. It presents a simple but presentable implementation of several useful message boxes i.e. confirm, error, info, user input, etc. and might be helpful to you. Good luck.