Windows Phone - MediaElement not working anymore after navigation - silverlight

I have a problem with the MediaElement in my windows phone (8) application. It's working fine until I navigate to some other page and come back on the page containing the MediaElement.
I have the following code to play a stream coming from Bing Translator APIs:
private void TranslationService_SpeakComplete(object sender, SpeakCompleteEventArgs e)
{
var stream = e.Stream;
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
try
{
MediaElement.Stop();
MediaElement.Source = null;
string filename = "FlipNLearnItAudio";
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
{
bool fileExists = isf.FileExists(filename);
if (fileExists)
{
isf.DeleteFile(filename);
}
var isfs = isf.CreateFile(filename);
using (isfs)
{
Helpers.SaveFile(stream, isfs);
isfs.Position = 0;
MediaElement.AutoPlay = true;
MediaElement.SetSource(isfs);
}
}
}
catch (Exception ex)
{
//TODO: log exception
}
});
}
The biggest problem is that there's no exception.... the "MediaElement.SetSource(isfs);" is called normally without any exception!
Do you have any idea of what can happen?
Thanks for any help!
Bastien

Ok, I find a solution to my problem. I put the MediaElement in the App.xaml resources and now it's working fine...
But it doesn't explain why it was not working with the MediaElement in the MainPage.xaml.
If somebody has the answer, I would be very interested to hear it ;-).

Related

Convert FileInfo read file code to maui equivalent

I know I should know how to do this, but I don't.
I have some code (from a brilliant map routing packing called Itinero http://docs.itinero.tech/index.html) that reads in a a routerDb file.
It works great (in windows) if I use a fully qualified path as it is absolute, but I have moved the file into the Resources.Raw folder and want to read it properly.
The working code
using (var stream = new FileInfo(#"/path/to/my/file/gb.routerdb").OpenRead())
{
routerDb = RouterDb.Deserialize(stream);
}
How can I use the Maui approach to do the same thing? Such as or simpler
using (var stream = new xxx("gb.routerdb").xxxx)
{
routerDb = RouterDb.Deserialize(stream);
}
I'm looking at https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/storage/file-system-helpers?tabs=windows but I don't get it :(
Thanks for any help.
G.
As document Platform differences of File system helpers mentioned, you can use method FileSystem.OpenAppPackageFileAsync to access file in the Resources\Raw folder as a MauiAsset.
Besides, if you want to access the path of the items in folder Raw, you can follow up the unknown issue about this:
https://github.com/dotnet/maui/issues/7943 .
Update:
As a test, I created a simple html(test.html) in folder Resource\Raw, and set BuildAction to MauiAsset.And I used the following code to read the html, it works well on my side(I tested on android device).
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async Task InitAsync()
{
string filePath = "test.html";
#if WINDOWS
var stream = await FileSystem.OpenAppPackageFileAsync("Assets/" + filePath);
#else
var stream = await FileSystem.OpenAppPackageFileAsync(filePath);
#endif
if (stream != null)
{
string s = (new System.IO.StreamReader(stream)).ReadToEnd();
this.MyWebView.Source = new HtmlWebViewSource { Html = s };
}
}
private void OnClicked(object sender, EventArgs e)
{
InitAsync();
}
}
MainPage.xaml
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<WebView x:Name="MyWebView"
/>
<Button
x:Name="CounterBtn"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
Well I certainly appreciate the example code, but for some reason it didn't work for me, but what did work based on your code (#Jessie Zhang) was:
var stream = await FileSystem.OpenAppPackageFileAsync("gb.routerdb");
if (stream != null)
{
routerDb = RouterDb.Deserialize(stream);
}
The conditional didn't work and I'm not sure why, but I'm only using windows so I'll check that out later. Thanks

Why is my GUI freezing?

I'm new in TPL world, and I did that code:
var myItems = myWpfDataGrid.SelectedItems;
this.Dispatcher.BeginInvoke(new Action(() =>
{
var scheduler = new LimitedConcurrencyLevelTaskScheduler(5);
TaskFactory factory = new TaskFactory(scheduler);
foreach (MyItem item in myItems)
{
Task myTask = factory.StartNew(() =>
DoLoooongWork(item)
).ContinueWith((t) =>
{
Debug.WriteLine(t.Exception.Message);
if (t.Exception.InnerException != null)
{
Debug.WriteLine(t.Exception.InnerException.Message);
}
},
TaskContinuationOptions.OnlyOnFaulted);
}
}), null);
The only one access to gui is "var myItems = myWpfDataGrid.SelectedItems;"
and it is read only! The function "DoLoooongWork()" does access to serial ports, etc. It's a separated SDK function that doesn't access the GUI. I know that "Dispatcher.BeginInvoke" is a bit redundant, but I don't know what I can do, or what I'm doing wrong. The only reason to this code is to free the GUI while "DoLoooongWork()" executes, but the GUI is frozen!
What's wrong with that code?
edit
Thanks to #Euphoric help, I discovered the problem that is similar to that post:
COM Interop hang freezes entire COM system. How to cancel COM call
I presume some objects inside DoLoooongWork require thread affinity and message pumping. Try my ThreadWithAffinityContext and see if helps, use it like this:
private async void Button_Click(object sender, EventArgs e)
{
try
{
using (var staThread = new Noseratio.ThreadAffinity.ThreadWithAffinityContext(
staThread: true, pumpMessages: true))
{
foreach (MyItem item in myItems)
{
await staThread.Run(() =>
{
DoLoooongWork(item);
}, CancellationToken.None);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
More info about ThreadWithAffinityContext.
[UPDATE] You mentioned in the comments that the code inside DoLoooongWork looks like this:
zkemkeeper.CZKEM axCZKEM1 = new zkemkeeper.CZKEM();
axCZKEM1.Connect_Net(ip, port);
I never heard of "zkemkeeper" before, but I did a brief search and found this question. Apparently, Connect_Net only establishes the connection and starts a session, while the whole communication logic happens asynchronously via some events, as that question suggests:
bIsConnected = axCZKEM1.Connect_Net("192.168.0.77", Convert.ToInt32("4370"));
if (bIsConnected == true)
{
iMachineNumber = 1;
if (axCZKEM1.RegEvent(iMachineNumber, 65535))
{
this.axCZKEM1.OnFinger += new kemkeeper._IZKEMEvents_OnFingerEventHandler(axCZKEM1_OnFinger);
this.axCZKEM1.OnVerify += new zkemkeeper._IZKEMEvents_OnVerifyEventHandler(axCZKEM1_OnVerify);
// ...
}
}
That would be a whole different story. Leave a comment if that's the case and you're still interested in some solution.
I had a hunch that something working with serial port would try to use application's event loop to do it's work. So it actually bypasses the whole dispatcher and thread system and blocks the application. I'm not experienced in this field so I don't know how to solve it, but this is different question.

Download a file through the WebBrowser control

I have a WebBrowser control on a form, but for the most part it remains hidden from the user. It is there to handle a series of login and other tasks. I have to use this control because there is a ton of Javascript that handles the login. (i.e., I can't just switch to a WebClient object.)
After hopping around a bit, we end up wanting to download a PDF file. But instead of downloading, the file is displayed within the webBrowser control, which the user can not see.
How can I download the PDF instead of having it load in the browser control?
Add a SaveFileDialog control to your form, then add the following code on your WebBrowser's Navigating event:
private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
if (e.Url.Segments[e.Url.Segments.Length - 1].EndsWith(".pdf"))
{
e.Cancel = true;
string filepath = null;
saveFileDialog1.FileName = e.Url.Segments[e.Url.Segments.Length - 1];
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
filepath = saveFileDialog1.FileName;
WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
client.DownloadFileAsync(e.Url, filepath);
}
}
}
//Callback function
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
MessageBox.Show("File downloaded");
}
Source: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/d338a2c8-96df-4cb0-b8be-c5fbdd7c9202
The solution I ended up using:
I did everything else as-needed to get the URL where it needed to go. Knowing that all of the login information, required settings, viewstates, etc. were stored in the cookies, I was finally able to grab the file using a hybrid of the web control to navigate then the WebClient object to actually snag the file bytes.
public byte[] GetPDF(string keyValue)
{
DoLogin();
// Ask the source to generate the PDF. The PDF doesn't
// exist on the server until you have visited this page
// at least ONCE. The PDF exists for five minutes after
// the visit, so you have to snag it pretty quick.
LoadUrl(string.Format(
"https://www.theMagicSource.com/getimage.do?&key={0}&imageoutputformat=PDF",
keyValue));
// Now that we're logged in (not shown here), and
// (hopefully) at the right location, snag the cookies.
// We can use them to download the PDF directly.
string cookies = GetCookies();
byte[] fileBytes = null;
try
{
// We are fully logged in, and by now, the PDF should
// be generated. GO GET IT!
WebClient wc = new WebClient();
wc.Headers.Add("Cookie: " + cookies);
string tmpFile = Path.GetTempFileName();
wc.DownloadFile(string.Format(
"https://www.theMagicSource.com/document?id={0}_final.PDF",
keyValue), tmpFile);
fileBytes = File.ReadAllBytes(tmpFile);
File.Delete(tmpFile);
}
catch (Exception ex)
{
// If we can't get the PDF here, then just ignore the error and return null.
throw new WebScrapePDFException(
"Could not find the specified file.", ex);
}
return fileBytes;
}
private void LoadUrl(string url)
{
InternalBrowser.Navigate(url);
// Let the browser control do what it needs to do to start
// processing the page.
Thread.Sleep(100);
// If EITHER we can't continue OR
// the web browser has not been idle for 10 consecutive seconds yet,
// then wait some more.
// ...
// ... Some stuff here to make sure the page is fully loaded and ready.
// ... Removed to reduce complexity, but you get the idea.
// ...
}
private string GetCookies()
{
if (InternalBrowser.InvokeRequired)
{
return (string)InternalBrowser.Invoke(new Func<string>(() => GetCookies()));
}
else
{
return InternalBrowser.Document.Cookie;
}
}
bool documentCompleted = false;
string getInnerText(string url)
{
documentCompleted = false;
web.Navigate(url);
while (!documentCompleted)
Application.DoEvents();
return web.Document.Body.InnerText;
}
private void web_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
documentCompleted = true;
}

Silverlight WebClient class returning empty string

I am trying to make a basic 'Silverlight Class Library' in Silverlight 4 to return basic Facebook Information using Facebook's Graph API, but I am only getting empty strings being returned.
I am using the following code:
string _Response = "";
public string GetFacebookMe(string access_token)
{
WebClient facebookClient = new WebClient();
facebookClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(facebookClientDownloadStringCompleted);
facebookClient.DownloadStringAsync(new Uri("https://graph.facebook.com/me" + "?access_token=" + access_token));
string ret = _Response;
return ret;
}
private void facebookClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
_Response = e.Result;
}
else
{
_Response = e.Error.Message;
}
}
I tried while debugging to init _Response to the value "Default", and the string "Default" was consequently being returned. I have been messing with this for a while and I'm not sure where I'm going wrong.
Thanks in advance!
It means a lot of effort to use directly WebClient. When using Silverlight and .Net Framework 4.0 you may use Facebook C# SDK at Codeplex
Usage of the SDK is excellently covered in this blog by Prabir Shrestha

Windows Phone 7 : Silverlight and playing Music

I have built a simple game in WP7 and I am trying to add background music to it using MediaPlayer. The problem is it just bombs with
{"An unexpected error has occurred."} System.Exception {System.InvalidOperationException}
Code
try
{
MediaPlayer.Stop();
// Timer to run the XNA internals (MediaPlayer is from XNA)
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(33);
dt.Tick += delegate { try { FrameworkDispatcher.Update(); } catch { } };
dt.Start();
Uri pathToFile = new Uri("Audio/music.m4a", UriKind.Relative);
Song playingSong = Song.FromUri("Music", pathToFile);
MediaPlayer.Play(playingSong);
}
catch (Exception e)
{
musicFailed = true;
Console.WriteLine("Exception: {0}", e.ToString());
MessageBox.Show("Warning, music failed to play however you can still continue to play your game.");
}
}
I tried a few tweaks, converting file to mp3, different paths etc. The file is marked for copy always and content type I also tried removing the Dispatcher as dont know what that is for.
If you can convert your audio files to WAV format, you can try using the XNA SoundEffect and SoundEffectInstance classes:
SoundEffect se = SoundEffect.FromStream(isolatedStorageFileStream);
SoundEffectInstance sei = se.CreateInstance();
sei.Play();
For this to work, you will need to reference the XNA library (Microsoft.XNA.Framework) and initialize the framework in this way:
App.xaml:
<Application>
<Application.ApplicationLifetimeObjects>
<local:XNAFrameworkDispatcherService />
...
</Application.ApplicationLifetimeObjects>
</Application>
And create this class somewhere in the app namespace ("local" in the previous xaml references this namespace):
public class XNAFrameworkDispatcherService : IApplicationService
{
private DispatcherTimer frameworkDispatcherTimer;
public XNAFrameworkDispatcherService()
{
this.frameworkDispatcherTimer = new DispatcherTimer();
this.frameworkDispatcherTimer.Interval = TimeSpan.FromTicks(333333);
this.frameworkDispatcherTimer.Tick += frameworkDispatcherTimer_Tick;
FrameworkDispatcher.Update();
}
void frameworkDispatcherTimer_Tick(object sender, EventArgs e) { FrameworkDispatcher.Update(); }
void IApplicationService.StartService(ApplicationServiceContext context) { this.frameworkDispatcherTimer.Start(); }
void IApplicationService.StopService() { this.frameworkDispatcherTimer.Stop(); }
}
Your problem is that your timer maybe not fired the first tick and FrameworkDispatcher.Update() not ran. Then Play throws a System.InvalidOperationException.
Better dispatch XNA Events global in your Application.
Complete Guide: XNAAsyncDispatcher

Resources