I'm new at async world and i started developing some stuff for windows store apps.
My problem is that i need to wait the async method of filepicker ends to do other stuff.
So i tried search for: "run synchronous and asynchronous method c#" and i ended on Stephen Cleary blog. I tryed to use Nito AsyncEx to lead with my problem and it doesn't work because when i call the following method it doesn't stop.
private List<Templates> classModel = new List<Templates>();
private void btnLoadClassExercises_Click(object sender, RoutedEventArgs e)
{
AsyncContext.Run(() => loadFile());
// do some stuff with classModel
}
private async void loadFile()
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.FileTypeFilter.Add(".xml");
var file = await openPicker.PickSingleFileAsync();
classModel = xml.getControlClass(file);
}
During my search i found a classe called AsyncInLine that helped me out with some others stuffs like this one. But with this specific situation, i didn't work too. (http://social.msdn.microsoft.com/Forums/en-US/async/thread/163ef755-ff7b-4ea5-b226-bbe8ef5f4796)
Can someone help me?
Edit #1:
//When i click on this button, it loads the file and all canvas are drawn on canvasSegment.
private async void btnLoadClassExercises_Click(object sender, RoutedEventArgs e)
{
try
{
if (classModel != null)
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.FileTypeFilter.Add(".xml");
var file = await openPicker.PickSingleFileAsync();
classModel = xml.getControlClass(file);
auxCV = 0;
if (editClass)
CanvasSegment.Children.Clear();
foreach (Templates temp in classModel)
createCanvasSegment(temp);
editClass = true;
}
}
catch { }
}
// When i want to del some canvas, i put the removeCV = true and click on a canvas
private void btnDelExercise_Click(object sender, RoutedEventArgs e)
{
classEdit = classModel; //To debuf if the classModel.Count = 0
removeCV = true;
}
//When i click on a canvas, if removeCV = false, i draw the canvas on the main canvas(canvasClass), otherwise, i delete it. The problem is here. My modelClass.count here is = 0.
private void cv_PointerPressed(Templates temp, Canvas cv)
{
if (!removeCV)
{
foreach (StackPanel sp in CanvasSegment.Children)
foreach (Canvas c in sp.Children)
c.Opacity = 1;
cv.Opacity = 0.8;
canvasClass.Children.Clear();
for (int i = 0; i < temp.Shapes.Count; i++)
{
//Do some stuff with points and draws on CanvasClass later
rh.renderModel(new InkManager(), canvasClass, temp.Shapes[i], true, false);
}
}
else
{
for (int i = 0; i <= classModel.Count; i++)
if (classModel[i] == temp)
{
classModel.RemoveAt(i);
break;
}
CanvasSegment.Children.Clear();
auxCV = 0;
foreach (Templates tempModel in classModel)
createCanvasSegment(tempModel);
removeCV = false;
}
}
//I create a mini canvas foreach template in file.
private void createCanvasSegment(Templates temp)
{
Thickness thk = new Thickness();
thk.Right = 5;
thk.Bottom = 5;
if (temp != null || temp.Shapes != null)
{
if (auxCV == 0)
{
spcv = new StackPanel();
spcv.Orientation = Orientation.Horizontal;
spcv.Width = 540;
}
Canvas cv = new Canvas()
{
Background = new SolidColorBrush(Colors.Wheat),
Name = temp.Template,
Height = 73.125,
Width = 130,
Margin = thk,
};
cv.PointerPressed += (s, e) => cv_PointerPressed(temp, cv);
foreach (ShapePoints tempshapePoints in temp.Shapes)
{
ShapePoints tempS = tempShapePoints;
if (!removeCV)
{
//Do some stuff with points
}
rh.renderModel(new InkManager(), cv, tempS, true, false);
}
auxCV++;
spcv.Children.Add(cv);
if (auxCV == 1)
CanvasSegment.Children.Add(spcv);
else if (auxCV == 4)
auxCV = 0;
}
}
Since this is already in a UI thread's context, there's no need for AsyncContext.Run - just use:
private async void btnLoadClassExercises_Click(object sender, RoutedEventArgs e)
{
await loadFile());
// do some stuff with classModel
}
// Make this task returning (or even return the list instead of setting a local)
private async Task loadFile()
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.FileTypeFilter.Add(".xml");
var file = await openPicker.PickSingleFileAsync();
classModel = xml.getControlClass(file);
}
Related
I have a list of images(Helper.theaterImagesInfo) that i am reading through a foreach loop and continuing infinite loop using while until a form closed. images display well with the code below
Application.DoEvents();
await Task.Delay(img.durationInMilliseconds);//img.durationInMilliseconds
this.PictureBox1.Image = img.ImageHere;
Now i want to implement fade effect to those images displaying. Each image is displayed after 5 seconds.
Function to apply fade:
private int intCount = 0;
private List<Bitmap> lstFade = new List<Bitmap>();
private void ApplyFade(Bitmap imgTarget, Bitmap imgSource)
{
int intX = 0;
int intY = 0;
int intAmount = 10;
Rectangle rctDest = new Rectangle(intX, intY,
imgTarget.Width, imgTarget.Height);
using (var tmpBitMap = new Bitmap(imgSource))
using (var gTemp = Graphics.FromImage(tmpBitMap))
for (int i = 0; i <= intAmount; i++)
{
gTemp.DrawImage(imgSource, intX, intY);
ColorMatrix cmColors = new ColorMatrix();
cmColors.Matrix33 = System.Convert.ToSingle(i /
(double)intAmount);
ImageAttributes iaAttributes = new ImageAttributes();
iaAttributes.SetColorMatrix(cmColors);
gTemp.DrawImage(imgTarget, rctDest, intX, intY,
imgTarget.Width, imgTarget.Height, GraphicsUnit.Pixel,
iaAttributes);
lstFade.Add((Bitmap)tmpBitMap.Clone());
}
}
Timer:
private void timer1_Tick(object sender, EventArgs e)
{
intCount += 2;
if (intCount > 10)
{
timer1.Stop();
}
else
{
PictureBox1.Image = lstFade[intCount];
}
}
and here reading list and applying fade to images
public int x=1;
private async void TheaterForm_Shown(object sender, EventArgs e)
{
if (this.PictureBox1.Image == null) SetDefaultImage();
if (Helper.theaterImagesInfo != null && Helper.theaterImagesInfo.Count >
0)
{
while (x == 1)
{
foreach (var img in Helper.theaterImagesInfo.ToList())
{
if (img.IsAnimated)
{
try
{
Application.DoEvents();
var imgS = new Bitmap(this.PictureBox1.Image);
await Task.Delay(img.durationInMilliseconds);
var imgT = new Bitmap(img.ImageHere);
ApplyFade(imgT, imgS);
intCount = 0;
timer1.Interval = 200;
timer1.Start();
}
catch { }
}
else
{
try
{
Application.DoEvents();
await Task.Delay(img.durationInMilliseconds);
var imgS = new Bitmap(this.PictureBox1.Image);
var imgT = new Bitmap(img.ImageHere);
ApplyFade(imgT, imgS);
intCount = 0;
timer1.Interval = 200;
timer1.Start();
}
catch { }
}
if (this.x == 0)
{
break; // exit the for loop early
}
}
}
SetDefaultImage();
}
}
Problem
Fade is applied between starting two images and then keep on the same fading process with those two images. Seems like ApplyFade function doesn't apply again the imgSource and imgTarget once passed through the parameter
I have a listview whose data is fetched from json on the server.
Code:
ObservableCollection<QuizHome> quizhomedatasource = new ObservableCollection<QuizHome>();
private bool incall = false, endoflist = false;
int offset = 1, halaman = 1;
private void MainGrid_Loaded(object sender, RoutedEventArgs e)
{
quizhomedatasource.Clear();
QuizList.ItemsSource = quizhomedatasource;
ProgressQuiz(offset);
}
private void QuizList_Loaded(object sender, RoutedEventArgs e)
{
ScrollViewer viewer = GetScrollViewer(this.QuizList);
viewer.ViewChanged += Viewer_ViewChanged;
}
private void Viewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
ScrollViewer view = (ScrollViewer)sender;
double progress = view.VerticalOffset / view.ScrollableHeight;
if (progress > 0.7 && !endoflist)
{
incall = true;
while (offset < halaman)
{
ProgressQuiz(++offset);
}
}
else
{
incall = false;
}
}
public static ScrollViewer GetScrollViewer(DependencyObject depObj)
{
if (depObj is ScrollViewer) return depObj as ScrollViewer;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = GetScrollViewer(child);
if (result != null) return result;
}
return null;
}
private async void ProgressQuiz(int offset)
{
try
{
urlPath = "https://mhnkp2.com/school/api-v3/fetch/tryout_paket_perkelas";
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("halaman", offset.ToString()),
new KeyValuePair<string, string>("limit", "10"),
new KeyValuePair<string, string>("kelas", "2")
};
var httpClient = new HttpClient(new HttpClientHandler());
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("SCH-API-KEY", "SCH_KEnaBiDeplebt");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("token", token.Token);
var response = await httpClient.PostAsync(urlPath, new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
string jsonText = await response.Content.ReadAsStringAsync();
try
{
JsonObject jsonObject = JsonObject.Parse(jsonText);
JsonObject groupObject1 = jsonObject.GetObject();
double pages = groupObject1["total_page"].GetNumber();
double page = groupObject1["current_page"].GetNumber();
Buku file = new Buku();
file.PageNo = Convert.ToInt32(page);
file.Pages = Convert.ToInt32(pages);
halaman = file.Pages;
JsonArray jsonData1 = jsonObject["data"].GetArray();
foreach (JsonValue groupValue1 in jsonData1)
{
JsonObject groupObject2 = groupValue1.GetObject();
string title = groupObject2["judul"].GetString();
QuizHome quiz = new QuizHome();
quiz.Title = title;
quizhomedatasource.Add(quiz);
}
if (quizhomedatasource.Count < 0)
{
QuizList.Visibility = Visibility.Collapsed;
statusKosong.Visibility = Visibility.Visible;
}
}
}
I have a problem that if the loaded page exceeds the number of pages, then "statuskosong" is displayed. How can I prevent "statuskosong" from being displayed and data not being loaded again?
Note:
"statuskosong" is the text that will be displayed when data = 0
How can I prevent "statuskosong" from being displayed and data not being loaded again?
You can set statusKosong.Visibility to Collapsed every time you call the ProgressQuiz method, and then display it when it detects that quizhomedatasource.Count is 0.
By the way, your judgment condition at the end of the ProgressQuiz method is quizhomedatasource.Count < 0, this will not happen, you can change it to quizhomedatasource.Count == 0
Regarding avoiding repeated requests, we can add new judgment conditions in the Viewer_ViewChanged callback and set the return value of the ProgressQuiz method to Task:
private async void Viewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
ScrollViewer view = (ScrollViewer)sender;
double progress = view.VerticalOffset / view.ScrollableHeight;
if (progress > 0.7 && !endoflist && !incall)
{
incall = true;
while (offset < halaman)
{
await ProgressQuiz(++offset);
}
}
else
{
incall = false;
}
}
private async Task ProgressQuiz(int offset)
{
//code
}
By judging the incall and waiting for the ProgressQuiz method to execute, repeated requests can be avoided.
From the code you provided, you are creating an incrementally loaded list.
Obtaining the ScrollViewer in ListView through VisualTreeHelper and attaching events is a workaround, but UWP provides another way to solve the incremental loading problem more elegantly (From Windows Community Toolkit):
Incremental Loading Collection Helpers
I'm making a retro-style game with C# .NET-Framework, and for dialogue I'm using a for-statement, that prints my text letter by letter (like a typewriter-effect):
I'm working with different scenes, and I have a skip button (bottom right) that skips the current dialogue and passes to the next scene. My typewriter-effect automatically stops when all the text is displayed, but when I click on the skip button, it automatically skips to the next scene.
I would like it, when the typewriter is still active, and if I click on the skip button, that it first shows all the text, instead of skipping to the next scene.
So that it only skips to the next scene when all the text is displayed (automatically or manually).
This is the (working code) that I'm using for my typewriter method (+ variables):
public string FullTextBottom;
public string CurrentTextBottom = "";
public bool IsActive;
public async void TypeWriterEffectBottom()
{
if(this.BackgroundImage != null) // only runs on backgrounds that arent black
{
for(i=0; i < FullTextBottom.Length + 1; i++)
{
CurrentTextBottom = FullTextBottom.Substring(0, i); // updating current string with one extra letter
LblTextBottom.Text = CurrentTextBottom; // "temporarily place string in text box"
await Task.Delay(30); // wait for next update
#region checks for IsActive // for debugging only!
if(i < FullTextBottom.Length + 1)
{
IsActive = true;
Debug1.Text = "IsActive = " + IsActive.ToString();
}
if(CurrentTextBottom.Length == FullTextBottom.Length)
{
IsActive = false;
Debug1.Text = "IsActive = " + IsActive.ToString();
}
#endregion
}
}
}
And this is the code that I want to get for my skip button (named Pb_FastForward):
private void PbFastForward_Click(object sender, EventArgs e)
{
if( //typewriter is active)
{
//print all text into the textbox
}
else if( //all text is printed)
{
// skip to the next scene
}
}
But I don't know how to formulate the 2nd part of code. I've tried many different approaches, like using counters that increase on a buttonclick (and using that to check in an if-statement), and many different types of if-statements to see if the typewriter is still active or not, but I haven't got anything to work yet.
Edit
This is the sequence in which different components need to be loaded (on button click), which is related to the way different variables are updated:
Gamestate_Cycle() --> called for loading new scene.
FullTextBottom = LblTextBottom.Text --> called to refresh variables for typewriter.
TypeWriterEffectBottom() --> called to perform typewriter effect.
Avoid async void. Otherwise you can get an Exception that will break your game and you will not able to catch it.
Then use as less global variables in async methods as possible.
I suggest CancellationTokenSource as thread-safe way to stop the Type Writer.
public async Task TypeWriterEffectBottom(string text, CancellationToken token)
{
if (this.BackgroundImage != null)
{
Debug1.Text = "TypeWriter is active";
StringBuilder sb = new StringBuilder(text.Length);
try
{
foreach (char c in text)
{
LblTextBottom.Text = sb.Append(c).ToString();
await Task.Delay(30, token);
}
}
catch (OperationCanceledException)
{
LblTextBottom.Text = text;
}
Debug1.Text = "TypeWriter is finished";
}
}
Define CTS. It's thread-safe, so it's ok to have it in global scope.
private CancellationTokenSource cts = null;
Call TypeWriter from async method to be able to await it.
// set button layout as "Skip text" here
using (cts = new CancellationTokenSource())
{
await TypeWriterEffectBottom(yourString, cts.Token);
}
cts = null;
// set button layout as "Go to the next scene" here
And finally
private void PbFastForward_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts?.Cancel();
}
else
{
// go to the next scene
}
}
I pondered on your task a bit more and it occurred to me that it is a good job for the Rx.Net library.
An advantage of this approach is that you have less mutable state to care about and you almost don't need to think about threads, synchronization, etc.; you manipulate higher-level building blocks instead: observables, subscriptions.
I extended the task a bit to better illustrate Rx capabilities:
there are two pieces of animated text, each one can be fast-forwarded separately;
the user can fast-forward to the final state;
the user can reset the animation state.
Here is the form code (C# 8, System.Reactive.Linq v4.4.1):
private enum DialogState
{
NpcSpeaking,
PlayerSpeaking,
EverythingShown
}
private enum EventKind
{
AnimationFinished,
Skip,
SkipToEnd
}
DialogState _state;
private readonly Subject<DialogState> _stateChanges = new Subject<DialogState>();
Dictionary<DialogState, (string, Label)> _lines;
IDisposable _eventsSubscription;
IDisposable _animationSubscription;
public Form1()
{
InitializeComponent();
_lines = new Dictionary<DialogState, (string, Label)>
{
{ DialogState.NpcSpeaking, ("NPC speaking...", lblNpc) },
{ DialogState.PlayerSpeaking, ("Player speaking...", lblCharacter) },
};
// tick = 1,2...
IObservable<long> tick = Observable
.Interval(TimeSpan.FromSeconds(0.15))
.ObserveOn(this)
.StartWith(-1)
.Select(x => x + 2);
IObservable<EventPattern<object>> fastForwardClicks = Observable.FromEventPattern(
h => btnFastForward.Click += h,
h => btnFastForward.Click -= h);
IObservable<EventPattern<object>> skipToEndClicks = Observable.FromEventPattern(
h => btnSkipToEnd.Click += h,
h => btnSkipToEnd.Click -= h);
// On each state change animationFarames starts from scratch: 1,2...
IObservable<long> animationFarames = _stateChanges
.Select(
s => Observable.If(() => _lines.ContainsKey(s), tick.TakeUntil(_stateChanges)))
.Switch();
var animationFinished = new Subject<int>();
_animationSubscription = animationFarames.Subscribe(frame =>
{
(string line, Label lbl) = _lines[_state];
if (frame > line.Length)
{
animationFinished.OnNext(default);
return;
}
lbl.Text = line.Substring(0, (int)frame);
});
IObservable<EventKind> events = Observable.Merge(
skipToEndClicks.Select(_ => EventKind.SkipToEnd),
fastForwardClicks.Select(_ => EventKind.Skip),
animationFinished.Select(_ => EventKind.AnimationFinished));
_eventsSubscription = events.Subscribe(e =>
{
DialogState prev = _state;
_state = prev switch
{
DialogState.NpcSpeaking => WhenSpeaking(e, DialogState.PlayerSpeaking),
DialogState.PlayerSpeaking => WhenSpeaking(e, DialogState.EverythingShown),
DialogState.EverythingShown => WhenEverythingShown(e)
};
_stateChanges.OnNext(_state);
});
Reset();
}
private DialogState WhenEverythingShown(EventKind _)
{
Close();
return _state;
}
private DialogState WhenSpeaking(EventKind e, DialogState next)
{
switch (e)
{
case EventKind.AnimationFinished:
case EventKind.Skip:
{
(string l, Label lbl) = _lines[_state];
lbl.Text = l;
return next;
}
case EventKind.SkipToEnd:
{
ShowFinalState();
return DialogState.EverythingShown;
}
default:
throw new NotSupportedException($"Unknown event '{e}'.");
}
}
private void ShowFinalState()
{
foreach ((string l, Label lbl) in _lines.Values)
{
lbl.Text = l;
}
}
private void Reset()
{
foreach ((_, Label lbl) in _lines.Values)
{
lbl.Text = "";
}
_state = DialogState.NpcSpeaking;
_stateChanges.OnNext(_state);
}
protected override void OnClosed(EventArgs e)
{
_eventsSubscription?.Dispose();
_animationSubscription?.Dispose();
base.OnClosed(e);
}
private void btnReset_Click(object sender, EventArgs e)
{
Reset();
}
I adjusted your code a little bit to achieve your goal. I'm not sure it's the best way to do it, but it should work.
public async void TypeWriterEffectBottom()
{
if(this.BackgroundImage == null)
{
return;
}
IsActive = true;
for(i=0; i < FullTextBottom.Length && IsActive; i++)
{
CurrentTextBottom = FullTextBottom.Substring(0, i+1);
LblTextBottom.Text = CurrentTextBottom;
await Task.Delay(30);
Debug1.Text = "IsActive = " + IsActive.ToString();
}
IsActive = false;
}
private void PbFastForward_Click(object sender, EventArgs e)
{
if(IsActive)
{
LblTextBottom.Text = FullTextBottom;
IsActive = false;
return;
}
// IsActive == false means all text is printed
// skip to the next scene
}
UPD: Just noticed that Hans Kesting has suggested pretty much exactly this in his comment.
You write what skip / forward button does, so you control it. Just have a check if the length of written text is equal to text that supposed to be written and if yes move as usual if not just display the text in full have delay to be read and move on
I have some bugs that need fixing, one of them involves an out of memory error.
Does anyone know how to do this properly? Thanks, I don't want it to be too messy, or too complicated. I just want to treat a new image as a buffer to render another image to (because of positional changes), and do it via a background thread. Not the UI thread (Too slow likely).
I get out of memory errors, and such. Also not able to access members of Form1 from within the thread function (images and the like throw access errors such as "Object already in use")
Here is my code:
System.Threading.Thread t;
public Image b;
public Bitmap c;
public Bitmap d;
public Bitmap e;
public Bitmap bg;
public Bitmap spr;
int spritex = 0;
int spritey = 0;
int spritedir = 1;
public Form1()
{
InitializeComponent();
Text = "Escape The Hypno Mansion!!".ToString();
t = new System.Threading.Thread(DoThisAllTheTime);
t.Start();
textBox1.Text = "Press Begin button to start!";
pictureBox1.Image = Image.FromFile(#"Images\introgirl.jpg");
b = new Bitmap(#"Images\introgirl.jpg");
c = new Bitmap(#"Images\sprite.png");
var graphics = Graphics.FromImage(b);
Pen blackpen = new Pen(Color.Black, 3);
graphics.DrawLine(blackpen, 0, 0, 100, 100);
graphics.DrawImage(c, new Point(500, 500));
pictureBox1.Image = b;
//pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
}
public void DoThisAllTheTime()
{
while (true)
{
Point p = new Point(spritex, spritey);
bg = new Bitmap(#"Images\test.bmp");
spr = new Bitmap(#"Images\sprite.png");
using (var graphics = Graphics.FromImage(bg))
{
graphics.DrawImage(spr, p);
}
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
pictureBox1.Image = bg;
pictureBox1.Invalidate();
if (spritedir == 1) { spritex += 5; }
if (spritedir == 2) { spritex -= 5; }
if (spritex < 0) { spritex = 0; spritedir = 1; }
if (spritex > 700) { spritex = 700; spritedir = 2; }
}
}
The reason you can't change the image in your picturebox is because the thread that created the image is not the thread that created the picturebox.
In a debugger you can check this by asking the picturebox for InvokeRequired (function Control.IsInvokeRequired) just before changing the function.
So let's rewrite your function and show that modern classes Like Task are much easier to use the your thread.
I'll start your task when the form is loading, and try to stop it when the form is closing.
private Task myTask = null;
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private void OnFormLoading(object sender, EventArgs e)
{
// Start the task
this.myTask = Task.Run( () => DoMyWork(this.cancellationTokenSource.Token));
}
private void OnFormClosing(object sender, FormClosingEventArgs e)
{
// if the Task is still running, ask it to stop itself:
if (myTask != null && !myTask.IsCompleted)
{ // ask the task to stop and wait until it is completed:
this.cancellationTokenSource.Cancel();
// all Tokens extractes from this source will get state CancellationRequested
// wait maximum 5 seconds until the task is completed:
this.UseWaitCursor = true;
this.myTask.Wait(TimeSpan.FromSeconds(5));
this.UseWaitCursor = false;
// cancel closing if the task is still not completed
e.Cancel = !this.myTask.Completed;
}
}
Now the function DoMyWork:
private void DoMyWork(CancellationToken cancellationToken)
{
// Do the same as in your DoThisAllTheTime
// except that you regularly check cancellationToken.IsCancelRequested:
while(!cancellationToken.IsCancelRequested)
{
// calculate the image to display
var imageToDisplay = ...
this.DisplayImage(imageToDisplay);
}
}
void DisplayImage(Image imageToDisplay)
{
if (this.pictureBox1.InvokeRequired)
{
this.Invoke(new MethodInvoker( () => this.DisplayImage(imageToDisplay)));
}
else
{
this.PictureBox1.Image = imageToDisplay;
}
}
See:
How to cancel a Task and its children
Use InvokeRequired with lambda expression
Dispose every disposable instances before the loop ends. Your memory leak is related with disposable items not being cleaned from memory, so you'll eventually run out of memory in your infinite loop.
At the very least, you'll want to dispose both bitmaps at the end of the loop:
bg = new Bitmap(#"Images\test.bmp");
spr = new Bitmap(#"Images\sprite.png");
What I need is to set the Position property on WPF's MediaElement control. But when I play the video, (either via Play() or through some kind of animation on Opacity) it is is not working at all. It is showing 00:00:00 time, but I would expect it to be set to 00:00:05.
I have hard-coded Position value and it is not working at all.
Just In case I am going to put all my code I have so u can see the whole animation logic.
Any clue?
public partial class CrossFadeTransition : UserControl
{
DoubleAnimation _doubleAnimationFrontPlayer = new DoubleAnimation();
DoubleAnimation _doubleAnimationBackPlayer = new DoubleAnimation();
Storyboard _sb1 = new Storyboard();
Storyboard _sb2 = new Storyboard();
public TimeSpan Duration = TimeSpan.FromSeconds(2);
public Dictionary<string, Position2D> PlayerPosition { set; get; }
public CrossFadeTransition()
{
InitializeComponent();
Player1.LoadedBehavior = MediaState.Manual;
Player1.UnloadedBehavior = MediaState.Stop;
Player2.LoadedBehavior = MediaState.Manual;
Player2.UnloadedBehavior = MediaState.Stop;
PlayerPosition = new Dictionary<string, Position2D>();
PlayerPosition.Add("Player1", Position2D.Front);
PlayerPosition.Add("Player2", Position2D.Back);
}
Position2D positionPlayer1;
Position2D positionPlayer2;
public void Stop()
{
if (Player1.IsEnabled)
Player1.Stop();
if (Player2.IsEnabled)
Player2.Stop();
}
public void Start(Uri uri, int? position)
{
try
{
positionPlayer1 = PlayerPosition["Player1"];
positionPlayer2 = PlayerPosition["Player2"];
if (positionPlayer1 == Library.Position2D.Back)
{
Player1.Source = uri;
if (Player1.IsEnabled)
Player1.Stop();
Player1.Position = TimeSpan.FromSeconds(5); // IT IS NOT WORKING !!!
Player1.Play();
}
if (positionPlayer2 == Library.Position2D.Back)
{
Player2.Source = uri;
if (Player2.IsEnabled)
Player2.Stop();
Player2.Position = TimeSpan.FromSeconds(5); // IT IS NOT WORKING !!!
Player2.Play();
}
_sb1.Children.Clear();
_sb2.Children.Clear();
if (positionPlayer1 == Position2D.Front)
{
_doubleAnimationFrontPlayer.From = 1;
_doubleAnimationFrontPlayer.To = 0;
_doubleAnimationFrontPlayer.Duration = new Duration(Duration);
PlayerPosition["Player1"] = Position2D.Back;
}
else if (positionPlayer1 == Position2D.Back)
{
_doubleAnimationFrontPlayer.From = 0;
_doubleAnimationFrontPlayer.To = 1;
_doubleAnimationFrontPlayer.Duration = new Duration(Duration);
PlayerPosition["Player1"] = Position2D.Front;
}
if (positionPlayer2 == Position2D.Front)
{
_doubleAnimationBackPlayer.From = 1;
_doubleAnimationBackPlayer.To = 0;
_doubleAnimationBackPlayer.Duration = new Duration(Duration);
PlayerPosition["Player2"] = Position2D.Back;
}
else if (positionPlayer2 == Position2D.Back)
{
_doubleAnimationBackPlayer.From = 0;
_doubleAnimationBackPlayer.To = 1;
_doubleAnimationBackPlayer.Duration = new Duration(Duration);
PlayerPosition["Player2"] = Position2D.Front;
}
_sb1.Children.Add(_doubleAnimationFrontPlayer);
Storyboard.SetTargetProperty(_doubleAnimationFrontPlayer, new PropertyPath("(Panel.Opacity)"));
Storyboard.SetTarget(_doubleAnimationFrontPlayer, Player1);
_sb1.Completed += _sb1_Completed;
_sb1.Begin();
//
_sb2.Children.Add(_doubleAnimationBackPlayer);
Storyboard.SetTargetProperty(_doubleAnimationBackPlayer, new PropertyPath("(Panel.Opacity)"));
Storyboard.SetTarget(_doubleAnimationBackPlayer, Player2);
_sb2.Completed += _sb2_Completed;
_sb2.Begin();
}
catch (Exception)
{
throw;
}
}
void _sb2_Completed(object sender, EventArgs e)
{
_sb2.Completed -= _sb2_Completed;
Debug.WriteLine("Player2 COMPLETED " + DateTime.Now.TimeOfDay);
if (positionPlayer2 == Position2D.Front)
{
Player2.Stop();
}
}
void _sb1_Completed(object sender, EventArgs e)
{
_sb1.Completed -= _sb1_Completed;
Debug.WriteLine("Player1 COMPLETED " + DateTime.Now.TimeOfDay);
if (positionPlayer1 == Position2D.Front)
{
Player1.Stop();
}
}
}
I have tried to do like
Player2.Play();
Player2.Pause();
Player2.Position = TimeSpan.FromSeconds(5); // IT IS NOT WORKING !!!
Player2.Play();
But no joy...
I found some solution. It is not completely ideal because sometimes it shows the first video frame, but at least it is working.
We need those events where we can apply new Position.
void Player2_MediaOpened(object sender, RoutedEventArgs e)
{
Player2.Position = new TimeSpan(0, 0, 7);
}
void Player1_MediaOpened(object sender, RoutedEventArgs e)
{
Player1.Position = new TimeSpan(0, 0, 7);
}
We have to close Mediaelement like this.
Player1.Stop();
Player1.Close();
Player1.Source = uri;
Player1.Play();
Have fun! (beer)