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();
});
}
Related
I have a user control which has a button and a dependency property for the action the button is to execute. The page which contains the control sets the action in XAML.
MyUserControl.cs
A Button, and dependency property ButtonAction, of type Action. When the button is clicked it executes the ButtonAction.
MainPage.xaml.cs
Action Action1
Action Action2
MainPage.xaml
Present an instance of MyUserControl, with ButtonAction=Action1
The problem: The ButtonAction property is not assigned from the XAML
MyUserControl.cs
public sealed partial class MyUserControl : UserControl
{
public Action ButtonAction {
get { return (Action)GetValue(ButtonActionProperty); }
set { SetValue(ButtonActionProperty, value); }
}
public static readonly DependencyProperty ButtonActionProperty =
DependencyProperty.Register("ButtonAction", typeof(Action), typeof(MyUserControl), new PropertyMetadata(null,ButtonAction_PropertyChanged));
private static void ButtonAction_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
Debug.WriteLine("ButtonAction_PropertyChanged");
// Is not called!
}
public MyUserControl() {
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e) {
if (ButtonAction != null) {
// Never reaches here!
ButtonAction();
}
}
}
MyUserControl.xaml
<Grid>
<Button Click="Button_Click">Do The Attached Action!</Button>
</Grid>
MainPage.xaml.cs
Action Action1 = (
() => { Debug.WriteLine("Action1 called"); });
Action Action2 = (() => { Debug.WriteLine("Action2 called"); });
MainPage.xaml
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:MyUserControl x:Name="myUserControl" ButtonAction="{Binding Action1}"/>
</Grid>
It does work if in the code-behind for MainPage (MainPage.xaml.cs) I assign the action in the Loaded event.
private void Page_Loaded(object sender, RoutedEventArgs e) {
this.myUserControl.ButtonAction = Action1;
}
In this case the PropertyChanged callback in the user control is also called. (This handler is provided only for diagnostic purposes. I can't see how it can be used to support the property in practice).
The issue is in your data binding. The Action1 in ButtonAction="{Binding Action1}" should be a public property while you defined it as a private variable.
Also, you cannot just declare a normal property directly in the code behind like that. You will need either a dependency property, or more commonly, a public property inside a viewmodel which implements INotifyPropertyChanged.
If we go with the second approach, we will need to create a viewmodel class like the following with an Action1 property. Note the OnPropertyChanged stuff is just the standard way of implementing INotifyPropertyChanged.
public class ViewModel : INotifyPropertyChanged
{
private Action _action1;
public Action Action1
{
get { return _action1; }
set
{
_action1 = value;
OnPropertyChanged("Action1");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
And then you just need to assign this to the DataContext of your main page.
public MainPage()
{
this.InitializeComponent();
var vm = new ViewModel();
vm.Action1 = (() =>
{
Debug.WriteLine("Action1 called");
});
this.DataContext = vm;
}
With these two changes, your ButtonAction callback should be firing now. :)
How can I make the background agent execution to wait before all ImageOpened() events are fired (3 in this case) in order to update secondary live tile with custom images?
Edit 1:
In the OnInvoke() method of the ScheduledAgent I am calling my own create tile data function implemented in a shared library which in turn subscribes to 3 ImageOpened() events as I am trying to create custom images for all live tile templates i.e. small, medium and wide.
Since these being asynchronous events I have no way to check if all the events have completed successfully so that I can call NotifyComplete() to notify the background agent that its job is now done. So sometimes the tile gets updated while most of the times it doesn't. Also I am using the same function to update the live tiles every time the app is launched so there is no problem with its implementation. I have also tried to take care of all the memory limitations with the ScheduledAgent by disposing Bitmaps and calling GC.Collect() forcefully.
Please help in any possible way to fix this problem.
Add a new class that lets you create custom events -
public class SaveImageCompleteEventArgs : EventArgs
{
public bool Success { get; set; }
public Exception Exception { get; set; }
public string ImageFileName { get; set; }
public SaveImageCompleteEventArgs(bool success, string fileName)
{
Success = success;
ImageFileName = fileName;
}
}
Initialize the events and required variables in the file you are updating the custom live tile from -
public static int countTile = 3;
public event EventHandler<SaveImageCompleteEventArgs> SaveMediumImageComplete;
public event EventHandler<SaveImageCompleteEventArgs> SaveWideImageComplete;
public event EventHandler<SaveImageCompleteEventArgs> SaveSmallImageComplete;
public event EventHandler<SaveImageCompleteEventArgs> SaveAllImagesComplete;
Fire the completion event in the ImageOpened() event handlers for all the tiles and check if the SaveAllImagesComplete event needs to be fired-
public void OnBackgroundBmpOpenedMedium(object sender, RoutedEventArgs e)
{
if (SaveMediumImageComplete != null)
{
countTile -= 1;
CheckIfAllImagesOpened();
SaveMediumImageComplete(this, new SaveImageCompleteEventArgs(true, mediumTileImageUriIronMan));
}
}
private void CheckIfAllImagesOpened()
{
if (countTile == 0)
{
if (SaveAllImagesComplete != null)
{
var args1 = new SaveImageCompleteEventArgs(true, "");
SaveAllImagesComplete(this, args1);
}
}
}
In the ScheduledAgent file -
public static ManualResetEvent evt;
public bool IsPaused { get { return !evt.WaitOne(0); } }
In the OnInvoke() function -
evt = new ManualResetEvent(false);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
//Initialize secondary tile here
if (secondaryTile != null)
{
/*
obj is a object of a helper file that contains all the
functions responsible for updating the custom live tile
Call the function that is responsible for initializing all the
tile image bitmpas and that subscribes to the ImageOpened events
*/
obj.SaveMediumImageComplete += async (s, args) =>
{
if (!IsPaused)
evt.Set();
};
obj.SaveWideImageComplete += async (s, args) =>
{
if (!IsPaused)
evt.Set();
};
obj.SaveSmallImageComplete += async (s, args) =>
{
if (!IsPaused)
evt.Set();
};
obj.SaveAllImagesComplete += async (s, args) =>
{
try
{
if (args.Success)
obj.UpdateTileIcon();
}
catch (Exception) { }
finally
{
if (!IsPaused)
evt.Set();
}
};
}
});
evt.WaitOne();
NotifyComplete();
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/
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 made my own slider as user control with some custom properties and one custom event. Everything works fine, but recently I start using Caliburn Micro, and I don't know how to capture my custom event.
Previously I used:
<my:RadialSlider x:Name="slider" WedgeAngle="270" ..... AngleChanged="slider_AngleChanged" />
and
public void slider_AngleChanged(object sender, ValueChangedEventArgs e)
{
.... something ....
}
Now, in Caliburn project I tried:
<my:RadialSlider x:Name="slider" WedgeAngle="270" ..... cal:Message.Attach="[Event AngleChanged] = [Action slider_AngleChanged($eventArgs)]" />
and in my ViewModel:
public void slider_AngleChanged(object sender, ValueChangedEventArgs e)
{
.... something ....
}
But, this doesn't work...
So, how to capture this event?
Slider UC code-behind:
public delegate void AngleChangedEventHandler(object sender, ValueChangedEventArgs e);
public sealed partial class RadialSlider : UserControl
{
public event AngleChangedEventHandler AngleChanged;
private void OnAngleChanged(ValueChangedEventArgs e)
{
if (AngleChanged != null)
AngleChanged(this, e);
}
public static readonly DependencyProperty WedgeAngleProperty = DependencyProperty.Register("WedgeAngle", typeof(double), typeof(RadialSlider), new PropertyMetadata((double)270, new PropertyChangedCallback(OnPropertyWedgeAngleChanged)));
public double WedgeAngle
{
get { return (double)this.GetValue(WedgeAngleProperty); }
set { this.SetValue(WedgeAngleProperty, value); }
}
private static void OnPropertyWedgeAngleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
(sender as RadialSlider).UpdateControls();
if (e.NewValue != e.OldValue)
{
(sender as RadialSlider).OnAngleChanged(new ValueChangedEventArgs((double)e.OldValue, (double)e.NewValue));
}
}
}
You need to use a routed event. This has to do with how events bubble up the visual tree and how Caliburn.Micro attaches itself to them. Standard events should be avoided on controls or UI widgets in any tech using Xaml as the loose out on some pretty funky features (bubble up / down).