I am trying to write an application, but it is constantly crashing when using the uiimagepickercontroller. I thought that it might be because I was not disposing of the picker after each use, but it will often freeze up on first run as well. Usually I'll take a picture and it just freezes, never asking to "use" the picture.
Do you have any suggestions? Here is my code. Has anyone gotten this to work?
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
myPicker = new UIImagePickerController();
myPicker.Delegate = new myPickerDelegate(this);
myAlbumButton.Clicked += delegate {
if(UIImagePickerController.IsSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary)){
myPicker.SourceType = UIImagePickerControllerSourceType.PhotoLibrary;
myPicker.AllowsEditing = true;
this.PresentModalViewController (myPicker, true);
}else{
Console.WriteLine("cannot get album");
}
};
myCameraButton.Clicked += delegate {
if(UIImagePickerController.IsSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){
myPicker.SourceType = UIImagePickerControllerSourceType.Camera;
//myPicker.AllowsEditing = true;
this.PresentModalViewController (myPicker, true);
}else{
Console.WriteLine("cannot get camera");
}
};
}
private class myPickerDelegate : UIImagePickerControllerDelegate
{
private TestView _vc;
public myPickerDelegate ( TestView controller):base()
{
_vc = controller;
}
public override void FinishedPickingImage (UIImagePickerController myPicker, UIImage image, NSDictionary editingInfo)
{
// TODO: Implement - see: http://go-mono.com/docs/index.aspx?link=T%3aMonoTouch.Foundation.ModelAttribute
_vc.myImageView.Image = image;
myPicker.DismissModalViewControllerAnimated(true);
}
}
Try to call your event handlers code from the main thread by using BeginInvokeOnMainThread().
So my issue was very similar.
Instead of having a delegate class, I had the delegates inline for the picker.
For some reason the app froze every time after talking the image, not stopping in any breakpoint after that.
The solution that worked for me was to use this book:
http://www.scribd.com/doc/33770921/Professional-iPhone-Programming-with-MonoTouch-and-NET-C
Related
I am trying to port an Xamarin.Forms app to .NET MAUI but have run into the deprecation of Device.StartTimer, whilst this obviously currently still works in MAUI, I am interested to find out what the alternative is?
Currently I have a wrapper class as follows:
public void Start()
{
if (IsRunning)
{
return;
}
var wrapper = new TaskWrapper(Task, IsRecurring, true);
Tasks.Add(wrapper);
Device.StartTimer(Interval, wrapper.RunTask);
}
I tried replacing this with a System.Timers.Timer however this led to the issue of not being able to modify UI elements due to being on the wrong thread? The timer wrapper itself is used in multiple places so I can't use binding for example in this case either.
Is there actually a direct replacement for Device.StartTimer? Any assistance is greatly appreciated.
The Device timer is obsolete in MAUI.
You can create an IDispatcherTimer instance and subscribe to the Tick event like this:
var timer = Application.Current.Dispatcher.CreateTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += (s,e) => DoSomething();
timer.Start();
Depending on the context that you're using the timer in, you should use the MainThread.BeginInvokeOnMainThread() method to update UI elements:
void DoSomething()
{
MainThread.BeginInvokeOnMainThread(() =>
{
//Update view here
});
}
More info on UI main thread:
https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/appmodel/main-thread
IDispatcherTimer timer;
timer = Dispatcher.CreateTimer();
timer.Interval = TimeSpan.FromMilliseconds(1000);
timer.Tick += (s, e) =>
{
label.Text = DateTime.Now.ToString();
};
timer.Start();
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/button#press-and-release-the-button
I have an Xamarin Forms app with MvvmCross for Android and IOS and I would like to add a dark theme. My idea was to have to dictionaries with the ressources for either the dark or the light theme and load the one I need on startup.
I added this after I registered the dependencies in my MvxApplication:
if (Mvx.IoCProvider.Resolve<ISettingsManager>().Theme == AppTheme.Dark)
{
Application.Current.Resources.Add(new ColorsDark());
}
else
{
Application.Current.Resources.Add(new ColorsLight());
}
ColorsDark and ColorsLight are my ResourceDictionary. After that i can see the new Dictionary under Application.Current.Resources.MergedDictionaries but the controls can't find the resources as it seems. However it does work when I add it to the App.xaml
<ResourceDictionary Source="Style/ColorsDark.xaml" />
Do I have to put move that another part in the code or is that a wrong approach at all?
Personally don't like this approach at all. What i do: have a static class with all the colors, sizes etc. defined in static fields. At app startup or at app reload after changing skin just call ex: UiSettings.Init() for this ui definitions static class, like follows:
public static class UiSettings
{
public static Init()
{
if (Settings.AppSettings.Skin=="butterfly")
{
ColorButton = Color.Blue;
TitleSize= 12.0;
}
else
if (Settings.AppSettings.Skin=="yammy")
{
ColorButton = Color.Red;
if (Core.IsAndroid)
ButtonsMargin = new Thickness(0.5,0.6,0.7,0.8);
}
// else just use default unmodified field default values
}
public static Color ColorButton = Color.Green;
public static Thickness ButtonsMargin = new Thickness(0.3,0.3,0.2,0.2);
public static double TitleSize= 14.0;
}
in XAML use example:
Color= "{x:Static xam:UiSettings.ColorProgressBack}"
in code use example:
Color = UiSettings.ColorProgressBack;
UPDATE:
Remember that if you access a static class from different assemblies it is possible that you will access a fresh copy of it with default values where Init() didn't happen, if you face such case call Init() from this assembly too.
If you want something to load up when your app loads then you have to code it in App.xaml.cs
protected override void OnStart ()
{
if (Mvx.IoCProvider.Resolve<ISettingsManager>().Theme == AppTheme.Dark)
{
Application.Current.Resources.Add(new Xamarin.Forms.Style(typeof(ContentPage))
{
ApplyToDerivedTypes = true,
Setters = {
new Xamarin.Forms.Setter { Property = ContentPage.BackgroundImageProperty, Value = "bkg7.png"},
}
});
}
else
{
Application.Current.Resources.Add(new Xamarin.Forms.Style(typeof(ContentPage))
{
ApplyToDerivedTypes = true,
Setters = {
new Xamarin.Forms.Setter { Property = ContentPage.BackgroundImageProperty, Value = "bkg7.png"},
}
});
}
}
In this code I'm setting the BackgroungImage of all of my pages. Hope you'll get the idea from this code.
I'm implementing a DynamicItemStart button inside a Menu Controller. I'm loading the dynamic items for this button when Visual Studio starts. Everything is loaded correctly so the initialize method is called an I see all the new items in this Dynamic button. After the package is completely loaded I want to add more items to this Dynamic button, but since the package is already loaded the initialize method is not called again and I cannot see the new items in this Dynamic button. I only see the ones that were loaded when VS started.
Is there any way that I can force the update of this Dynamic button so it shows the new items?. I want to be able to update the VS UI after I added more items but outside the Initialize method.
The implementation I did is very similar to the one showed on this msdn example:
http://msdn.microsoft.com/en-us/library/bb166492.aspx
Does anyone know if an Update of the UI can be done by demand?
Any hints are greatly appreciated.
I finally got this working. The main thing is the implementation of a derived class of OleMenuCommand that implements a new constructor with a Predicate. This predicate is used to check if a new command is a match within the DynamicItemStart button.
public class DynamicItemMenuCommand : OleMenuCommand
{
private Predicate<int> matches;
public DynamicItemMenuCommand(CommandID rootId, Predicate<int> matches, EventHandler invokeHandler, EventHandler beforeQueryStatusHandler)
: base(invokeHandler, null, beforeQueryStatusHandler, rootId)
{
if (matches == null)
{
throw new ArgumentNullException("Matches predicate cannot be null.");
}
this.matches = matches;
}
public override bool DynamicItemMatch(int cmdId)
{
if (this.matches(cmdId))
{
this.MatchedCommandId = cmdId;
return true;
}
this.MatchedCommandId = 0;
return false;
}
}
The above class should be used when adding the commands on execution time. Here's the code that creates the commands
public class ListMenu
{
private int _baselistID = (int)PkgCmdIDList.cmdidMRUList;
private List<IVsDataExplorerConnection> _connectionsList;
public ListMenu(ref OleMenuCommandService mcs)
{
InitMRUMenu(ref mcs);
}
internal void InitMRUMenu(ref OleMenuCommandService mcs)
{
if (mcs != null)
{
//_baselistID has the guid value of the DynamicStartItem
CommandID dynamicItemRootId = new CommandID(GuidList.guidIDEToolbarCmdSet, _baselistID);
DynamicItemMenuCommand dynamicMenuCommand = new DynamicItemMenuCommand(dynamicItemRootId, isValidDynamicItem, OnInvokedDynamicItem, OnBeforeQueryStatusDynamicItem);
mcs.AddCommand(dynamicMenuCommand);
}
}
private bool IsValidDynamicItem(int commandId)
{
return ((commandId - _baselistID) < connectionsCount); // here is the place to put the criteria to add a new command to the dynamic button
}
private void OnInvokedDynamicItem(object sender, EventArgs args)
{
DynamicItemMenuCommand invokedCommand = (DynamicItemMenuCommand)sender;
if (null != invokedCommand)
{
.....
}
}
private void OnBeforeQueryStatusDynamicItem(object sender, EventArgs args)
{
DynamicItemMenuCommand matchedCommand = (DynamicItemMenuCommand)sender;
bool isRootItem = (matchedCommand.MatchedCommandId == 0);
matchedCommand.Enabled = true;
matchedCommand.Visible = true;
int indexForDisplay = (isRootItem ? 0 : (matchedCommand.MatchedCommandId - _baselistID));
matchedCommand.Text = "Text for the command";
matchedCommand.MatchedCommandId = 0;
}
}
I had to review a lot of documentation since it was not very clear how the commands can be added on execution time. So I hope this save some time whoever has to implement anything similar.
The missing piece for me was figuring out how to control the addition of new items.
It took me some time to figure out that the matches predicate (the IsValidDynamicItem method in the sample) controls how many items get added - as long as it returns true, the OnBeforeQueryStatusDynamicItem gets invoked and can set the details (Enabled/Visible/Checked/Text etc.) of the match to be added to the menu.
When I instantiate a new StageVideo instsance with stage.stageVideos[0] everything works great, but when i leave my view that's displaying the video it sticks on the stage. So when i goto another view it's still showing on the stage in the background. I tried setting sv = null, removing event listeners...etc.
I've created a StageVideoDisplay class which is instantiated in mxml like: and on view initialization i call a play() method:
if ( _path )
{
//...
// Connections
_nc = new NetConnection();
_nc.connect(null);
_ns = new NetStream(_nc);
_ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
_ns.client = this;
// Screen
_video = new Video();
_video.smoothing = true;
// Video Events
// the StageVideoEvent.STAGE_VIDEO_STATE informs you whether
// StageVideo is available
stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY,
onStageVideoState);
// in case of fallback to Video, listen to the VideoEvent.RENDER_STATE
// event to handle resize properly and know about the acceleration mode running
_video.addEventListener(VideoEvent.RENDER_STATE, videoStateChange);
//...
}
On video stage event:
if ( stageVideoInUse ) {
try {
_rc = new Rectangle(0,0,this.width,this.height);
_sv.viewPort = _rc;
} catch (e:Error) {}
} else {
try {
_video.width = this.width;
_video.height = this.height;
} catch (e:Error) {}
}
And on stage video availability event:
protected function toggleStageVideo(on:Boolean):void
{
// To choose StageVideo attach the NetStream to StageVideo
if (on)
{
stageVideoInUse = true;
if ( _sv == null )
{
try {
_sv = stage.stageVideos[0];
_sv.addEventListener(StageVideoEvent.RENDER_STATE, stageVideoStateChange);
_sv.attachNetStream(_ns);
_sv.depth = 1;
} catch (e:Error) {}
}
if (classicVideoInUse)
{
// If you use StageVideo, remove from the display list the
// Video object to avoid covering the StageVideo object
// (which is always in the background)
stage.removeChild ( _video );
classicVideoInUse = false;
}
} else
{
// Otherwise attach it to a Video object
if (stageVideoInUse)
stageVideoInUse = false;
classicVideoInUse = true;
try {
_video.attachNetStream(_ns);
stage.addChildAt(_video, 0);
} catch (e:Error) {}
}
if ( !played )
{
played = true;
_ns.play(path);
}
}
What happens is in the view when i navigator.popView(), the stageVideo remains on the stage, even in other views, and when returning to that view to play a different stream the same stream is kind of "burned" on top. I can not figure out a way to get rid of it! Thanks in advance!
In Flash Player 11, Adobe added the dispose() method to the NetStream class.
This is useful to clear the Video or StageVideo object when you're done with it.
When you call the dispose() method at runtime, you may get an exception indicating that there is no property named dispose on the NetStream object.
This occurs because Flash Builder is not compiling the app with the proper SWF version. To fix that, just add this to your compiler directives:
-swf-version=13
In the new Flash Builder 4.7, we hopefully won't have to specify the SWF version to use the newer Flash Player features.
This seems to be the best solution, but if you can't use Flash 11 (or the latest Adobe AIR), some other work arounds would be:
set the viewPort of stage video object to a rectangle that has width=0 and height=0
since stage video appears underneath your app, you can always cover the viewport (with a background color or some DisplayObject)
Ok the issue was actually that, even though it seemed like stage video was in use since i got the "Accelerated" message in status, that it was actually the video render even running and classic video was actually in use. The only thing I needed to do was add stage.removeChild( _video ) to the close() event in the class!!
I have a VideoDisplay instance playing some video. When I click on the video slider (also my component) the property videoDisplay.playheadTime is set and the videoDisplay.state goes from 'playing' into a 'seeking' state for a brief moment (the videoDisplay seeks for a new position and then plays the video again). Intended bevaiour.
But if I'm (or any random user) fast enough, I can set the playheadTime again while the player is still in 'seeking' state. When repeated several times every click is enqueued and the videoDisplay jump on every place of the video I have clicked(this is happening in an interval about 10-15 second after my last click). When I use live dragging the videoDisplay, overwhelmed by seekings, goes into 'error' state.
My question is - is there any way to cancel seeking state of the VideoDisplay class? For example player is in 'seeking' state, I set playheadTime, and the player forgets about last seeking and try to find the new place of the video.
I will downvote pointless answers like 'Use the Flex4 VideoPlayer class'!
One possible way is wrap the video display in a component and manage the seek a little better yourself. So if someone calls seek, make sure that the video is not currently seeking, if so, then wait till the current operation is complete before proceeding to the new one. If the user tries to seek again, discard all currently pending operations and make the latest one the next operation. Working on this exact problem right now.... Here's the code:
public function Seek(nSeconds:Number, bPlayAfter:Boolean):void
{
trace("Player Seek: "+ nSeconds);
var objSeekComand:VideoPlayerSeekCommand = new VideoPlayerSeekCommand(ucPlayer, nSeconds, bPlayAfter);
ProcessCommand(objSeekComand);
}
protected function ProcessCommand(objCommand:ICommand):void
{
if(_objCurrentCommand != null)
{
_objCurrentCommand.Abort();
}
_objCurrentCommand = objCommand
objCommand.SignalCommandComplete.add(OnCommandComplete);
objCommand.Execute();
}
Here's the Command
public class VideoPlayerSeekCommand extends CommandBase
{
private var _ucVideoDisplay:VideoDisplay;
private var _nSeekPoint:Number;
private var _bPlayAfterSeek:Boolean;
private var _bIsExecuting:Boolean;
public function VideoPlayerSeekCommand(ucVideoDisplay:VideoDisplay, nSeekPointInSeconds:Number, bPlayAfterSeek:Boolean, fAutoAttachSignalHandler:Function = null)
{
_ucVideoDisplay = ucVideoDisplay;
_nSeekPoint = nSeekPointInSeconds;
_bPlayAfterSeek = bPlayAfterSeek;
super(fAutoAttachSignalHandler);
}
override public function Execute():void
{
//First check if we are playing, and puase if needed
_bIsExecuting = true;
if(_ucVideoDisplay.playing == true)
{
_ucVideoDisplay.addEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, OnPlayerStateChangedFromPlay, false, 0, true);
_ucVideoDisplay.pause();
}
else
{
DoSeek();
}
}
protected function OnPlayerStateChangedFromPlay(event:MediaPlayerStateChangeEvent):void
{
_ucVideoDisplay.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, OnPlayerStateChangedFromPlay);
if(_bIsExecuting == true)
{
if(_ucVideoDisplay.playing == false)
{
DoSeek();
}
else
{
throw new Error("VideoPlayerSeekAndPlayCommand - OnPlayerStateChangedFromPlay error");
}
}
}
private function DoSeek():void
{
if(_bIsExecuting == true)
{
_ucVideoDisplay.seek(_nSeekPoint);
CheckSeekComplete();
}
}
private function CheckSeekComplete():void
{
if(_bIsExecuting == true)
{
if (Math.abs( _ucVideoDisplay.currentTime - _nSeekPoint) < 2)
{
if(_bPlayAfterSeek == true)
{
_ucVideoDisplay.play();
}
DispatchAndDestroy();
}
else
{
CoreUtils.CallLater(CheckSeekComplete, .07);
}
}
}
override public function Abort():void
{
_bIsExecuting = false;
SignalCommandComplete.removeAll();
}
}
Im Using AS3 Signals here instead of events, and the CoreUtils.Call later you can use setInterval, or a Timer. But the idea is to not call seek until the video is paused, and to keep track of when the seek is complete.