Trouble with MediaElement in Silverlight - silverlight

I'm having difficulty with the MediaElement control in Silverlight for Windows Phone 7. My goal is to play two tones when the user presses a button. The method of doing this that I came up with is to have a MediaElement for each tone. (Is there possibly a better way?)
<phone:PhoneApplicationPage
x:Class="MediaElementTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<StackPanel x:Name="LayoutRoot" Background="Transparent">
<MediaElement
x:Name="firstTone"
MediaEnded="firstTone_MediaEnded"
Source="{Binding FirstTone}" />
<MediaElement
x:Name="secondTone"
Source="{Binding SecondTone}" />
<Button Content="Play" Click="Button_Click" />
</StackPanel>
</phone:PhoneApplicationPage>
Code-behind:
public partial class MainPage : PhoneApplicationPage
{
public Uri FirstTone
{
get
{
return new Uri("A.mp3", UriKind.Relative);
}
}
public Uri SecondTone
{
get
{
return new Uri("B.mp3", UriKind.Relative);
}
}
public MainPage()
{
InitializeComponent();
LayoutRoot.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
firstTone.Stop();
secondTone.Stop();
firstTone.Play();
}
private void firstTone_MediaEnded(object sender, RoutedEventArgs e)
{
secondTone.Play();
}
}
When I click the button, no tone plays. Why is this? What am I doing wrong?

Don't use MediaElement for this. Instead add a reference to Microsoft.Xna.Framework.dll (yes, even in a Silverlight Project) and then using SoundEffect.
Something Like:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using (var stream = TitleContainer.OpenStream("a.mp3"))
{
var effect = SoundEffect.FromStream(stream);
effect.Play();
// This will pause while the first sound plays
// so they don't play over each other but as it also blocks!
Thread.Sleep(effect.Duration);
}
using (var stream = TitleContainer.OpenStream("b.mp3"))
{
var effect = SoundEffect.FromStream(stream);
effect.Play();
}

The MediaElement is intended for playing one piece of media at a time. As others have mentioned, use the SoundEffect or SoundEffectInstance classes for simulaneous sounds. You can play up to 16 SoundEffects at a time.
Also, using MediaElement can cause troubles with certification if you aren't careful. If you set the MediaElement source in your XAML, for instance, it will cause any currently playing media on the phone to stop. Unless you ask for user permission to stop playing media you will fail certification.

You can get all sorts of weird things going with URI's and their locations relative to the Silverlight application; and data-bindings are notoriously difficult to get right. My recommendation would be to start troubleshooting this by hard-coding the absolute URI's to the MP3 files directly in the XAML. Once you've got that working, switch to relative URI's, and when that's working, switch to data-binding them to the code-behind (or ViewModel or whatever).
If you've got the file placed in Isolated Storage, something like this ought to work (haven't tested it on WP7):
var store = IsolatedStorageFile.GetUserStoreForApplication();
var iStream = store.OpenFile("a.mp3", System.IO.FileMode.Open,FileAccess.Read);
firstTone.SetSource(iStream);
firstTone.Play();

I don't know why you're hearing nothing at all, but if you want to hear two consecutive tones when the user clicks a button, why not just combine the tones ahead of time into a single file, and then just play that?

I've never been able to get multiple MediaElements to work on a phone app. I see the same problems as you, I can't play the media.
I believe that you can only have one active, but I haven't seen documention to that effect. The way I've worked around it is to have one MediaElement defined in App.XAML
<!--Application Resources-->
<Application.Resources>
<MediaElement
x:Key='mediaElement' AutoPlay='True' Source='/music/GroovinIntro.wav'/>
</Application.Resources>
and then expose it as a property on the Application class.
public MediaElement MediaElement
{
get
{
return this.Resources["mediaElement"] as MediaElement;
}
}
Then call the MediaElement like this.
App.Current.MediaElement.Source = new Uri(_musicLocation, UriKind.Relative);

Related

How can I showdialog on the mainwindow when it is inactive?

I am developing an app with WPF.
In my case, the users have 2 monitors. When he opens the app in the secondary screen, it will load data for nearly 5 seconds. During this period, he may turn to the primary screen for personal stuff, like visit Facebook or visit twitter.
After the data are loaded, a dialogbox should be prompted. What bothers me is that it often shows in the primary screen where he deals with personal stuff, not the secondary screen where he opens the app. The dialog window is supposed to show on the top of the app.
I am thinking it's because that the app is not active when the data are loaded. Do you guys have any idea?
I know that the MessageBox.Show() has a "owner" parameter can fix this. How can I automatically get the correct owner? I am using a PRISM pattern so that it's not easy for me to find the window as the owner.
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Delay(5000);
MessageBox.Show("Loaded!");
}
}
Yes the owner parameter should fix that. Regarding Prism, there are different approaches. The easiest for me was to use the Application.Current.MainWindow which refers to the main window of the application.
MessageBox.Show(Application.Current.MainWindow, "bla bla");
You could as well try to resolve your Shell window via your IoC container and take this as owner.
You could try to use MessageBox from Extended WPF Toolkit Community Edition (NuGet Package Extended.Wpf.Toolkit):
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="MainWindow" Height="350" Width="525">
<Grid>
<xctk:WindowContainer>
<xctk:ChildWindow Height="100" Width="250" Left="10" Top="10" Name="chWindow">
<TextBlock Text="Hello World ..." />
</xctk:ChildWindow>
<xctk:MessageBox Height="100" Width="250" Left="10" Top="100" Name="msgBox">
</xctk:MessageBox>
</xctk:WindowContainer>
<Button Name="btnTst" Click="btnTst_Click" Content="Test" Width="65" Height="30" HorizontalAlignment="Left" VerticalAlignment="Bottom" />
</Grid>
</Window>
And:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void btnTst_Click(object sender, RoutedEventArgs e)
{
await Task.Delay(5000);
//this.chWindow.Show();
this.msgBox.ShowMessageBox();
}
}
MessageBox is inside Window…

Capture screen from a Silverlight Application to be included in a printed report

I want to include in a report (e.g., written in a word file) a screen shot from a Silverlight application. The problem is that the image resolution is very bad and the printed result is unsatisfactory when using the print-screen button.
Is there a way to create better screen captures?
Note: I have access to the source code and i can modify the application if needed. The app is written in Silverlight version 4.
Here's a sample code I use to capture a UIElement and save it as a .png image. The resolution is the size of the displayed element at the time you capture it.
I use PngEncoder from the ImageTools library. You need to add references to it in your project.
Here's a simple XAML page displaying a blue square with green borders, and a button to capture it:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot">
<StackPanel>
<Border x:Name="myElement"
Width="200"
Height="200"
BorderBrush="Green"
Background="AliceBlue"
BorderThickness="2" />
<Button Click="SaveScreenShot"
Content="Capture"/>
</StackPanel>
</Grid>
</UserControl>
And here's the SaveScreenShot method:
private void SaveScreenShot(object sender, RoutedEventArgs e)
{
//Capture the element
var screenShot = new WriteableBitmap((UIElement)myElement, null);
var encoder = new PngEncoder();
SaveFileDialog saveDialog = new SaveFileDialog();
saveDialog.Filter = "Picture Files (*.png)|*.png";
bool? result = saveDialog.ShowDialog();
if (result.Value)
{
using (Stream saveStream = saveDialog.OpenFile())
{
encoder.Encode(screenShot.ToImage(), saveStream);
}
}
}
When clicking the button, it will open a dialog for you to save the captured image as a file. You cannot use the clipboard as Silverlight doesn't allow to put images in it (See the "remarks" section of the MSDN page)
So if you add the button with the associated method in your page, and change the "myElement" in the callback to the name of the element you want to capture, you should be good.

Playing video in Silverlight with Source binding

I am trying to make a WMV video to play in Silverlight MediaElement. It works in this XAML code:
<MediaElement
x:Name="VideoElement"
Stretch="Fill"
Source=""http://ecn.channel9.msdn.com/o9/pdc09/wmv/CL20.wmv""
Grid.Row="0"
Grid.Column="0"
AutoPlay="True"/>
But when I try to bind the source to some property in my code like this:
<MediaElement
x:Name="VideoElement"
Stretch="Fill"
Source="{Binding VidPath}"
Grid.Row="0"
Grid.Column="0"
AutoPlay="True"/>
Where VidPath is:
public Uri VidPath
{
get
{
return new Uri("http://ecn.channel9.msdn.com/o9/pdc09/wmv/CL20.wmv", UriKind.Absolute);
}
set;
}
It doesn't work. Can you help me figure out why?
First, I'll assume this is an out of browser application with full trust, otherwise cross-domain policy restrictions would prevent the MediaElement from playing that video either way.
Given that, there's nothing wrong with the code you've supplied, but I have a hunch the DataContext of the page that contains your MediaPlayer isn't set correctly. If you put a breakpoint in the getter for VidPath, does it ever get hit? I'm betting no.
Whatever object contains your "VidPath" property, you want to make sure that's the DataContext of your page. E.g. if you just put VidPath as a property in the code-behind, you can add this to the constructor:
this.DataContext = this;

WPF Top Level Window and Child Window on different Dispatchers?

OK, here's what I am trying to do. I have a nasty WPF control (that I am forced to use) that requires a parent with an HWND (because it contains some Windows.Forms stuff) and can be very unresponsive at times. This causes my WPF app to also get bogged down and become unresponsive (less responsive would be more accurate). I would like to move this control onto its own Dispatcher so that my app's Dispatcher isn't tied up when the control becomes unresponsive.
I have tried to create a Window derived class to host the Control. I launch it on an STA thread and do a Dispatcher.Run(). So far all is good. The badness happens when I try to add that Window derived class to the topmost Grid inside my app. I get the "The calling thread cannot access this object because another thread owns it." exception.
XAML for my app:
<Window
x:Class="MyAppWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid
ClipToBounds="True"
x:Name="mainGrid"
Grid.Row="1">
</Grid>
</Window>
myApp.cs
public class MyAppWindow :Window
{
CrapControlWindow crapControlWindow;
Dispatcher crapControlWindowDispatcher;
Dispatcher myAppDispatcher;
public MyAppWindow()
{
myAppDispatcher = Dispatcher.CurrentDispatcher;
Thread launchThread = new Thread(new ThreadStart(crapControlHostLauncher));
launchThread.IsBackground = true;
launchThread.ApartmentState = ApartmentState.STA;
launchThread.Start();
}
public Initialize()
{
mainGrid.Children.Add(crapControlWindow); <--- Fails Here
}
void crapControlHostLauncher()
{
crapControlWindowDispatcher = Dispatcher.CurrentDispatcher;
Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate()
{
crapControlWindow = new crapControlWindow();
});
Dispatcher.Run();
}
}
XAML for child window:
<Window x:Class="CrapControlWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Crappy Control Window" Height="300" Width="300">
<Grid x:Name="mainGrid">
<CrapControl
Grid.Row="1"
Name="crapControl1"
ClipToBounds="True"
Margin="0,4,0,4" />
</Grid>
</Window>
I've tried various permutations of the failing line - crapControlWindowDispatcher.Invoke, myAppDispatcher.Invoke etc. NADA! Same exception...
So lay it on me - am I crazy? other strategies? Can I make this work?
Thanks for your help!
You've inherited from Window but are then attempting to host that Window within a Grid. That makes no sense whether it has the same Dispatcher or not, since a Window is a top-level element. You can have separate windows, each with their own Dispatcher, but they need to be displayed as separate windows. I know of no way to integrate a control with its own Dispatcher into another WPF control with a different Dispatcher.
For an example of separate windows, each with their own dispatcher, see here.

How to build a generic/re-usable modal dialog for WPF following MVVM

I would like to build a generic/re-usable modal dialog that I can use in our WPF (MVVM) - WCF LOB application.
I have a Views and associated ViewModels that I would like to display using dialogs. Bindings between Views and ViewModels are done using Type-targeted DataTemplates.
Here are some requirements that I have been able to draft:
I prefer this to be based on a Window instead of using Adorners and controls that act like a modal dialog.
It should get its minimum size from the content.
It should center on the owner window.
The window must not show the Minimize and Maximize buttons.
It should get its title from the content.
What is the best way to do this?
I usually deal with this by injecting this interface into the appropriate ViewModels:
public interface IWindow
{
void Close();
IWindow CreateChild(object viewModel);
void Show();
bool? ShowDialog();
}
This allows the ViewModels to spaw child windows and show them modally on modeless.
A reusable implementation of IWindow is this:
public class WindowAdapter : IWindow
{
private readonly Window wpfWindow;
public WindowAdapter(Window wpfWindow)
{
if (wpfWindow == null)
{
throw new ArgumentNullException("window");
}
this.wpfWindow = wpfWindow;
}
#region IWindow Members
public virtual void Close()
{
this.wpfWindow.Close();
}
public virtual IWindow CreateChild(object viewModel)
{
var cw = new ContentWindow();
cw.Owner = this.wpfWindow;
cw.DataContext = viewModel;
WindowAdapter.ConfigureBehavior(cw);
return new WindowAdapter(cw);
}
public virtual void Show()
{
this.wpfWindow.Show();
}
public virtual bool? ShowDialog()
{
return this.wpfWindow.ShowDialog();
}
#endregion
protected Window WpfWindow
{
get { return this.wpfWindow; }
}
private static void ConfigureBehavior(ContentWindow cw)
{
cw.WindowStartupLocation = WindowStartupLocation.CenterOwner;
cw.CommandBindings.Add(new CommandBinding(PresentationCommands.Accept, (sender, e) => cw.DialogResult = true));
}
}
You can use this Window as a reusable host window. There's no code-behind:
<Window x:Class="Ploeh.Samples.ProductManagement.WpfClient.ContentWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self="clr-namespace:Ploeh.Samples.ProductManagement.WpfClient"
xmlns:pm="clr-namespace:Ploeh.Samples.ProductManagement.PresentationLogic.Wpf;assembly=Ploeh.Samples.ProductManagement.PresentationLogic.Wpf"
Title="{Binding Path=Title}"
Height="300"
Width="300"
MinHeight="300"
MinWidth="300" >
<Window.Resources>
<DataTemplate DataType="{x:Type pm:ProductEditorViewModel}">
<self:ProductEditorControl />
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" />
</Window>
You can read more about this (as well as download the full code sample) in my book.
I'm answering my own question to help others find all answers I struggled to find in one place. What above seems like a straight forward problem, actually presents multiple problems that I hope to answer sufficiently below.
Here goes.
Your WPF window that will serve as the generic dialog can look something like this:
<Window x:Class="Example.ModalDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ex="clr-namespace:Example"
Title="{Binding Path=mDialogWindowTitle}"
ShowInTaskbar="False"
WindowStartupLocation="CenterOwner"
WindowStyle="SingleBorderWindow"
SizeToContent="WidthAndHeight"
ex:WindowCustomizer.CanMaximize="False"
ex:WindowCustomizer.CanMinimize="False"
>
<DockPanel Margin="3">
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button Content="Cancel" IsCancel="True" Margin="3"/>
<Button Content="OK" IsDefault="True" Margin="3" Click="Button_Click" />
</StackPanel>
<ContentPresenter Name="WindowContent" Content="{Binding}"/>
</DockPanel>
</Window>
Following MVVM, the right way to show a dialog is through a mediator. To use a mediator, you typically require some service locator as well. For mediator specific details, look here.
The solution I settled on involved implementing an IDialogService interface that is resolved through a simple static ServiceLocator. This excellent codeproject article has the details on that. Take note of this message in the article forum. This solution also solves the problem of discovering the owner window via the ViewModel instance.
Using this interface, you can call IDialogService.ShowDialog(ownerViewModel, dialogViewModel). For now, I'm calling this from the owner ViewModel, meaning I have hard references between my ViewModels. If you use aggregated events, you will probably call this from a conductor.
Setting the minimum size on the View that will eventually be displayed in the dialog doesn't automatically set the minimum size of the dialog. Also, since the logical tree in the dialog contains the ViewModel, you can't just bind to the WindowContent element's properties. This question has an answer with my solution.
The answer I mention above also includes code that centers the window on the owner.
Finally, disabling the minimize and maximize buttons is something WPF can't natively do. The most elegant solution IMHO is using this.

Resources