Xamarin.Forms Dispatcher.BeginInvokeOnMainThread causing deadlock on iOS - c#

I have an activity indicator on my Page called indicator.
I want to Animate it, so that it cycles through every color. For this i'm using this method:
public static async Task ActivityIndicatorRainbow(ActivityIndicator indicator, int colorDelay)
{
for (double i = 0; i < 1; i += 0.01)
{
Color c = Color.FromHsla(i, 0.5, 0.5);
indicator.Dispatcher.BeginInvokeOnMainThread(()=> indicator.Color = c);
await Task.Delay(colorDelay);
}
}
And in my ButtonPressed Method i call that using this:
activityIndicator.IsRunning = true;
await Task.Run(() => ActivityIndicatorRainbow(activityIndicator, 10));
On Android this works fine, the Indicator spins while changing colors. But on iOS the Indicator stands still until the Rainbow Task finishes and then it starts spinning.
I have already tried Device.InvokeOnMainThreadAsync() with the same results

I have tested in iOS Native Develop Tools (Xcode) by using Objective-C to change color each time less than 1 second, it occurs the same phenomnenon with Xamarin Forms . Then I think ActivityIndicator in iOS not supports changing color each time less than one seconds .
Therefore ,the easy way to make it work is to modfiy code as follow with every one second to change color :
activityIndicator.IsRunning = true;
await Task.Run(() => ActivityIndicatorRainbow(activityIndicator, 1000));
If need to less than one second to change color in iOS ,you can have a try with custom renderer to custom a ActivityIndicator by using Navtive mtethod CADisplayLink or DispatchSource.Timer .

Related

Applying gradient effect on scroll text in Unity2D

I made this gradient effect. I used SoftMask to mark the place I want to cover with the background getting the gradient effect on text. I know it would be easier to just use image background and make a gradient in photoshop (example). But what if I want things to be more dynamic? For example, I have a video in the background that on click pauses and shows up the same scroll text. Now, the tool that I made wouldn't work because the background in this scenario would be different. I've been searching for a shader that could do the trick, but couldn't find any. Is Unity even capable of doing something like this? The effect is pretty obvious and looks easy to make, but man in was pain in the a** to make this work efficiently.
The gradient effect function:
private void FadeOutTextGradiantAndArrow(Vector2 scrollRectValue)
{
var contentY = _textScrollViewContent.anchoredPosition.y;
var contentMaxY = _textScrollViewContent.rect.height - _textScrollView.GetComponent<RectTransform>().rect.height;
var gradientHeight = _softMaskCanvasGroup.GetComponent<RectTransform>().rect.height;
var contentYSkewed = contentY - (contentMaxY - gradientHeight);
var positionPercentage = contentYSkewed / gradientHeight;
if (contentMaxY - gradientHeight < contentY)
{
_softMaskCanvasGroup.alpha = 1 - positionPercentage;
_arrowBelowTextCanvasGroup.alpha = _softMaskCanvasGroup.alpha;
}
else
{
_softMaskCanvasGroup.alpha = 1;
_arrowBelowTextCanvasGroup.alpha = _softMaskCanvasGroup.alpha;
}
}

How to create a smooth animated text marquee?

I know that there are lot of different threads about horizontal text animation/text scrolling, but unfortunately none of them give smooth scrolling with repeatable text. I have tried double/thickness animation using various WPF controls containing text. Also tried animating with visual brush which gives me by far the most elegant scrolling compared to other approaches (for e.g. playing with Canvas.Left property etc.) but that too goes blur the text, if the text length or the animation speed is too high.
I'm over to a pure DirectX C# implementation using SharpDX library. Should also mention that I'm a beginner with DirectX programming. Here is the code:
public void RunMethod()
{
// Make window active and hide mouse cursor.
window.PointerCursor = null;
window.Activate();
var str = "This is an example of a moving TextLayout object with no snapped pixel boundaries.";
// Infinite loop to prevent the application from exiting.
while (true)
{
// Dispatch all pending events in the queue.
window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessAllIfPresent);
// Quit if the users presses Escape key.
if (window.GetAsyncKeyState(VirtualKey.Escape) == CoreVirtualKeyStates.Down)
{
return;
}
// Set the Direct2D drawing target.
d2dContext.Target = d2dTarget;
// Clear the target.
d2dContext.BeginDraw();
d2dContext.Clear(Color.CornflowerBlue);
//float layoutXOffset = 0;
float layoutXOffset = layoutX;
// Create the DirectWrite factory objet.
SharpDX.DirectWrite.Factory fontFactory = new SharpDX.DirectWrite.Factory();
// Create a TextFormat object that will use the Segoe UI font with a size of 24 DIPs.
textFormat = new TextFormat(fontFactory, "Verdana", 100.0f);
textLayout2 = new TextLayout(fontFactory, str, textFormat, 2000.0f, 100.0f);
// Draw moving text without pixel snapping, thus giving a smoother movement.
// d2dContext.FillRectangle(new RectangleF(layoutXOffset, 1000, 1000, 100), backgroundBrush);
d2dContext.DrawTextLayout(new Vector2(layoutXOffset, 0), textLayout2, textBrush, DrawTextOptions.NoSnap);
d2dContext.EndDraw();
//var character = str.Substring(0, 1);
//str = str.Remove(0, 1);
//str += character;
layoutX -= 3.0f;
if (layoutX <= -1000)
{
layoutX = 0;
}
// Present the current buffer to the screen.
swapChain.Present(1, PresentFlags.None);
}
}
Basically it creates an endless loop and subtracts the horizontal offset. Here are the challenges: I need repeatable text similar to HTML marquee without any gaps, Would probably need to extend it to multiple monitors.
Please suggest.
I don't know neither how to use DirectX nor sharpdx, but if you want you can consider this solution
I had a similar problem a while ago, but with the text inside a combobox. After a bounty i got what i was looking for. I'm posting the relevant piece of code as an example, but you can check the complete answer here
Basically, whenever you have a textblock/textbox that contain a string that cannot be displayed completely, cause the length exceed the textblock/box lenght you can use this kind of approach. You can define a custom usercontrol derived from the base you need (e.g. SlidingComboBox : Combobox) and define an animation for you storyboard like the following
_animation = new DoubleAnimation()
{
From = 0,
RepeatBehavior = SlideForever ? RepeatBehavior.Forever : new RepeatBehavior(1), //repeat only if slide-forever is true
AutoReverse = SlideForever
};
In my example i wanted this behaviour to be active only when the mouse was on the combobox, so in my custom OnMouse enter i had this piece of code
if (_parent.ActualWidth < textBlock.ActualWidth)
{
_animation.Duration = TimeSpan.FromMilliseconds(((int)textBlock.Text?.Length * 100));
_animation.To = _parent.ActualWidth - textBlock.ActualWidth;
_storyBoard.Begin(textBlock);
}
Where _parent represent the container of the selected item. After a check on the text lenght vs combobox lenght i start the animation and end it at the end of the text to be displayed
Note that in the question i mentioned there are also other soltions. I'm posting the one that worked for me

How to resume picturebox image's gif winforms C#

When a specific condition is met I disable my picturebox and the image freeze's, whenever it runs again the gif starts all over again not from where it was stopped is there anyway to resume it ?
Stopping picturebox : http://prntscr.com/b6gg7a
"Resuming" picturebox : http://prntscr.com/b6gglb
I believe indeed this is a rather tedious/unlikely to perform with an animated gif.
What you could/should do IMO is the following:
Instead of making the background a gif, break it into multiple frames(pictures).
Set up a timer that triggers the background to change to the next image.
You can pause and resume that timer the same as you would enable and disable the picturebox.
Your background change function could look something like this:
int bgCount = 8; //Amount of frames your background uses
int bgCurrent = 0;
string[] bgImg = {
"bg1.png",
"bg2.png",
"bg3.png",
"bg4.png",
"bg5.png",
"bg6.png",
"bg7.png",
"bg8.png",
};
void changeBG()
{
if(bgCurrent > bgCount-1) //If not displaying the last background image go to the next one
{
bgCurrent++;
imgBox.image = bgImg[bgCurrent];
}else //If displaying the last background image -> Start with the first one
{
bgCurrent = 0;
imgBox.image = bgImg[bgCurrent];
}
}
}
And you just let the timer trigger this event. And you simply start and stop the timer.
Sidenote:
1* You might experiment with, instead of changing the source, just make 8 imageboxed and call/display them as they are needed.

Asynchronous method call in C#

We have such a situation. We have a canvas, on which some ammount of figures are rendered. It may be 1 or many more (for example thousand) and we need to animate their translation to another location (on button click) using storyboard:
internal void someStoryBoard(figure someFigure, double coordMoveToValue)
{
string sbName = "StoryBoard_" + figure.ID;
string regName = "figure_" + figure.ID;
try
{
cnvsGame.Resources.Remove(sbName);
cnvsGame.UnregisterName(regName);
}
catch{ }
someCanvas.RegisterName(regName, someFigure.Geometry);
var moveFigureYAnimation = new PointAnimation();
moveFigureYAnimation.From = new Point(someFigure.Geometry.Center.X, someFigure.Geometry.Center.Y);
moveFigureYAnimation.To = new Point(someFigure.eGeometry.Center.X, coordMoveToValue);
moveFigureYAnimation.Duration = TimeSpan.FromSeconds(0.5);
var sbFigureMove = new Storyboard();
Storyboard.SetTargetName(sbFigureMove, regName);
Storyboard.SetTargetProperty(sbFigureMove, new PropertyPath(Geometry.CenterProperty));
sbFigureMove.Children.Add(moveFigureYAnimation);
cnvsGame.Resources.Add(sbName, sbFigureMove);
sbFigureMove.Begin();
}
Figures are stored in list. We are calling this StoryBoard using for loop:
for(int i = 0; i<listOfFigures.Count; i++)
{
someStoryBoard(listOfFigures[i], someCoord);
}
But here's the problem: if we have a little amount of figures - code completes quickly. But if ammount is big - there is a delay after a button is clicked and before the figures begin to move.
So, here's the question: is it possible to call someStoryBoard method asynchronously? Is next algorithm possible -> When someStoryBoard is called it begins to move figure instantly, not waiting for whole for loop to complete.?
You can add actions into Dispatcher queue by calling Dispatcher.InvokeAsync. You can also specify dispatcher priority, depending on your requirements.
Please note that moving thousands of items can't be reliably fast, so you may need to rethink the drawing logic. If even starting animation is slow, it's highly likely animating won't be fast enough too.
You can try use async/await modifier
async internal Task someStoryBoard(figure someFigure, double coordMoveToValue)

MP3 played in separate thread

One problem was solved, another followed: In a C#-program I use following method to set a labels color to green, then playing a mp3-file and finally setting the color back to black.
The problem is that the sound seems to be played in an extra thread, thus the time between the change of the two colors is too short (in fact, it should have the green color while the file is played).
private void playSound()
{
label1.ForeColor = Color.LimeGreen;
Application.DoEvents();
WMPLib.WindowsMediaPlayer wmp = new WMPLib.WindowsMediaPlayer();
wmp.URL = #"C:\examplesound.mp3"; // duration about 30s
wmp.controls.play();
label1.ForeColor = Color.Black;
}
Is there anything can be done to force the label to keep the green color whilst the mp3-file is played?
Don't set the colour back to black straight away as the playback is in another thread.
When the current track ends WMPLib sends out a PlayStateChange event.
So add a handler:
wmp.PlayStateChange += this.Player_PlayStateChange;
private void Player_PlayStateChange(int newState)
{
if ((WMPLib.WMPPlayState)newState == WMPLib.WMPPlayState.wmppsStopped)
{
label1.ForeColor = Color.Black;
}
}
The page for playState has a list of values:
8 - MediaEnded - Media item has completed playback.
You'll need to make sure this is done on the UI thread.
Try hooking the PlayStateChanged event and put the label1.ForeColor = Color.Black; in there.
At the moment there's nothing in your code saying that it should only change to black when it finishes, only after it has started to play.

Categories