How to prevent firing of event multiple times on rapid click of a button in Xamarin forms - xamarin

How can I avoid invoking of the same event multiple times when a button is clicked rapidly.
Below is the code:
I've created a Custom Delegate Command as below
View Model
namespace TestProject.ViewModels
{
public class TestViewModel
{
public CustomDelegateCommand MenuButtonClickCommand { get; set; }
public TestViewModel()
{
MenuButtonClickCommand = new CustomDelegateCommand (async () => await ShowMenuAction());
}
private async Task ShowMenuAction()
{
//await some stuff
}
}
}
CustomDelegateCommand.cs
public class CustomDelegateTimerCommand : DelegateCommand
{
public CustomDelegateTimerCommand(Action executeMethod, Func<bool> validateMethod, Action onBusy = default(Action)) : base(executeMethod)
{
BackgroundTaskWaitHandle = new EventWaitHandle(true, EventResetMode.ManualReset);
_validateMethod = validateMethod;
_onBusy = onBusy;
}
}
The problem I'm facing is whenever a user clicks on the button rapidly, the menu list popup is opening multiple times.
I have lot of commands in my project and I need a solution that would work globally.
I tried to resolve the issue like below using ObservesCanExecute() but I don't like the idea of creating a separate variable for every command as I've a lot of commands in my project and I don't want the button to go in to disabled state when CanExecute = false.
ViewModel
MenuButtonClickCommand = new CustomDelegateCommand (async () => await ShowMenuAction().ObservesCanExecute(() => CanExecute );
private async Task ShowMenuAction()
{
CanExecute = false;
//await some stuff
CanExecute = true;
}
Any help is much appreciated!

There are 2 solutions to it. One is when you use, MVVM other is when you dont.
The non MVVM solution is delaying the execution of method for certain amount of time, like this:
public class SingleClickListener
{
private bool hasClicked;
private Action<object, EventArgs> _setOnClick;
public SingleClickListener(Action<object, EventArgs> setOnClick)
{
_setOnClick = setOnClick;
}
public void OnClick(object v, EventArgs e)
{
if (!hasClicked)
{
_setOnClick(v, e);
hasClicked = true;
}
reset();
}
private void reset()
{
Android.OS.Handler mHandler = new Android.OS.Handler();
mHandler.PostDelayed(new Action(() => { hasClicked = false; }), 500);
}
}
And then when you subscribe the onclick event:
var buttonNa = new Button { Text = "Test Button" };
buttonNa.Clicked += new SingleClickListener((sender, e) =>
{
//DO something
}).OnClick;
The Mvvm solution is bit more complicated, but its not as hacky.
TestCommand = new Command(
execute: async () =>
{
IsEditing = true;
RefreshCanExecutes();
//Fire Method
TestMethod();
},
canExecute: () =>
{
return !IsEditing;
});
public void RefreshCanExecutes()
{
(TestCommand as Command).ChangeCanExecute();
}
public void TestMethod()
{
//DO something
IsEditing = false;
RefreshCanExecutes();
}
Obviously dont forget to bind your commands to xaml :)
also second solution actually disables the button, so user cannot even tap it, first one however only ignores further taps, till time delay has finished.

Create a new class which inherits from Xamarin.Forms.Button with delay in click event, than add it to your xmal.
public class DelayedButton : Xamarin.Forms.Button
{
public DelayedButton()
{
this.Clicked += DelayedButton_Clicked;
}
async private void DelayedButton_Clicked(object sender, EventArgs e)
{
this.IsEnabled = false;
await Task.Delay(Delay);
this.IsEnabled = true;
}
public int Delay { get; set; } = 500;
}
In XAML:
<yourNameSpace:DelayedButton Delay="300" Text="DelayedButton" Command="{Binding ClickCommand}"/>

Related

I wanted to call parent page method from child dialog page in xamarin forms

I am new to xamarin forms and trying to understand this for like three hours now.
I used the below code to open the dialog:
await NavigationService.NavigateAsync("DialogPopupManualConfigPage");
In 'DialogPopupManualConfigPage' dialog box, I have a to buttons, 'Complete' and 'Cancel'. When 'Complete' is clicked, I want to call a function which is located in the parent page that calls the dialog.
public DialogPopupManualConfigPage(INavigationService navigationService) : base(navigationService)
{
CurrentProgressPercent = string.Format("{0:F0}% complete", 0.00);
ConfigureCommand = new DelegateCommand(async () => await ConfigureAsync());
CloseCommand = new DelegateCommand(async () => await CloseAsync());
}
private async Task ConfigureAsync()
{
//call a method from parent viewModel
}
private async Task CloseAsync()
{
await NavigationService.GoBackAsync();
}
Any help will be appreciated. Thank You!
The easiest way is pass a viewModel reference to child page:
ChildPage:
myViewModel parentViewModel;
public DialogPopupManualConfigPage(myViewModel vm)
{
InitializeComponent();
parentViewModel = vm;
}
private async Task ConfigureAsync()
{
//call a method from parent viewModel
parentViewModel.test();
}
ParentPage:
public partial class MainPage : ContentPage
{
myViewModel currentViewModel;
public MainPage()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new DialogPopupManualConfigPage(currentViewModel));
}
}
public class myViewModel {
public void test() {}
}
Examples of using a messagingCenter:
ChildPage:
private async Task ConfigureAsync()
{
//call a method from parent viewModel
MessagingCenter.Send<Object>(new Object(), "Hi");
}
ParentPage:
myViewModel currentViewModel;
public MainPage()
{
InitializeComponent();
MessagingCenter.Subscribe<Object>(new Object(), "Hi", (sender) =>
{
// Do something whenever the "Hi" message is received
currentViewModel.test();
});
}
I found a solution like below and works for me at the time:
When calling the DialogPopup:
- create navParams and pass 'this' to represent the current PARENT caller page.
NavigationParameters navParams = new NavigationParameters
{
{ "parent", this },
};
await NavigationService.NavigateAsync("DialogPopupManualConfigPage", navParams);
When the DialogPopup page is Loading:
-- create the below global variable
<Caller_Parent_Page> _parent;
public DialogPopupManualConfigPageViewModel(INavigationService navigationService, <Caller_Parent_Page> parent) : base(navigationService)
{
//accept the passed input and assign it to the global variabl
_parent = parent;
}
Where I want to call the function:
- Simply check if global variable is not null
if(_parent != null)
{
//call any public function from the Parent Page
_parent.AreWeOnSameBranch()
}

Xamarin Forms MessagingCenter Subscribe called two times

I'm clicking on a product item in listview in product page viewmodel to show a popup(using rg.plugin popup) for selecting one of the product variants.After selecting variant,i am sending the selected variant to product page using messagingcenter from variant popup page viewmodel,subscribed in product page viewmodel constructor. working fine there.when i navigate to the previous page and then came back to this product page for adding one or more variant to the
same previously selected product,Messagingcenter subscribe called twice and product value increased twice.Tried to subscribe in the product page onappearing and unsubscribe in disappearing method.still calling two times? How to solve this issue?
calling popup:
var result = await dataService.Get_product_variant(store_id, product_id);
if (result.status == "success")
{
ind_vis = false;
OnPropertyChanged("ind_vis");
App.Current.Properties["product_variant_result"] = result;
App.Current.Properties["cartitems"] = purchaselist;
App.Current.Properties["selected_product"] = product_List2 ;
await PopupNavigation.Instance.PushAsync(new Popup_variant());
}
popup viewmodel: sending message
public Popup_variant_vm()
{
Radio_btn = new Command<Product_variant_list2>(Radio_stk_tapped);
product_variant_list = new List<Product_variant_list2>();
purchaselist = new ObservableCollection<Product_list2>();
show_variants();
}
internal void Confirm_variant()
{
if(App.Current.Properties.ContainsKey("selected_variant"))
{
MessagingCenter.Send<Popup_variant_vm, object>(this, "selected_variant", App.Current.Properties["selected_variant"]); //Message send from popup to product page
}
else
{
DependencyService.Get<IToast>().LongAlert("Please select any size");
}
}
product page viewmodel: subscribed here..called twice when navigating from previous page to this
public Store_page()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
var vm = new store_page_vm();
vm.Navigation = Navigation;
BindingContext = vm;
MessagingCenter.Unsubscribe<Popup_variant_vm, object>(this, "selected_variant");
MessagingCenter.Subscribe<Popup_variant_vm, object>(this, "selected_variant",async (sender, selected_variant) =>
{
var vm1 = BindingContext as store_page_vm;
vm1?.Addcart2(selected_variant);// called twice
});
}
unsubscribed in product cs page
protected override void OnDisappearing()
{
var vm = BindingContext as store_page_vm;
vm?.Save_cart();
MessagingCenter.Unsubscribe<Popup_variant_vm>(this, "selected_variant");
}
Your unsubscription should look something like below and it should work :
MessagingCenter.Unsubscribe<Popup_variant_vm, object>(this, "selected_variant");
https://stackoverflow.com/a/44753021/10937160
try this, and make sure you do not call Subscribe more than once.
My solution:
put unsubscribe sentence into subscribe body !!
MessagingCenter.Subscribe<object, string>(this, "IdSearch", (sender, arg) =>
{
listView.ItemsSource = arg;
MessagingCenter.Unsubscribe<object, string>(this, "IdSearch");
}, BindingContext);
I have created static counter variable in my app the in subscriber I have done this:
public static class Constants
{
public static int msgCenterSubscribeCounter { get; set; } = 0;
}
MessagingCenter.Subscribe<object, string>(this, "hello", (sender, arg) =>
{
Constants.msgCenterSubscribeCounter++;
if (arg.Equals("hello") && Constants.msgCenterSubscribeCounter == 1)
{
// handle your logic here
}
});
Reset counter in OnDisappearing() method from where you have called Send.
Changing Messagingcenter in to single subscription.
public class Messagingcenter_singleton
{
private static Messagingcenter_singleton _instance;
private bool isActivated = false;
private Action<string> callBackFun = null;
public static Messagingcenter_singleton Instance()
{
if (_instance == null)
{
_instance = new Messagingcenter_singleton();
}
return _instance;
}
public void setCallBack(Action<string> eventCallBack)
{
callBackFun = eventCallBack;
}
public void startSubscribe()
{
if (!isActivated)
{
isActivated = true;
MessagingCenter.Subscribe<string, string>(this, "Name", eventCallBack);
}
}
private void eventCallBack(string arg1, string arg2)
{
if (callBackFun != null)
{
InvokeMethod(new Action<string>(callBackFun), arg2);
}
}
public static object InvokeMethod(Delegate method, params object[] args)
{
return method.DynamicInvoke(args);
}
}
Use Below Code in you view model class
public void initSubscribe()
{
Messagingcenter_singleton.Instance().startSubscribe();
Messagingcenter_singleton.Instance().setCallBack(eventCallBack)
}
public void eventCallBack(string arg2)
{
// write your code here
}

Initialize an xamarin view after an async method

Good evening everyone.
For some time now I have been to Xamarin. My first tests are rather conclusive. I decided to try to make a small application that retrieves information in a database via an API and then update this data via a ListView.
When I launch the application on my emulator everything works but as soon as I install the application on my phone it crashes. I thought this was because the API but I have an API that I use to check the Login / password that works correctly.
The API that returns the data reviews a lot of line about 3500/4000, can this be the reason?
So I passed the loading of the data in my viewModel in an async method but the problem now is that the view loads before the data is loaded correctly. Is there a way to get the view initialized after the data is loaded?
Below my code.
Initializing my viewModel
class ManageInventViewModel
{
public ObservableCollection<InventViewModel> InventProduct { get; set; }
public Command<InventViewModel> UpdateCommand
{
get
{
return new Command<InventViewModel>(invent =>
{
var index = invent.IndexLigneInventaire;
InventProduct.Remove(invent);
InventProduct.Insert(index, invent);
});
}
}
public Command<InventViewModel> ResetStock
{
get
{
return new Command<InventViewModel>(invent =>
{
var index = InventProduct.IndexOf(invent);
InventProduct.Remove(invent);
invent.RealStockProduct = 0;
InventProduct.Insert(index, invent);
});
}
}
public ManageInventViewModel()
{
LoadInventaire();
}
private async void LoadInventaire()
{
var listMvt = await Utils.Utils.GetListMouvementUntilDate();
var listStock = Utils.Utils.GetStockByProduct(listMvt).Take(20);
InventProduct = new ObservableCollection<InventViewModel>();
var indexLine = 0;
foreach (var stock in listStock)
{
var inventViewModel = new InventViewModel
{
LibelleProduit = stock.LibelleProduit,
PrCodeProduit = stock.PrCodeProduit,
UpCodeProduit = stock.UpCodeProduit,
RealStockProduct = stock.StockTheoProdct,
StockTheoProdct = stock.StockTheoProdct,
IndexLigneInventaire = indexLine
};
++indexLine;
InventProduct.Add(inventViewModel);
}
}
}
Initializinz my view
public partial class InventPage : ContentPage
{
public InventPage()
{
InitializeComponent();
TableInvent.ItemSelected += (sender, e) =>
{
if (TableInvent.SelectedItem != null)
{
if (TableInvent.SelectedItem is InventViewModel item)
{
PopupNavigation.Instance.PushAsync(new ChangeStockModal(item, this));
}
TableInvent.SelectedItem = null;
}
};
}
private void Reset_Stock(object sender, EventArgs e)
{
var input = sender as Button;
var inventViewModel = input?.BindingContext as InventViewModel;
var listViewModel = BindingContext as ManageInventViewModel;
listViewModel?.ResetStock.Execute(inventViewModel);
}
public void Update_Stock_List(InventViewModel dataStockUpdate)
{
var listViewModel = BindingContext as ManageInventViewModel;
listViewModel?.UpdateCommand.Execute(dataStockUpdate);
PopupNavigation.Instance.PopAsync();
}
}
Thanks
I managed to create the ActivityIndicator but I can not get my data loaded while I'm displaying the wait screen.
Regarding this issue, I don't see you useActivityIndicator from your code,maybe you didn't update your code, I think if you use useActivityIndicator , You can bind one property to ActivityIndicator IsRunning and IsVisible, then you can solve your issue.
Related use ActivityIndicator step, you can take a look:
ActivityIndicator

How can I await modal form dismissal using Xamarin.Forms?

Using Xamarin.Forms how can I use make an async method that waits for the form to dismiss? If I use
await Navigation.PushModalAsync(page);
it will return once the animation is finished not when the page is dismissed.
I want a to create modal Task SignInAsync method that return true if sign-in is successful.
You can do this by triggering an event in your login page and listen for that event before going on, but you want the full TAP support and I second you there. Here's a simple yet working 2 page app that does just this. You'll obviously want to use ContentPage custom subclass and have proper methods instead of my quick Commands, but you get the idea, and it saves me typing.
public static Page GetFormsApp ()
{
NavigationPage navpage = null;
return navpage = new NavigationPage (new ContentPage {
Content = new Button {
Text = "Show Login dialog",
Command = new Command (async o => {
Debug.WriteLine ("Showing sign in dialog");
var result = await SignInAsync (navpage);
Debug.WriteLine (result);
})
}
});
}
static Task<bool> SignInAsync (NavigationPage navpage)
{
Random rnd = new Random ();
var tcs = new TaskCompletionSource<bool> ();
navpage.Navigation.PushModalAsync (new ContentPage {
Content = new Button {
Text = "Try login",
Command = new Command ( o => {
var result = rnd.Next (2) == 1;
navpage.Navigation.PopModalAsync ();
tcs.SetResult (result);
})
}
});
return tcs.Task;
}
The minor drawback is that the Task<bool> returns before the end of the pop modal animation, but that's:
easy to fix
only an issue if you're awaiting that result to push a new modal Page. Otherwise, meh, just go on.
Override OnAppearing
Firstly, it's worth noting that simply overriding OnAppearing in the calling Page may suffice in many circumstances.
protected override void OnAppearing()
{
base.OnAppearing();
...
// Handle any change here from returning from a Pushed Page
}
(note that the pushed page's OnDisappearing override is called after the caller's OnAppearing - seems a bit backwards to me!)
AwaitableContentPage
Secondly...this is my take on #Chad Bonthuys answer:
public class AwaitableContentPage : ContentPage
{
// Use this to wait on the page to be finished with/closed/dismissed
public Task PageClosedTask { get { return tcs.Task; } }
private TaskCompletionSource<bool> tcs { get; set; }
public AwaitableContentPage()
{
tcs = new System.Threading.Tasks.TaskCompletionSource<bool>();
}
// Either override OnDisappearing
protected override void OnDisappearing()
{
base.OnDisappearing();
tcs.SetResult(true);
}
// Or provide your own PopAsync function so that when you decide to leave the page explicitly the TaskCompletion is triggered
public async Task PopAwaitableAsync()
{
await Navigation.PopAsync();
tcs.SetResult(true);
}
}
And then call it thus:
SettingsPage sp = new SettingsPage();
await Navigation.PushAsync(sp);
await sp.PageClosedTask; // Wait here until the SettingsPage is dismissed
Just thought I would contribute to this one, although it's been a while since it was asked and answered. I built upon the answer by #noelicus. I wanted a generic way to do this with multiple situations so the Task needs to be able to return not just bool but anything. Then, using generics:
public class AwaitableContentPage<T> : ContentPage
{
// Use this to wait on the page to be finished with/closed/dismissed
public Task<T> PageClosedTask => tcs.Task;
// Children classes should simply set this to the value being returned and pop async()
protected T PageResult { get; set; }
private TaskCompletionSource<T> tcs { get; set; }
public AwaitableContentPage()
{
tcs = new TaskCompletionSource<T>();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
tcs.SetResult(PageResult);
}
}
Now, in the page you want to run as modal, you can do:
public partial class NewPerson : AwaitableContentPage<Person>
and when done, simply do:
base.PageResult = newPerson; // object you created previously
await base.Navigation.PopAsync();
Then, to make it super simple to use, use an extension method:
public static class ExtensionMethods
{
async public static Task<T> GetResultFromModalPage<T>(this INavigation nav, AwaitableContentPage<T> page)
{
await nav.PushAsync(page);
return await page.PageClosedTask;
}
That's all. Now, in your code, in any page where you want to use this, the syntax ends up simply like this:
Person newPerson = await Navigation.GetResultFromModalPage<string>(new NewPersonCreatePage());
if (newPerson != null)
UseNewPersonCreatedByOtherPage();
Hope this helps!
In my implementation I used:
await navigation.PopModalAsync();
Full Example:
private INavigation navigation;
public LoginPageModel(INavigation navigation, LoginPage loginPage)
{
this.navigation = navigation;
this.loginPage = loginPage;
}
public bool IsValid { get; set; }
protected async void ExecuteLoginCommand()
{
var loginResult = await AuthenticationHelper.Authenticate(Email, Password);
var isValid = false;
if (loginResult != null)
{
isValid = true;
}
//return isValid;
AuthenticationResult(isValid);
}
private async void AuthenticationResult(bool isValid)
{
if (isValid)
{
Debug.WriteLine("Logged in");
await navigation.PopModalAsync();
}
else
{
Debug.WriteLine("Failed" + email + password);
await loginPage.DisplayAlert("Authentication Failed", "Incorrect email and password combination","Ok", null);
}
}
The answer selected and given by #Stephane Delcroix above is awesome. But for anybody willing to push this further, by waiting for a page's completion and returning more structured data in a good MVVM fashion, you could do the following:
By calling an event from the page's OnDisapearing method, this event can then be subscribed by the navigation service which you create, and you can then use the "TaskCompletionSource" to wati until your page finishes its work and then complete the task.
For more details about accomplishing this, you can check this blog post.
Here is the base page's implementation, every page in this demo app inherit this page:
public class BasePage<T> : ContentPage
{
public event Action<T> PageDisapearing;
protected T _navigationResut;
public BasePage()
{
}
protected override void OnDisappearing()
{
PageDisapearing?.Invoke(_navigationResut);
if (PageDisapearing != null)
{
foreach (var #delegate in PageDisapearing.GetInvocationList())
{
PageDisapearing -= #delegate as Action<T>;
}
}
base.OnDisappearing();
}
}
Here is an overview of the navigation service you should use:
public async Task<T> NavigateToModal<T>(string modalName)
{
var source = new TaskCompletionSource<T>();
if (modalName == nameof(NewItemPage))
{
var page = new NewItemPage();
page.PageDisapearing += (result) =>
{
var res = (T)Convert.ChangeType(result, typeof(T));
source.SetResult(res);
};
await App.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page));
}
return await source.Task;
}
To call this page with the navigation service, you can use the following code:
var item = await new SimpleNavigationService().NavigateToModal<Item>(nameof(NewItemPage));
Items.Add(item);

What is the TPL equivalent of rx's Observable.FromEventPattern?

In rx you can write :
var oe = Observable.FromEventPattern<SqlNotificationEventArgs>(sqlDep, "OnChange");
and then subscribe to the observable to convert the OnChange event on the sqlDep object into an observable.
Similarily, how can you create a Task from a C# event using the Task Parallel Library ?
EDIT: clarification
The solution pointed by Drew and then written explicitely by user375487 works for a single event. As soon as the task finished ... well it is finished.
The observable event is able to trigger again at any time. It is can be seen as an observable stream. A kind of ISourceBlock in the TPL Dataflow. But in the doc http://msdn.microsoft.com/en-us/library/hh228603(v=vs.110).aspx there is no example of ISourceBlock.
I eventually found a forum post explaining how to do that: http://social.msdn.microsoft.com/Forums/en/tpldataflow/thread/a10c4cb6-868e-41c5-b8cf-d122b514db0e
public static ISourceBlock CreateSourceBlock(
Action,Action,Action,ISourceBlock> executor)
{
var bb = new BufferBlock();
executor(t => bb.Post(t), () => bb.Complete(), e => bb.Fault(e), bb);
return bb;
}
//Remark the async delegate which defers the subscription to the hot source.
var sourceBlock = CreateSourceBlock<SomeArgs>(async (post, complete, fault, bb) =>
{
var eventHandlerToSource = (s,args) => post(args);
publisher.OnEvent += eventHandlerToSource;
bb.Complete.ContinueWith(_ => publisher.OnEvent -= eventHandlerToSource);
});
I've not tryed the above code. There may be a mismatch between the async delegate and the definition of CreateSourceBlock.
There is no direct equivalent for the Event Asynchronous Pattern (EAP) baked into the TPL. What you need to do is using a TaskCompletionSource<T> that you signal yourself in the event handler. Check out this section on MSDN for an example of what that would look like which uses WebClient::DownloadStringAsync to demonstrate the pattern.
You can use TaskCompletionSource.
public static class TaskFromEvent
{
public static Task<TArgs> Create<TArgs>(object obj, string eventName)
where TArgs : EventArgs
{
var completionSource = new TaskCompletionSource<TArgs>();
EventHandler<TArgs> handler = null;
handler = new EventHandler<TArgs>((sender, args) =>
{
completionSource.SetResult(args);
obj.GetType().GetEvent(eventName).RemoveEventHandler(obj, handler);
});
obj.GetType().GetEvent(eventName).AddEventHandler(obj, handler);
return completionSource.Task;
}
}
Example usage:
public class Publisher
{
public event EventHandler<EventArgs> Event;
public void FireEvent()
{
if (this.Event != null)
Event(this, new EventArgs());
}
}
class Program
{
static void Main(string[] args)
{
Publisher publisher = new Publisher();
var task = TaskFromEvent.Create<EventArgs>(publisher, "Event").ContinueWith(e => Console.WriteLine("The event has fired."));
publisher.FireEvent();
Console.ReadKey();
}
}
EDIT Based on your clarification, here is an example of how to achieve your goal with TPL DataFlow.
public class EventSource
{
public static ISourceBlock<TArgs> Create<TArgs>(object obj, string eventName)
where TArgs : EventArgs
{
BufferBlock<TArgs> buffer = new BufferBlock<TArgs>();
EventHandler<TArgs> handler = null;
handler = new EventHandler<TArgs>((sender, args) =>
{
buffer.Post(args);
});
buffer.Completion.ContinueWith(c =>
{
Console.WriteLine("Unsubscribed from event");
obj.GetType().GetEvent(eventName).RemoveEventHandler(obj, handler);
});
obj.GetType().GetEvent(eventName).AddEventHandler(obj, handler);
return buffer;
}
}
public class Publisher
{
public event EventHandler<EventArgs> Event;
public void FireEvent()
{
if (this.Event != null)
Event(this, new EventArgs());
}
}
class Program
{
static void Main(string[] args)
{
var publisher = new Publisher();
var source = EventSource.Create<EventArgs>(publisher, "Event");
source.LinkTo(new ActionBlock<EventArgs>(e => Console.WriteLine("New event!")));
Console.WriteLine("Type 'q' to exit");
char key = (char)0;
while (true)
{
key = Console.ReadKey().KeyChar;
Console.WriteLine();
if (key == 'q') break;
publisher.FireEvent();
}
source.Complete();
Console.ReadKey();
}
}

Resources