in a prism application i have created a login form that start in a module. After the user is authenticated the login form wait for all application modules had been initialized (like a splah screen shows some information about the modules loaded), then starts the shell. All seems to work fine except when the user click on the cancel button of the login form. I would like that the application shutdown, but because the login form start in another thread i'm not able to use the Application.Current.Shutdown() (only work Enviroment.Exit(0) but i'm not sure this is a correct way). I try to invoke from Application.Current.Dispatcher but the application still run.
This is the login module initialization and where i catch the event that should do the application shutdown:
public void Initialize()
{
Dispatcher.CurrentDispatcher.BeginInvoke(
(Action) (() =>
{
Shell.Show();
EventAggregator.GetEvent<LoginStatusEvent>().Publish(new LoginStatusEvent { LoginStatus = LoginViewStatus.Close });
}));
ThreadStart showSplash =
() =>
{
Dispatcher.CurrentDispatcher.BeginInvoke(
(Action) (() =>
{
Container.RegisterType<LoginViewModel, LoginViewModel>();
Container.RegisterType<LoginView, LoginView>();
var login = Container.Resolve<LoginView>();
EventAggregator.GetEvent<LoginStatusEvent>().Subscribe(e => login.Dispatcher.BeginInvoke(
(Action)(() =>
{
if (e.LoginStatus == LoginViewStatus.Aborted)
{
login.Close();
Application.Current.Dispatcher.BeginInvoke((Action)(() => Application.Current.Shutdown()));
//Environment.Exit(0);
}
else if (e.LoginStatus == LoginViewStatus.Close)
{
login.Close();
}
else
{
WaitForCreation.Set();
}
})));
login.Show();
}));
Dispatcher.Run();
};
WaitForCreation = new AutoResetEvent(false);
var thread = new Thread(showSplash) {Name = "LoginView Thread", IsBackground = true};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
WaitForCreation.WaitOne();
}
any help is appreciated!
Why is your login UI running on the background thread? It seems the login UI should run on the GUI thread. Then you have a BackgroundWorker object (much easier to use than your code above and much more maintainable) that can run ProgressChanged anytime it needs to update the UI as the ProgressChanged method calls back to the GUI thread?
I often overload BackgroundWorker to be something like MyDataObjectBackgroundWorker, and I create a property that holds my object. This way I can pass data easily back and forth with ProgressChanged (like the information on your modules being loaded).
Related
I am working on a Chat Application using socket.io/ReactJS. I am facing an issue wherein I need to remove a particular user(String value - Just a username) from the backend(NodeJS/ExpressJS) on Browser window close and not page refresh. As I am using redux-persist(Session Storage) I want to persist the user state when the window is refreshed, but when the user closes the browser window the user will be removed from the backend and obviously from session storage too.
In my App.js file I have below code to register for an event :
removeUserBeforeUnload = () => {
if (window.performance.navigation.type !== 1)
// The socket request which removes the username from the backend
socket.emit("removeUser", this.props.currentUser);
};
setupBeforeUnloadListener = () => {
window.addEventListener("beforeunload", ev => {
ev.preventDefault();
return this.removeUserBeforeUnload();
});
};
componentDidMount() {
// Registering event
this.setupBeforeUnloadListener();
}
I tried using window.performance.navigation.type but that doesn't seem to work.
Thanks for any help.
I'm creating a chatboard and I'm using signalR to let users know when new comment is added on some status/post.
I'm using React and I've tried to add event listeners for the hub function in different components, in some components it works, not others.
This is the hub:
public class CommentHub : Hub
{
public void UpdateComments(int postId)
{
try
{
var context = GlobalHost.ConnectionManager.GetHubContext<CommentHub>();
context.Clients.All.updateNewComments(postId);
}
catch (Exception ex)
{
Log.Error(ex.Message);
}
}
}
I call the event listener in the componentDidMound function:
componentDidMount: function() {
this.commentUpdateListener();
},
And this is the event listener:
commentUpdateListener: function () {
console.log("in the comment update listener!");
var commentHub = $.connection.commentHub;
commentHub.client.updateNewComments = function (postId) {
console.log("updateNewComments called!");
};
$.connection.hub.start();
},
I have a react component for a post/status, a component for the "wall/newsfeed" and then I have a component for the "comment box" for each status on the wall, where the comments are rendered.
When I place the event listener in the wall component or the post component it works, and the 'updateNewComponent' function gets called, but not when I place it in the "comment box" component.
Still "in the comment update listener!" gets logged no matter where I place the event listener, but the hub function is not always called, and it seems to matter in what react component the event listener is placed.
Is the $.connection.commentHub not staying alive? Is it closing?
Is there some reason the function is not always being called?
You can enable logging before starting the hub connection like this :
$.connection.hub.logging = true;
$.connection.hub.start();
If you see some disconnections you can try to restart the connection when it closes :
$.connection.hub.disconnected(function() {
setTimeout(function() {
$.connection.hub.start();
}, 5000); // Restart connection after 5 seconds.
});
Further reading : Understanding and Handling Connection Lifetime Events in SignalR
I have an event that fires when the app is live and I receive an notification CurrentChannel_PushNotificationReceived. In this function I want to find out which page is currently displayed to know if the notification should update content on the page. The question is therefore twofold, how to know which page is currently displayed and interact with the toast notification.
Update
The issue is that I cannot interact with the elements because of clash with the OS threading (Dispatcher).
Therefore using the below code it allows me to access the content of the message. But I am still not able to get the info of the current_page
_channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
_channel.PushNotificationReceived += OnPushNotificationReceived;
private void OnPushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
{
switch (args.NotificationType)
{
case PushNotificationType.Badge:
this.OnBadgeNotificationReceived(args.BadgeNotification.Content.GetXml());
break;
case PushNotificationType.Tile:
this.OnTileNotificationReceived(args.TileNotification.Content.GetXml());
break;
case PushNotificationType.Toast:
this.OnToastNotificationReceived(args.ToastNotification.Content.GetXml());
break;
case PushNotificationType.Raw:
this.OnRawNotificationReceived(args.RawNotification.Content);
break;
}
args.Cancel = true;
}
private void OnBadgeNotificationReceived(string notificationContent)
{
// Code when a badge notification is received when app is running
}
private void OnTileNotificationReceived(string notificationContent)
{
// Code when a tile notification is received when app is running
}
private void OnToastNotificationReceived(string notificationContent)
{
// Code when a toast notification is received when app is running
// Show a toast notification programatically
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(notificationContent);
var toastNotification = new ToastNotification(xmlDocument);
//toastNotification.SuppressPopup = true;
ToastNotificationManager.CreateToastNotifier().Show(toastNotification);
}
private void OnRawNotificationReceived(string notificationContent)
{
// Code when a raw notification is received when app is running
}
Question
How do I access the current page information in the different onXXXXNotificationReceived. The current snippets work but not within these functions:
var currentPage = ((PhoneApplicationFrame)Application.Current.RootVisual).Content;
var tempBool = currentPage.GetType() is BC_Menu.StartUp.SecondScreen;
or
RootFrame.CurrentSource;
My guess is it is because of the UI-thread. So how can I use the dispatcher to get the information? I have tried some solutions with the dispatcher, but I cannot await the information, and therefore it is not applicable.
System.Windows.Threading.DispatcherOperation op = App.RootFrame.Dispatcher.BeginInvoke(new Func<Uri>(() =>
{
return RootFrame.CurrentSource;
})
);
await op; //Not awaitable.
There's no reason to await the dispatcher to the UI thread. Simply dispatch to the UI thread and then perform the rest of your logic, like displaying the toast or navigating to a page, from within the UI thread...
Register the event...
var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
channel.PushNotificationReceived += Channel_PushNotificationReceived;
On the event handler, cancel displaying the notification and then dispatch to UI thread...
private void Channel_PushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
{
// Make sure you cancel displaying the toast on this thread (not on UI thread)
// since cancellation needs to be set before this thread/method returns
args.Cancel = true;
// Then dispatch to the UI thread
App.RootFrame.Dispatcher.BeginInvoke(delegate
{
var currPage = ((PhoneApplicationFrame)Application.Current.RootVisual).Content;
switch (args.NotificationType)
{
case PushNotificationType.Toast:
// TODO
break;
}
});
}
Do all of your code inside the dispatcher's delegate. All your code will be executing on the UI thread... you'll be able to navigate pages, obtain current page, etc.
Ok. Try this. Create a static property on App.xaml.cs.
public static object CurrentPageInfo { get; set; }
And assign the page type or page name to the property on 'OnNavigatedTo' method on every page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var currentPage = ((PhoneApplicationFrame)Application.Current.RootVisual).Content;
App.CurrentPageInfo = currentPage.GetType() is BC_Menu.StartUp.SecondScreen;
}
So that you can identify the page source type on receiving notifications by accessing the App.CurrentPageInfo property. Hope it helps!
I am implementing functionality to notify the user of long running job completions using SignalR in an AngularJS application.I have created groups of user based on their name,so for each user a group of his name and different connectionids which he has opened up will be created and he would be notified by his group. I want to notify the user on two pages i.e. landing Page and Job Run Page as even if the user is on landing page and job run completes he should be notified of it.
For the same reason i am creating group by his name on both the pages,so that if he is on any page he would be nofied through the group.
On landing page controller js file i have written code to add the user in group as follow...
$rootScope.signalRHub = $.connection.signalRHub;
$rootScope.hubStart = null;
$rootScope.startHub = function () {
if ($rootScope.hubStart == null)
{
$rootScope.hubStart = $.connection.hub.start();
}
return $rootScope.hubStart;
}
$scope.$on('$locationChangeStart', function (event) {
if ($rootScope.userName != "") {
$rootScope.signalRHub.server.leaveGroup($rootScope.userName);
}
});
// Start the connection
$rootScope.startHub().done(function () {
$rootScope.signalRHub.server.joinGroup($rootScope.userName);
});
on Job Run controller js file i have written following code....
$rootScope.signalRHub.client.showNotification = function (message) {
notify('Your notification message');//notify is the angular js directive injected in this controller which runs fine
};
$scope.$on('$locationChangeStart', function (event) {
$rootScope.signalRHub.server.leaveGroup($rootScope.studyid);
});
// Start the connection
$rootScope.startHub().done(function () {
$rootScope.signalRHub.server.joinGroup($rootScope.userName
});
My Hub File.....
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class SignalRHub : Hub
{
public Task JoinGroup(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
}
public Task LeaveGroup(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
public void ShowNotification(string jobRunDetailId, string userName)
{
if (!string.IsNullOrEmpty(userName))
{
var context = GlobalHost.ConnectionManager.GetHubContext<SignalRHub>();
context.Clients.Group(userName).showNotification(jobRunDetailId);
}
}
}
The issue is when i run the application the group add functionality for both pages works fine.but when i call "showNotification" from Hub it doesn't show any message.
But strange thing is if i comment the "$rootScope.startHub().done...." function on landing page then the jobrun page notify functionality works fine.I am not sure if writing "$rootScope.startHub().done()..." on two places is creating this problem.please help.
You need to wire up all callbacks before calling start. If you turn client side logging on, it'll tell you what hubs you are subscribed to.
Aside:
[EnableCors] is a webapi specific attribute that does not work in SignalR.
In a view-model I use a factory:
private async Task<BaseData> InitializeAsync()
{
await InstancesAsync();
await ProjectsAsync();
await AdminAsync();
return this;
}
public static async Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return await ret.InitializeAsync();
}
The awaited methods are rather staightforward, with e.g.
var instances = await TaskEx.Run(new Func<List<string>>(() => Agent.GetInstances()));
In the wpf view I want to set the DataContext in the constructor:
Loaded += delegate
{
Dispatcher.Invoke(new Action(async () => { DataContext = await BasisGegevens.CreateAsync(); }));
};
Although it works, I feel rather uncomfortable because the UI thread is used everywhere, also after the callbacks when the awaits complete. What am I missing?
Also I don't understand how to use the factory pattern for the DataContext because without the Invoke above I get the error that a different thread owns the object.
EDIT: using the ideas of Mr. Cleary I get:
Loaded += async (object sender, RoutedEventArgs e) =>
{ DataContext = await BaseData.CreateAsync(); };
public static Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return ret.InitializeAsync();
}
private async Task<BaseData> InitializeAsync()
{
// UI thread here
await InstancesAsync().ConfigureAwait(false);
// thread 'a' here
await ProjectsAsync().ConfigureAwait(false);
// thread 'a' sometimes 'b' here
await AdminAsync().ConfigureAwait(false);
// thread 'a' or 'b' here
return this;
}
This works fine, except I cannot understand how ConfigureAwait(false) works.
Inside the method InstancesAsync() I have the awaited task:
var instances = await TaskEx.Run(new Func<List<string>>(() => Agent.GetInstances()));
After awaiting the repsonse, I return in the UI thread - I never expected that to happen!
Note that ProjectsAsync() and AdminAsync() behave the same, although they start on a worker (or background) thread!
I thougth that ConfigureAwait(true) has the effect of returning in the calling thread (in my case UI thread). I tested that and it is so.
Why do I see this with ConfigureAwait(false) too: because of a nested await, see comments.
I find it most useful to treat the ViewModel as having UI thread affinity. Think of it as the logical UI, even if it's not the actual UI. So all property and observable collection updates on ViewModel classes should be done on the UI thread.
In your async methods, if you don't need to return to the UI thread, then you can use ConfigureAwait(false) to avoid resuming on the UI thread. For example, if your various initialization methods are independent, you could do something like this:
private async Task<BaseData> InitializeAsync()
{
// Start all methods on the UI thread.
var instancesTask = InstancesAsync();
var projectsTask = ProjectsAsync();
var adminTask = AdminAsync();
// Await for them all to complete, and resume this method on a background thread.
await Task.WhenAll(instancesTask, projectsTask, adminTask).ConfigureAwait(false);
return this;
}
Also, any time you have return await, take another look to see if you can just avoid async/await entirely:
public static Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return ret.InitializeAsync();
}
Finally, you should strongly avoid Dispatcher. Your Loaded event could be simplified to this:
Loaded += async ()
{
DataContext = await BasisGegevens.CreateAsync();
};