我正在开发一个WPF应用程序,用户可以通过按下UI上的按钮来启动进程。然后可以向用户提示他们必须执行的一系列动作以完成该过程。视图负责将初始请求传递给域以启动进程。该视图还负责显示用户完成该过程必须执行的步骤。 另一方面,域是对于确定必须由用户执行的步骤的正确响应。该域还能够检测用户何时完成所请求的步骤。
如果用户启动进程,并且该进程要求他们执行某些物理操作,那么我希望弹出一个框,其中会显示一条描述他们必须执行的操作的消息。操作完成后,域将检测到该操作,窗口应自动关闭。
将View中的请求传递给域很简单。我使用wpf ICommand模式执行此操作。它将信息传递回我发现具有挑战性的另一种方式。我知道绑定和INotifyProperyChanged接口,但我觉得这不适合我想要做的事情。
所以,这是我最初的尝试......
此接口由View实现并由Domain使用。它允许域与用户通信;
public interface IUserRequestMedium
{
/// <summary>
/// Ask the user to perform an action. User does
/// not need to provide any feedback via the user
/// interface, since it is possible for the
/// application to detect when the action has been
/// carried out by the user. The dialog will be closed
/// when either the requested action has been detected,
/// or the user aborts.
/// </summary>
/// <param name="message">
/// Request to be displayed to the user.
/// </param>
/// <param name="userAbortCallback">
/// Callback invoked by the view when the user cancels
/// the request.
/// </param>
/// <param name="actionDetectedCallback">
/// Callback invoked by the domain to confirm the
/// that the requested action has been completed.
/// </param>
void AskUserToPerformDetectableAction(
string message, Action userAbortCallback,
out Action actionDetectedCallback);
}
这是View代码隐藏。其中一些代码来自网络上的教程(并随后被修改)。它不起作用,但我希望它能传达我的意图。
public partial class MainWindow : Window, IUserRequestMedium
{
// Constructor and other stuff...
public void AskUserToPerformDetectableAction(
string message, Action userAbortCallback,
out Action actionDetectedCallback)
{
Action closeWindow;
NewWindowThread(
() => new ActionRequestBox(message, userAbortCallback),
out closeWindow);
actionDetectedCallback = closeWindow;
}
private Window newWindow;
private void NewWindowThread(
Func<Window> construction,
out Action closeWindow)
{
var thread = new Thread(() =>
{
newWindow = construction();
newWindow.Show();
newWindow.Closed += (sender, e) => newWindow.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Window rememberedWindow = newWindow;
closeWindow = () =>
{
if (rememberedWindow != null)
rememberedWindow.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(Close));
};
}
}
这是域中的一个用法示例;
public class SomeDomainClass
{
IUserRequestMedium userRequestMedium; // assume this has been assigned in constructor
private Action notifyUserOfActionDetected;
public void PerformSomeProcess()
{
bool processCannotBeCompletedWithoutPowerCycle = ...; // some logic
if (processCannotBeCompletedWithoutPowerCycle)
{
userRequestMedium.AskUserToPerformDetectableAction(
"Please cycle the power on the external device",
CancelProcess,
out notifyUserOfActionDetected);
}
}
public void CancelProcess()
{
// User doesn't want to perform the required action
// so process must be aborted...
}
private void OnPowerCycleDetected()
{
notifyUserOfActionDetected();
}
}
我该如何使这项工作?这是我遇到的跨线程方面。当域检测到操作时,我没有成功地使窗口自动关闭。
或者,退一步,是否有更好的方法来解决这个问题?
答案 0 :(得分:0)
在学习了一些关于Dispatcher.Invoke之后,这就是我最终的结果。到目前为止它似乎工作得很好。
private Window activeRequestBox;
// invoked on domain thread
public void AskUserToPerformDetectableAction(
string message, Action userAbortCallback,
out Action actionDetectedCallback)
{
OpenDetectableActionRequestBox(message, userAbortCallback);
actionDetectedCallback = CloseRequestBox;
}
private void OpenDetectableActionRequestBox(
string message, Action userAbortCallback)
{
Action openWindow =
() =>
{
activeRequestBox = new DetectableActionRequestBox(
message, userAbortCallback);
activeRequestBox.Closed += RequestBoxClosedHandler;
activeRequestBox.Show();
};
this.Dispatcher.Invoke(openWindow);
}
// invoked on request box thread
private void RequestBoxClosedHandler(object sender, EventArgs e)
{
activeRequestBox = null;
}
// invoked on domain thread
private void CloseRequestBox()
{
if (activeRequestBox != null)
{
Action closeWindow =
() => activeRequestBox.Close();
this.Dispatcher.Invoke(closeWindow);
}
}