Creating a "toast" - xamarin.forms

I have been trying to get similar functionality to an Android "toast" using Xamarin forms. After looking around, I found what I think is a good solution. The general approach appears to be to make a new Absolute layout, and make it appear for a set time, then disappear. While I think I generally understand what is being done, I can't seem to get it to work. Can anyone suggest how I would use this class if I want to make a toast appear in my MainPage? Should I be adding an AbsoluteLayout in the XAML file? Sorry, I'm sure this is a simple question, but I can't really figure out what to do...
Any help would be greatly appreciated!
public static class Popper
{
public async static Task Pop (string message, AbsoluteLayout attachLayout, int showforMilliseconds = 1500)
{
var container = new StackLayout
{
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
BackgroundColor = Color.FromHex ("#DDEFEFEF"),
Padding = 10
};
var label = new Label
{
Text = message,
FontAttributes = FontAttributes.Bold,
Style = (Style)Application.Current.Resources["PopupText"]
};
container.Children.Add (label);
container.Scale = 0;
container.Opacity = 0;
attachLayout.Children.Add (container, attachLayout.Bounds, AbsoluteLayoutFlags.PositionProportional);
container.ScaleTo (1.0f, 100);
container.FadeTo (1.0f, 100);
await Task.Delay (showforMilliseconds);
container.ScaleTo (0.0f, 250);
await container.FadeTo (0.0f, 250);
attachLayout.Children.Remove (container);
}
}

On Android you don't have to reinvent the wheel since Toast exists natively. On other platforms there is no such thing like Toast, therefor there is no silver-bullet solution here. This problem have been solved by multiple people in multiple ways, thats why I left a comment that your question might be a duplicate of existing thread with multiple examples.
Now about your idea. Your implementation is working, however it will show Toast only on an AbsoluteLayout. Why to set such a restriction? If you will recheck the link I shared in comments you will find a more appropriate and elegant solutions.
I can't seem to get it to work.
All you need is a an AbsoluteLayout on your page so you could call your method:
await Popper.Pop("Hello world", referenceToYourAbsoluteLayout, 5000);
If you still for some reason want to stick to this exact solution, maybe it will make sense to have an extension method instead. However this solution just does not make sense for the average user.
P.S: Once again, please check the existing thread for more information and details.
P.S.S: Usage example of your code snippet
<!-- XXXPage.xaml -->
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XXX"
x:Class="XXX.XXXPage">
<AbstractLayout x:name="myLayout />
</ContentPage>
// XXXPage.xaml.cs
public partial class XXXPage : ContentPage
{
public Test999Page()
{
InitializeComponent();
}
async protected override void OnAppearing()
{
base.OnAppearing();
await Popper.Pop("Hello world", myLayout, 5000);
}
}

Related

Garbage Collector seems to cause the Button in my MahApps.Metro Custom Dialog to stop working

I am trying to write a simple custom dialog with one button to close/hide it. The code is mostly taken from the from the MahApps Examples Application with slight modifications. I am using a dialog service (called AutomationDialogService) to open the dialog from my ViewModel (see code below).
The problem I having, is that I am seeing weird behavior from my custom dialog. When I start my program, my button works correctly and executes the action to close the dialog as expected (see code). This works two to three times, if do it in quick succession, until it stops working. At that point, the dialog opens, but the button does not work any more. The point in time at which the button stops working seems to coincide with a run of the Garbage Collector (GC) as indicated in the Diagnostics Tools of Visual Studio.
Unfortunately I don't even know were to start looking/debugging. What I don't understand, is that my code is very similar to the code used in the .Net 4.5 CustomDialogExampleContent.cs, which pops up when you select "Show CustomDialog via VM" from the Dialogs drop-down menu of the example application include MahApps.Metro. But my code it does not work.
Therefore, the problem may be related to my usage of Dependency-Injection with StructureMap 4.3. Since this is a Plugin, I have put its registry into a separate file PluginRegistry. I also added the relevant entries as code snippets below. I should add, that I am using the same registry entries for my main DialogService, without any issues.
I would be grateful, if anyone could provide any pointer, as to what I could try to debug this strange issue.
After much talking, here is the code I have:
Dialog-related code in AutomationDialogService:
private CustomDialog automationRunningDialog;
private AutomationRunningDialogViewModel customDialogContent;
public async void OpenAutomationRunningDialog()
{
automationRunningDialog = new CustomDialog() { Title = "Automation Running" };
customDialogContent = new AutomationRunningDialogViewModel(instance =>
{
dialogCoordinator.HideMetroDialogAsync(parentViewModel, automationRunningDialog);
});
automationRunningDialog.Content = new AutomationRunningDialogView { DataContext = customDialogContent };
await dialogCoordinator.ShowMetroDialogAsync(parentViewModel, automationRunningDialog);
}
ViewModel of the AutomationDialog:
class AutomationRunningDialogViewModel : ViewModelBase
{
public AutomationRunningDialogViewModel(Action<AutomationRunningDialogViewModel> closeButtonHandler)
{
buttonCommand = new RelayCommand(() =>
{
closeButtonHandler(this);
});
}
private ICommand buttonCommand;
public ICommand ButtonCommand => buttonCommand;
}
And this is the corresponding AutomationRunningDialogView:
<UserControl x:Class="Extensions.Automation.Dialogs.AutomationRunningDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Extensions.Automation.Dialogs"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Button Margin="0 8 0 8"
HorizontalAlignment="Right"
Command="{Binding ButtonCommand}">
OK
</Button>
</Grid>
</UserControl>
Code snipptes from PluginRegistry related to AutomationDialogService:
this.ForConcreteType<AutomationDialogService>()
.Configure
.Ctor<IDialogCoordinator>("dialogCoordinator").Is(c => c.GetInstance<DialogCoordinator>())
.Ctor<ViewModelBase>("parentViewModel").Is(c => c.GetInstance<MainWindowDialogParentViewModel>())
.Singleton();
this.For<IAutomationDialogService>()
.Use(c => c.GetInstance<AutomationDialogService>())
.Singleton();
Your RelayCommand action is accessing a closure (closeButtonHandler), which is being garbage collected:
buttonCommand = new RelayCommand(() =>
{
closeButtonHandler(this);
});
and therefore you need to use the keepTargetAlive parameter of RelayCommand:
buttonCommand = new RelayCommand(() =>
{
closeButtonHandler(this);
}, keepTargetAlive: true);
Further details here http://www.mvvmlight.net/doc/weakaction.cshtml
I thought my code was sufficiently similar to the MahApps-Example, but it was not. The MahApps example uses its own command SimpleCommand, while my code used the RelayCommand from MvvmLight.
I can eliminate the problem by replacing the following lines in AutomationRunningDialogViewModel:
buttonCommand = new RelayCommand(() =>
{
closeButtonHandler(this);
});
with these:
buttonCommand = new SimpleCommand
{
ExecuteDelegate = o => closeButtonHandler(this)
};
The main difference seems to be the signature of the command delegate-action between these two commands. For ExecuteDelegate in SimpleCommand it is:
public Action<object> ExecuteDelegate { get; set; }
where as for the ctor of RelayCommand it is:
public RelayCommand(Action execute);
So there is an obvious difference. But I don't understand, why this causes the problematic behavior with RelayCommand mentioned above, and if there is some way to continue using RelayCommand instead of SimpleCommand.
I would be grateful for comments!

Handling error conditions on Flex

I have the following AS code.I have noticed that if an Application i s using the webcamera then it cannot be used by any secondary applications until unless the primary application is closed.
My question is that from the following code 1.can we capture that condition
2.If no camera is detected how to give the alert since it is an AS code
EDIT:
Filename is cldAS.as
Now how to call cldAS() from any.mxml file .Some example would be appreciated
package org.com
{
import flash.display.Sprite;
import flash.media.*;
import flash.net.*;
public class cldAS extends Sprite
{
public function cldAS()
{
var cam:Camera = Camera.getCamera();
if(cam != null)
{
cam.setMode(640, 480, 30);
var video:Video = new Video(300, 450);
video.attachCamera(cam);
addChild(video);
}
else
{
trace("No Camera Detected");
//How to give an alert here
}
}
}
}
Alert.show("You don't seem to have a webcam.");
instead of
trace(...) ?
Alert is available in Flex only , in AS3 you should really implement your own solution, on the other hand , since Alert is a Javascript function , you could also use ExternalInterface to call it.
As far as implementing your own solution is concerned, at the minimum you need a TextField to display your message, which text you could provide by sending a CustomEvent with a message property that will simply take a String. It wouldn't take too much work to create your own Alert class.It would sit on top of your App , you could toggle visibility when receiving a CustomEvent and have a Close button to hide it.
You should be able to call your AS3 class within script tags , other than that I'll leave a more detailed answer to Flex experts. I'm not sure if you can add a Sprite directly into Flex , for all I remember an object in Flex must inherit from UIComponent in order to be added to the stage but check with the other guys here, I haven't used Flex in quite some time...
<mx:Script>
import org.com.cldAS;
public cld:cldAS = new cldAS();
</mx:Script>

How does one smooth an image used as a control skin?

I'm embedding an image like this:
[Embed(source="/tool_deleteUp.png")]
private static const c_deleteButton_styleUp:Class;
I'm using it like this:
_removeButton = new Button();
_removeButton.setStyle('upSkin', c_deleteButton_styleUp);
When I rotate the button, the image doesn't scale smoothly. I know the tricks one uses to scale an image loaded in an Image control, but I'm banging my head against a wall trying to figure out how to do it here.
Help!
a hacky way would be to traverse the children/grandchildren of the button, to find the corresponding Bitmap that is of type c_deleteButton_styleUp, and set its smoothing to true ... it is a big flaw of flex, that sometimes it requires classes for styling although some IDisplayObjectFactory would completely suffice for that purpose and would make your life a lot easier ... but life is life ...
don't know of a clean flex only way ... the only possibility i could think of, is to create an SWF, that contains your asset as a symbol, with smoothing turned on, and embed this symbol from that SWF ...
hope that helps ...
greetz
back2dos
A better, more general solution. Not only does it handle the above case, but it does it
Without subclasses
It works with any UIComponent, including IRawChildContainers (like Container), which hide skin children in rawChildren
It only smooths newly added items, instead of running every time the control updates.
public static function smoothChildBitmaps(object:UIComponent):void{
// Define a nested smooth method
function smoothChildren(val:UIComponent):void{
var childList:IChildList;
if(val is IRawChildrenContainer){
childList = (val as IRawChildrenContainer).rawChildren;
}
else{
childList = object;
}
for(var i:int = 0; i < childList.numChildren; i++){
var child:Bitmap = childList.getChildAt(i) as Bitmap;
if(child != null){
child.smoothing = true;
}
}
};
// Call the nested method on the object right away
smoothChildren(object);
// Set up an event handler to re-call the method when a child is added
object.addEventListener(
Event.ADDED,
function(args:Event):void{
smoothChildren(object);
}
);
}
Silly, but it works.
import flash.display.Bitmap;
import mx.controls.Button;
public class SmoothButton extends Button{
protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
for(var i:int = 0; i < this.numChildren; i++){
var child:Bitmap = this.getChildAt(i) as Bitmap;
if(child != null){
child.smoothing = true;
}
}
}
}
I do not know what you folks banged your head on no offense, just joking. I had a desk :-P
Anyway, those scripts presented are totally useless! For one thing you cannot cast a uicomponent to bitmap! The other thing is, that the seconds script with rawchildren ist more ore less the same. rawchildren gets your the children and the chrome of the container - that's it. If you have a button in a container you can try to smooth the chrome of the container - the rest stays the same since all casts to bitmap will fail.
So, did anyon rdfm?
On the other hand. One could try out stage.quality to stagequality.best. If you check the adobe forums http://forums.adobe.com/thread/427899 you will see that flex runs well - just air kind of ignores this :-(
If those two scripts ever worked - esp. the part with button as bitmap - I'll go and smash my head somewhere :-). I see NO WAY in the class hierarchy that this could work. After all, numChildren only gives you uicomponents or displayobjects but nothing more!

Flex: Popup Window - Get [ok] or [cancel]

I've done a lot of C# programming with both Winforms and WPF. I'm working on a Flex/Air app now for cross platform support. But this is my first flex project, so I'm learning as I go.
I've got a window that I want to popup, that the user will fill out a form, then hit OK or CANCEL. I set it up the same way I would've in C#, but it doesn't work, and I can't really see a way to make it do what I want.
EDIT:
So I'm trying events now, the events just don't seem to be handled...
EDIT again:
Oh, It's because the popup manager seems to create a new instance of the Form object, rather than using the one I created already.
so in the showWindow method, I put in this code rather than the popup manager:
parent.addChild(this);
then I remove it when I close it. The only problem is, it doesn't disable the rest of the parent like the popup manager does. Any suggestions on that?
PARENT:
private function btnAdd_Clicked():void
{
var form:Form = new Form();
form.addEventListener(CloseEvent.CLOSE, onFormClosed, false, 0, true);
recipeForm.showWindow(this);
}
private function onFormClosed(e:CloseEvent):void
{
//none of these Alerts are ever shown. I also tried breakpoints in debug to try an follow the code, with no luck
Alert.show("Closed");
if(e.detail == Alert.OK)
{
Alert.show("OK");
}
else if(e.detail == Alert.CANCEL)
{
Alert.show("Cancel");
}
}
CHILD:
private function btnCancel_Clicked():void
{
okClicked = false;
closeWindow();
}
public function closeWindow():void
{
var e:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
e.detail = okClicked ? Alert.OK : Alert.CANCEL;
dispatchEvent(e);
PopUpManager.removePopUp(this);
}
public function showWindow(parent:WindowedApplication):void
{
var window:IFlexDisplayObject = PopUpManager.createPopUp(parent, RecipeForm, true);
PopUpManager.centerPopUp(window);
}
You can do this at least two different ways:
FIRST WAY: Using events
Let your Form class dispatch an event when either of the buttons is clicked. After Form is instantiated from the parent view, add an eventListener for the event(s) it's known to dispatch. When the Form dispatches the event, the eventListener will be invoked. You can even reuse Flex's CloseEvent and set the "detail" property to either Alert.OK or Alert.CANCEL before dispatching it.
In Form:
var e:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
e.detail = okClicked ? Alert.OK : Alert.CANCEL;
dispatchEvent(e);
In parent:
var f:Form = new Form();
f.addEventListener(CloseEvent.CLOSE, onClose, false, 0, true);
...
private function onClose(e:CloseEvent):void
{
if (e.detail == Alert.OK)
// do something
else if (e.detail == Alert.CANCEL)
// do something else
}
SECOND WAY: Using callbacks
Add a public var of type "Function" to your Form class and supply a callback function from the parent. This does basically the same thing as #1 except with little less abstraction / indirection.
I would recommend #1 since the event model in Flex is pretty well-conceived and more flexible than the callback.
In Form:
var e:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
e.detail = okClicked ? Alert.OK : Alert.CANCEL;
dispatchEvent(e);
In parent:
var f:Form = new Form();
f.addEventListener(CloseEvent.CLOSE, onClose, false, 0, true);
...
private function onClose(e:CloseEvent):void
{
if (e.detail == Alert.OK)
// do something
else if (e.detail == Alert.CANCEL)
// do something else
}
Not sure if this is still an open issue. I ran into this very same problem and I think I figured out what is wrong. At least I did for my problem.
I implemented things exactly as you did. I also have the close attribute set to closeWindow (I'm using a TitleWindow for my dialog).
So when the window is closed via the X at the top, it will call closeWindow, also if you click on the Cancel button, it will also call closeWindow.
The problem for me was that clicking cancel, dispatches a CloseEvent which seems to be caught by a Listener which calls closeWindow again (possibly via the close attribute which probably creates its own internal listener). I'm not sure if its an infinite loop but Flex does not like this.
My solution was to create two functions, one for the X close window to call and one for the Cancel button to dispatch a CloseEvent of its own. This seemed to work for me. Hope it helps you.

drag-and-drop problem in a Tilelist using an ItemRenderer

in my flex application, I created a Tilelist. In this Tilelist, I am using an ItemRenderer to create a box consisting of an image and an VSlider in each tile.
The tile need to be dragable when you click on the image, but not dragable when you slide the slider. How can I achieve this ? I have been scratching my head searching on Google for one day and I have really no idea.
I look forward to your help.
Thank you.
I found a solution to my problem, however it may not be the best one.
Using this :
public var overImage:Boolean = false;
public function checkAllow(evt:DragEvent):void {
if(overImage == false)
{
evt.preventDefault()
}
}
public function isOverImage():void {
overImage = true;
}
public function isOutImage():void {
overImage = false;
}
I call those functions like this :
On my image component
mouseOver="outerDocument.isOverImage()" mouseOut="outerDocument.isOutImage()"
And for my tilelist I did this
Tiles.addEventListener(DragEvent.DRAG_START, checkAllow);
Hope it helps some people.
if(event.target is ScrollThumb )
{
return;
}
problem solved by returning the scrollThumb property of sroller in imageDragStart method...
BackGround:My TileList is provided with mouseDown ="event.currentTarget.addEventListener(MouseEvent.MOUSE_MOVE, imageDragStart )";
which was effecting complete TileList along with scroller ,
This above was the temp fix, but expecting experts suggestions.
Basically this is for Native application (AIR), used NativeDragStart. but am forced to use mouseDown over my TileList to invoke imageStartDrag() method of mine....

Resources