最终目标是拥有一个调用回调函数的WCF服务器,每个订阅的WCF客户端在回调上显示一个新的表单实例。
为了实现这一点,我使用pub / sub模式,客户端通过命名管道连接到服务器并调用Subscribe(),然后在从事件日志中读取事件时接收回调,EventReceived() 。在回调的实现中,客户端实例化表单的新实例并显示它。
问题是客户端显示的表单会立即冻结。任何的意见都将会有帮助。下面显示了具有相同行为的代码的简化版本。
客户端代码(Form1只是一个空白的默认表单,没有其他代码/装饰器.Form2的唯一目的是记录SynchronizationContext.Current以供稍后使用的回调。)
static class Program
{
public static SynchronizationContext SynchronizationContext;
[STAThread]
static void Main()
{
ClientContract callbacks = new ClientContract();
DuplexChannelFactory<IEventService> pipeFactory = new DuplexChannelFactory<IEventService>(
callbacks, new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/EventPipe"));
IEventService pipeProxy = pipeFactory.CreateChannel();
pipeProxy.Subscribe();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form2());
}
}
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
class ClientContract : IClientContract
{
public void EventReceived(int eventId)
{
Form1 newForm = new Form1();
newForm.Show();
}
}
客户端代码 - Form2构造函数
public Form2()
{
InitializeComponent();
Program.SynchronizationContext = SynchronizationContext.Current;
}
服务器代码
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(EventService), new Uri[] { new Uri("net.pipe://localhost") }))
{
host.AddServiceEndpoint(typeof(IEventService), new NetNamedPipeBinding(), "EventPipe");
host.Open();
Console.WriteLine("Service started");
do
{
Console.WriteLine("Enter \"callback\" to trigger a callback, or enter to quit");
string input = Console.ReadLine();
if (input == "callback")
{
EventService.PublishEvent(3);
}
else if (input == "")
{
break;
}
} while (true);
host.Close();
}
}
}
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.PerCall)]
class EventService : IEventService
{
public static event NewEventHandler NewEventAvailableHandler;
public delegate void NewEventHandler(EventLogEventArgs e);
private NewEventHandler newEventHandler;
private IClientContract callback = null;
public static void PublishEvent(int eventId)
{
EventLogEventArgs e = new EventLogEventArgs();
e.eventId = eventId;
if (NewEventAvailableHandler != null)
{
NewEventAvailableHandler(e);
}
}
public void Subscribe()
{
callback = OperationContext.Current.GetCallbackChannel<IClientContract>();
newEventHandler = new NewEventHandler(MagazineService_NewIssueAvailableEvent);
NewEventAvailableHandler += newEventHandler;
}
public void Unsubscribe()
{
NewEventAvailableHandler -= newEventHandler;
}
public void MagazineService_NewIssueAvailableEvent(EventLogEventArgs e)
{
callback.EventReceived(e.eventId);
}
}
public class EventLogEventArgs : EventArgs
{
public int eventId;
}
共享代码(通过类库DLL文件实现)
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientContract))]
public interface IEventService
{
[OperationContract(IsOneWay = true, IsInitiating = true)]
void Subscribe();
[OperationContract(IsOneWay = true, IsInitiating = true)]
void Unsubscribe();
}
public interface IClientContract
{
[OperationContract(IsOneWay = true)]
void EventReceived(int eventId);
}
** 修改 **
正如评论中所指出的,回调是在工作线程上收到的,但UI事件需要在主线程上发生。我能够让它工作,但感觉就像一个黑客,因为我有一个未使用的第二个表单,只是为了使用回调的SynchronizationContext。