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

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…

Related

Why is RoutedEventArgs.Handled ignored between TouchDown and Click

I am trying to support both TouchDown and Click with the same event.
You will notice that the following code, instead of toggling between green and red, just flashes red and goes back to green. As far as I can tell, this is because the click event is ignoring the Handled property of the RoutedEvent. I need this to work with both Touch and Mouse.
XAML:
<Window x:Class="CodeSpace.WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Background="LightGreen">
<Grid>
<Button Content="Touch Me" HorizontalAlignment="Center" VerticalAlignment="Center" Height="75" Width="75" Click="OnClick" TouchDown="OnClick"/>
</Grid>
</Window>
Code behind:
using System.Windows;
using System.Windows.Media;
namespace CodeSpace.WPF
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnClick(object sender, RoutedEventArgs e)
{
Background = (Background.Equals(Brushes.LightGreen)) ? Brushes.LightCoral : Brushes.LightGreen;
e.Handled = true;
}
}
}
Note: the reason I am not just using Click alone (which is supposed to work with Touch) is because with my specific case when I do that, the first time the button is touched nothing happens. All subsequent touches work just fine. This is a separate issue that I can't ask about because I cannot reproduce it in a simple code example. Also, this behavior only happens when windows is set to 120 DPI. Everything works just fine in 96 DPI. I have no idea!
The click event comes as a result of MouseUp/TouchUp rather than *Down (unless you change the ClickBehavior of the button). This answer has a good approach to globally blocking the promotion of mouse events to touch events for an element: https://stackoverflow.com/a/19540120/518955

databinding in silverlight

I have a xaml with a button like this:
Button.xaml
<Grid x:Name="LayoutRoot" >
<StackPanel >
<Button Content="Button1" Click="Button1_Click" />
</StackPanel >
</Grid>
and Button.xaml.cs:
private void Button1_Click(object sender, RoutedEventArgs e)
{
// Get a instance of ClientOversikt
CustomerView childWindow = m_container.Resolve<CustomerView >();
childWindow.Show();
}
It's working fine. But I want to use Databinding in Button.xaml instead of Click="Button1_Click". How could I do it?
I appreciate all the help
Since you're using Silverlight 4, you can use commands. You bind the Command property of the Button to an instance of ICommand, which will open the child window when executed. Then, when you click on the button, the command will be executed.
This page contains a reasonably good introduction to commanding.

Trouble with MediaElement in 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);

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.

Odd Loaded behavior in markup versus code

I'm currently very confused by the differing behavior regarding the FrameworkElement.Loaded event. I've put together a small example application that demonstrates this.
Xaml:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
Loaded="Window_Loaded">
<Grid>
<TabControl>
<TabItem Header="Tab 1" />
<TabItem Header="Tab 2" >
<WindowsFormsHost Name="formHost" Loaded="formHost_Loaded" />
</TabItem>
</TabControl>
</Grid>
</Window>
Code:
using System.Windows;
namespace WpfApplication1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
formHost.Loaded += delegate
{
MessageBox.Show("Delegate");
};
}
private void formHost_Loaded(object sender, RoutedEventArgs e)
{
MessageBox.Show("Markup");
}
}
}
As it is, when I run the application I get two immediate MessageBoxes - "Markup" and "Delegate". If, however, I remove Loaded="formHost_Loaded" from the WindowsFormsHost, I get neither on startup. It obviously makes sense why I don't get the "Markup" dialog, but why does this also remove the "Delegate"? I would imagine it has to do with the order in which the events are called (Window versus its children), but I'm having a tough time figuring it out.
Note: You can replace the WindowsFormsHost with other controls, it really shouldn't matter - I was just using it for another few tests.
The Loaded event handlers are determined before any of them are called, as the Loaded event is effectively broadcast all at once starting at the top of the tree. Since you added a handler after WPF has decided what handlers need to be called, it is ignored.
This can be verified using Reflector. Specifically, the BroadcastEventHelper.BroadcastLoadedSynchronously method will call the BroadcastEventHelper.BroadcastEvent method with the LoadedEvent routed event.
BroadcastEvent method gathers all the objects in the visual tree that have a Loaded event handler first, then loops through and raises the events on those objects.

Resources