I am learning Swing and created a sample GUI. I am trying to achieve the following in exact order...
The user enters text into some text fields.
The user clicks a "launch" button.
The "launch" button becomes disabled.
A background thread spawns and processes the text from text fields.
the background thread finishes.
the "launch" button becomes enabled again.
I am trying to use invokeandwait as can be seen below but I get "Cannot call invokeAndWait from the event dispatcher thread". My main method is in the same .class file and I'm not too sure what exactly the "event dispatcher thread" is. Whats the best approach for something like this, do I need to setup some kind of alert in my worker thread to route back to the "event dispatcher thread"?
LaunchButton code
private void launchButtonActionPerformed(java.awt.event.ActionEvent evt) {
launchButton.setEnabled(false);
try {
java.awt.EventQueue.invokeAndWait(new MyTestThread());
} catch (Exception e) {
}
launchButton.setEnabled(true);
}
Worker Thread
public class MyTestThread extends Thread {
private int i = 0;
public void run() {
while (i < 5) {
try {
System.out.println(i++);
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Solution
Worker Thread
public class WorkerThread extends SwingWorker<Integer[], Void> {
#Override
public Integer[] doInBackground() {
System.out.println("Doing in background");
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
System.out.println("Doing in background" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
#Override
public void done() {
System.out.println("Swingworker is Done");
}
}
Starting the worker thread from my Event Dispatch Thread (EDT)
new WorkerThread().execute();
The event dispatch thread (EDT) is the only thread allowed to access Swing classes and (almost all) methods. All the initizalization and events of the GUI are executed by the EDT. To execute something in the EDT you have to use SwingUtilities.invokeLater or invokeAndWait (this is equivalent to EventQueue.invokeXXX). This is why all Swing programs start with SwingUtilities.invokeLater() in the main: to execute the GUI initialization in the EDT.
While the EDT is busy the UI freezes that's why background threads are useful. When you need to do a big amount of work independent from the UI (calculations, I/O, transmission, ...) you have to use "worker threads".
For more about threading in Swing see this tutorial: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
Now, what you are trying to accomplish is right but the tools you are using aren't.
You need to know two things: how to handle events (like button presses) and how to create background threads.
For the first one see this tutorial:
http://docs.oracle.com/javase/tutorial/uiswing/components/button.html
You just need to add anActionListener to a button and whenever the button throws an event the listener's actionPerformed method will be executed, in the EDT.
Minimal example:
JButton button = new JButton("Test button");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.out.println("The button was pressed.");
}
}
The event variable contains useful information, for example event.getSource() return the object that threw the event, in this case the button.
Now what you want to do when the button is pressed is create a worker thread. Worker threads are created using the SwingWorker class, as you've seen in the concurrency tutorial. There you can define a piece of code that will be executed in the background thread (in the doInBackground() method) and a piece of code that will be executed in the EDT after the work in the background is done (in the done() method).
So you'd want to do something like this:
private static JButton _button;
//...
_button = new JButton("Test button");
_button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.out.println("The button was pressed.");
_button.setEnabled(false);
SwingWorker worker = new SwingWorker()
{
#Override
protected Object doInBackground() throws Exception
{
//do something useful in the background thread
return null;
}
#Override
protected void done()
{
_button.setEnabled(true);
}
}
worker.execute();
}
}
There's a lot of information out there about this. Read the Java reference for the classes, read the official tutorials and search in SO. Another good tutorial about SwingWorkers is: http://www.javacreed.com/swing-worker-example/
Related
I'm testing a JavaFX application with JUnit, In most cases I use the #Rule approach from Basic JUnit test for JavaFX 8. However there are a couple of situations where this approach does not work, so I setup the JavaFX platform manually, and call Platform.runLater() where necessary.
What appears to be happening is that at some point the JavaFX application thread is disappearing, this means subsequent tests lockup as the Platform.runLater() calls never return - that's why I've added timeouts in example. I've proved this with calls to Thread.getAllStackTraces()
Yes I'm aware of JemmyFX, and I'll likely move to it soon, but I'd still like to understand what's going on here...
Code
public class JavaFxThreadJUnit {
private static boolean setup;
private Stage stage;
#Before
public void before() throws Exception {
setupJavaFX();
Platform.setImplicitExit(true);
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
stage = new Stage();
stage.show();
latch.countDown();
});
latch.await(5, TimeUnit.SECONDS);
}
#After
public void after() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
stage.hide();
latch.countDown();
});
latch.await(5, TimeUnit.SECONDS);
}
#Test
public void foo() throws Exception {
// test stuff...
System.out.println("foo test: "
+ Thread.getAllStackTraces().keySet().stream().map(Thread::getName).collect(Collectors.toList()));
}
#Test
public void bar() throws Exception {
// test stuff...
System.out.println("bar test: "
+ Thread.getAllStackTraces().keySet().stream().map(Thread::getName).collect(Collectors.toList()));
}
// https://gist.github.com/andytill/3835914
public static void setupJavaFX() throws InterruptedException {
if (setup) {
return;
}
long timeMillis = System.currentTimeMillis();
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// initializes JavaFX environment
new JFXPanel();
latch.countDown();
}
});
System.out.println("javafx initialising...");
latch.await();
System.out.println("javafx is initialised in " + (System.currentTimeMillis() - timeMillis) + "ms");
setup = true;
}
}
Output... JavaFX Application Thread was there, then it's gone...
javafx initialising...
javafx is initialised in 327ms
bar test: [Thread-3, ReaderThread, AWT-Shutdown, AWT-Windows, Thread-2, Finalizer, JavaFX Application Thread, Signal Dispatcher, Java2D Disposer, AWT-EventQueue-0, main, Attach Listener, Reference Handler, QuantumRenderer-0]
foo test: [Thread-3, ReaderThread, Java2D Disposer, AWT-Windows, Thread-2, main, Finalizer, Attach Listener, Reference Handler, Signal Dispatcher]
Looks like the stage closing is triggering an "implicit exit". I'd still be interested to know why this doesn't also affect tests using the #Rule approach...
Workaround:
Platform.setImplicitExit(false)
what would be the steps to add timer to change selected item's image in listpicker. Any suggestions? FYI, have never used ListPicker before. So i am finding it kind of hard to understand where to start and what to do.
You will need an ObservableCollection of your ImageSources and a DispatcherTimer to fire the events every TimeSpan of your choosing.
Here's some code to help you get started. You can modify it to do exactly what you want. It basically contains a ListPicker that has a collection of images as its ItemTemplate. Every one second the DispatchTimer fires and switches the selectedItem's Image between the 2 default images that are created in about every single WP8.0 application.
Make it a habit to use ObervableCollection when you want to display something to the user instead of a List, it will make your WP8 development life a lot easier.
XAML
<toolkit:ListPicker x:Name="my_listpicker" SelectionChanged="my_listpicker_SelectionChanged_1" Background="Black">
<toolkit:ListPicker.HeaderTemplate>
<DataTemplate/>
</toolkit:ListPicker.HeaderTemplate>
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel Background="Black">
<Image Source="{Binding ImageSource}" Height="200"></Image>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>
C# Namespaces
using System.ComponentModel; // ObservableCollection
using System.Collections.ObjectModel; // INotifyPropertyChanged
using System.Windows.Threading; // Dispatch Timer
C# Model of your Images (pretty basic, but pay attention to the INotifyPropertyChanged
public class MyBindingImage : INotifyPropertyChanged
{
public MyBindingImage() { }
public MyBindingImage(string source)
{
this.ImageSource = source;
}
// Create the OnPropertyChanged method to raise the event
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
string image_source;
public String ImageSource {
get { return image_source; }
set
{
image_source = value;
OnPropertyChanged("ImageSource");
}
}
}
C# (Create the Timer and ObservableCollection and Set the ItemSource)
DispatcherTimer timer;
// Constructor
public MainPage()
{
// create our dispatch timer
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(2000);
timer.Tick += OnTimerTick;
InitializeComponent();
// create our list picker elements
ObservableCollection<MyBindingImage> my_image_list = new ObservableCollection<MyBindingImage>();
my_image_list.Add(new MyBindingImage("Assets/ApplicationIcon.png"));
my_image_list.Add(new MyBindingImage("Assets/AlignmentGrid.png"));
my_listpicker.ItemsSource = my_image_list;
}
C# Events (For the Timer & ListPicker SelectionChange)
// each time the selection has changd: stop the timer, then start it again
private void my_listpicker_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
if (timer != null)
{
timer.Stop();
timer.Start();
}
}
// if the timer is on, cycle the images of the selected item
private void OnTimerTick(object sender, EventArgs e)
{
try
{
MyBindingImage item = (MyBindingImage) my_listpicker.SelectedItem;
// cycle the selected image between to different images
if (item.ImageSource == "Assets/AlignmentGrid.png")
{
item.ImageSource = "Assets/ApplicationIcon.png";
}
else
{
item.ImageSource = "Assets/AlignmentGrid.png";
}
}
catch (Exception ex)
{
string error_message = ex.Message;
}
}
[APPLICATION SCREENSHOT]
I want to update a textblock whenever there is a change in battery percent. I found the event RemainingChargePercentChanged in the Windows.Phone.Devices.Power.Battery namespace. But whenever the eventhandler is called and i try to update the textblock, i struck with error.
the error is:
An exception of type System.UnauthorizedAccessException occurred in System.Windows.ni.dll but was not handled in user code.
Do I want to select any capabilities in AppManifest file??.. If so, what?
Any help will be appreciated.
Following is my code,
public partial class MainPage : PhoneApplicationPage
{
private readonly Battery _battery;
// Constructor
public MainPage()
{
InitializeComponent();
_battery = Battery.GetDefault();
_battery.RemainingChargePercentChanged += OnRemainingChargePercentChanged;
UpdateUI();
}
private void OnRemainingChargePercentChanged(object sender, object e)
{
UpdateUI();
}
private void UpdateUI()
{
sampletext.Text = string.Format("{0} %", _battery.RemainingChargePercent);
}
}
The problem is that the event handler is called on another thread, if you read the exception message it will say Invalid cross-thread access.
The solution is to change the Text property on the UI thread using the Dispatcher, like this:
Dispatcher.BeginInvoke(() => {
sampletext.Text = string.Format("{0} %", _battery.RemainingChargePercent);
});
Edit: or your whole UpdateUI function call:
private void OnRemainingChargePercentChanged(object sender, object e)
{
Dispatcher.BeginInvoke(() => {
UpdateUI();
});
}
I ran into bizarre problem, hopefully you will be able to give me a hand. I start PhotoChooserTask, choose photo and when it comes back it suddenly throws an exception. Picture below depicts the situation. If I delete code connected with State or change writing object to string it works as expected. I try to find a solution why this error occurs. I'm receiving an error "rootframe_navigation_failed" and "An unhandled exception of type 'System.Runtime.Serialization.InvalidDataContractException' occurred in Microsoft.Phone.Interop.ni.dll". When PhotoChoser is invoked onNavigatedFrom is launched and then it stops, but why? Image assigning seems to be correct.
Unfortunately that has nothing in common with Image. I made simple class as I could and error still takes place but when I get rid of photochooser task and for instance put into gallery method navigation to another page there is no problem so I presume that problem results from photochoosertask. But why this happens, still trying to find out.
public partial class Page1 : PhoneApplicationPage
{
private PhotoChooserTask photo_chooser;
public Page1()
{
InitializeComponent();
}
private void gallery(object sender, RoutedEventArgs e)
{
photo_chooser = new PhotoChooserTask();
photo_chooser.Show();
photo_chooser.Completed += photo_chooser_Completed;
}
void photo_chooser_Completed(object sender, PhotoResult e)
{
switch (e.TaskResult)
{
case TaskResult.OK:
{
break;
}
}
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
PhoneApplicationService.Current.State["sth"] = new Rect();
base.OnNavigatingFrom(e);
}
}
I am working on a Windows Phone 8 App which should be protected with a passcode. What is the best way to show the passcode screen everytime the app is lauchend or activated?
I think the central point of action shoule be the App.xaml.cs with its Launch and Activation event handlers. But how exactly can I show the passcode screen?
The problem is, that one never know which pages will be displayed when the app launches or is reactivated. It is either the main page or any other page which was last displayed when the app was deactivated.
I tried to intercept the navigation to the first page, cancel it and show the passcode page instead:
// App.xaml.cs
private void InitializePhoneApplication() {
...
RootFrame.Navigating += HandleFirstNavigation;
...
}
private void HandleFirstNavigation(object sender, NavigatingCancelEventArgs e) {
RootFrame.Navigating -= HandleFirstNavigation;
e.Cancel = true;
RootFrame.Dispatcher.BeginInvoke(new Action(this.OpenPasscodePage));
}
private void OpenPasscodePage() {
RootFrame.Navigate(PasscodePageUri);
}
This works, but only when the app lauchend. When the app reactivated (dormant or tombstoned) the e.Cancel is irgnored. Although the navigation to the passcode page is called the original page is shown.
Moving the navigation the the passcode page from Navigating to Navigated does not worth either:
private void InitializePhoneApplication() {
...
RootFrame.Navigated += PasscodePageAfterFirstNavigation;
...
}
private void PasscodePageAfterFirstNavigation(object sender, EventArgs e) {
RootFrame.Navigated-= PasscodePageAfterFirstNavigation;
RootFrame.Navigate(PasscodePageUri);
}
This seems to be some kind of race condition: Sometimes the passcode page is shown, sometimes the original page. Even if the passcode pages comes up this looks bad because one first see the original page for the fraction of a second before the app navigates further to the passcode page.
Both solution do not work. Any idea what is the right way to implement this?
EDIT: Meanwhile I tried a third solution which does not work either. This solution uses the Uri Mapper:
App.xaml.cs
public bool PasscodeWasConfirmed; private void Application_Launching(object sender, LaunchingEventArgs e) {
...
PasscodeWasConfirmed = false;
...
}
private void Application_Activated(object sender, ActivatedEventArgs e) {
...
PasscodeWasConfirmed = false;
...
}
public Uri InitialPageUri;
public bool ShouldRedirectToPasscodePage(Uri uri) {
if (PasswordWasConfirmend == false) {
InitialPageUri = uri;
return true;
}
return false;
}
UriMapper
public class AppUriMapper : UriMapperBase {
public override Uri MapUri(Uri uri) {
App app = (Application.Current as App);
if (app != null) {
if (app.ShouldRedirectToPasscodePage(uri))
return PasscodeQueryPage.PageUri;
}
// default
return uri;
}
}
PasscodePage
public partial class PasscodePage : PhoneApplicationPage {
...
private void PasscodeConfirmed() {
App app = (Application.Current as App);
app.PasscodeWasConfirmed = true;
NavigationService.Navigate(app.InitialPageUri);
}
}
The Logic is working without any problem, but the app does not navigate to InitialPageUri after the passcode was confirmed. The Uri Mapper is called and correctly and returns the InitialPageUri (no redirect any more). But no navigation happens...
There are no errors, exceptions or debug output. simply nothing happes...
Biggest problem when using Uri Mapper:
When the app is reactivated from Dormant state there is no navigation which could be mapped or redirected...
(I've edited previous answer instead of adding a new one)
I've spend a little time trying to find a solution, and I don't see why your code doesn't run.
In my case it's enough if I do such a change in App.xaml:
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
RootVisual = RootFrame;
// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
App.RootFrame.Navigate(new Uri("/passPage.xaml", UriKind.RelativeOrAbsolute));
}
This works on my example which is under the link http://sdrv.ms/1ajH40E
But - I cannot prevent user from seeing last screen when he holds back buton and is chosing to which app return, and then for a blink he can see the last page before leaving the app. I don't know if it is possible to change this behaviour after clicking MS Button:
windows phone change deactivated app image
Second edit
Ok - maybe I've found solution why it sometiems work and sometimes not in your code. After pressing the Start or Search buton the App can go to two cases: Tombstone and non-tombsone. After return different events happen. Code above works with Tombstone case but not with non-tombstone. To work it with the second you need to add (because page is not initialized again) - (of course it can be different solution):
bool afterActivation = false;
private void Application_Activated(object sender, ActivatedEventArgs e)
{
afterActivation = true;
}
private void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
// If the app has received a 'reset' navigation, then we need to check
// on the next navigation to see if the page stack should be reset
if (e.NavigationMode == NavigationMode.Reset)
RootFrame.Navigated += ClearBackStackAfterReset;
if (afterActivation)
{
afterActivation = false;
App.RootFrame.Navigate(new Uri("/passPage.xaml", UriKind.RelativeOrAbsolute));
}
}
Please also ensure of your debug properties in VS: Project->Properties->Debug->Tombstone upon deactiovation checkbox.
You can also find some information here (if you haven't seen it before):
http://blogs.msdn.com/b/ptorr/archive/2010/12/11/how-to-correctly-handle-application-deactivation-and-reactivation.aspx