I am building a Universal App and I think I have found a bug in the Windows Phone 8.1 PopupMenu control. I have been able to reproduce it with a small piece of code. It works fine on Windows 8 but not on Windows Phone 8.1.
Whenever I create a PopupMenu from within a button click it doesn't return from ShowFromSelectionAsync() when there is a background task running ? Why ?
The same code works on Windows 8.
I my application a lot of background work is being done, so the control doesn't work correctly on Phone anymore. Any suggestions how to fix this ?
I have a MainPage.xaml:
<Page
x:Class="PopupMenuBugPhone.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PopupMenuBugPhone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<Button Content="Test Bug" Click="Button_Click" />
</StackPanel>
</Grid>
</Page>
MainPage.xaml.cs:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
var frameworkElement = sender as FrameworkElement;
var task = SimulateBackgroundWork(); // COMMENT THIS TO MAKE IT WORK ON PHONE!!!
var menu = new PopupMenu();
var сmdOption1 = new UICommand("Option1");
var cmdOption2 = new UICommand("Option2");
menu.Commands.Add(сmdOption1);
menu.Commands.Add(cmdOption2);
// We don't want to obscure content, so pass in a rectangle representing the sender of the context menu event.
var chosenCommand = await menu.ShowForSelectionAsync(frameworkElement.GetElementRect());
if (chosenCommand == null) // The command is null if no command was invoked.
{
await new MessageDialog("No choice").ShowAsync();
}
else
{
await new MessageDialog("Choice: " + chosenCommand.Label).ShowAsync();
}
await task; // COMMENT THIS TO MAKE IT WORK ON PHONE!!!
}
private Task SimulateBackgroundWork()
{
var t = Task.Run(() =>
{
var dt = DateTime.Now;
// Do some dummy processing loop
while (DateTime.Now < dt.AddSeconds(300))
{
;
}
});
return t;
}
}
How about using a MenuFlyout?
Let's say you defined it in code-behind of the page, along with the TaskCompletionSource to wrap it to make showing awaitable:
MenuFlyout flyout = new MenuFlyout();
TaskCompletionSource<string> tcs;
Then on button click you could do this:
private async void Button_Click(object sender, RoutedEventArgs e)
{
var frameworkElement = sender as FrameworkElement;
var task = SimulateBackgroundWork();
flyout.Closed += flyout_Closed;
var mf1 = new MenuFlyoutItem { Text = "Option1" };
var mf2 = new MenuFlyoutItem { Text = "Option2" };
mf1.Click += mf_Click;
mf2.Click += mf_Click;
flyout.Items.Clear();
flyout.Items.Add(mf1);
flyout.Items.Add(mf2);
await ShowMenuFlyout(sender as FrameworkElement);
await task;
}
ShowMenuFlyout is awaitable and implemented like this:
public Task<string> ShowMenuFlyout(FrameworkElement sender)
{
tcs = new TaskCompletionSource<string>();
flyout.ShowAt(sender as FrameworkElement);
return tcs.Task;
}
And event handlers would simply do this:
async void mf_Click(object sender, RoutedEventArgs e)
{
flyout.Closed -= flyout_Closed;
await new MessageDialog("Choice: " + (sender as MenuFlyoutItem).Text).ShowAsync();
tcs.SetResult((sender as MenuFlyoutItem).Text);
}
async void flyout_Closed(object sender, object e)
{
flyout.Closed -= flyout_Closed;
await new MessageDialog("No choice").ShowAsync();
tcs.SetResult("No choice");
}
This works on both platforms. Of course, this is just proof of concept, you might want a null check here or there, but it works.
Related
I'm working on a UWP Desktop application for video editing and I need to include the subtitles feature. I've did what other questions recommend, but the TimedMetadataTracks list is still empty. Any help is most welcome.
XAML
<MediaPlayerElement x:Name="mediaPlayerElement"
AutoPlay="False"
Margin="5"
Width="640" Height="480"
HorizontalAlignment="Center"
AreTransportControlsEnabled="True" />
Code Behind
private void nviRestore_Tapped(object sender, TappedRoutedEventArgs e)
{
// Create the media source and supplement with external timed text sources
var source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/teste.mp4"));
var ttsEnUri = new Uri("ms-appx:///Assets/en.srt");
var ttsEn = TimedTextSource.CreateFromUri(ttsEnUri);
ttsMap[ttsEn] = ttsEnUri;
ttsEn.Resolved += Tts_Resolved;
source.ExternalTimedTextSources.Add(ttsEn);
// Create the playback item from the source
var playbackItem = new MediaPlaybackItem(source);
playbackItem.TimedMetadataTracksChanged += MediaPlaybackItem_TimedMetadataTracksChanged;
// Set the source to start playback of the item
this.mediaPlayerElement.SetPlaybackSource(playbackItem);
}
private void Tts_Resolved(TimedTextSource sender, TimedTextSourceResolveResultEventArgs args)
{
var ttsUri = ttsMap[sender];
// Handle errors
if (args.Error != null)
{
var ignoreAwaitWarning = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//rootPage.NotifyUser("Error resolving track " + ttsUri + " due to error " + args.Error.ErrorCode, NotifyType.ErrorMessage);
});
return;
}
// Update label manually since the external SRT does not contain it
var ttsUriString = ttsUri.AbsoluteUri;
if (ttsUriString.Contains("_en"))
args.Tracks[0].Label = "English";
}
private async void MediaPlaybackItem_TimedMetadataTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
// Present the first track
sender.TimedMetadataTracks.SetPresentationMode(0, TimedMetadataTrackPresentationMode.PlatformPresented);
}
I need to implement some functionality in a second Window in UWP.
So I want to do following
Start new Window in a Modal state so parent Window cannot be acceced.
On closed event return value to the parent Window.
I use following code to create a new Window please help me to archive these points
private async void Button_Click(object sender, RoutedEventArgs e)
{
var switchToView = true;
var newView = CoreApplication.CreateNewView();
int newViewId = 0;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var frame = new Frame();
frame.Navigate(typeof(MyCam), null);
Window.Current.Content = frame;
newViewId = ApplicationView.GetForCurrentView().Id;
});
var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
if (switchToView && viewShown)
{
// Switch to new view
await ApplicationViewSwitcher.SwitchAsync(newViewId);
}
}
I found nice solution here
https://github.com/bkaankose/UWPModalDialogHelper
It is about to use Prism.Core Nuget package to simulate fullscreen popup with custom UserControl.
I have a web browser in WPF
<WebBrowser x:Name="WebBrowserControl" Width="1000" Height="600" Source="https://www.1.com" cal:Message.Attach="[Event LoadCompleted]=[Action LoadCompleted($eventArgs)]"/>
It loads the www.1.com and when i click a button on 1.com it jump to http://2.com
I listen to loadCompleted event
public void LoadCompleted(NavigationEventArgs e)
{
if (e.Uri.AbsoluteUri == "https://www.2.com")
{
//Here i want to get WebBrowserControl.Document as mshtml.HTMLDocument;
MessageBox.Show("Completed loading the page");
}
}
I want to get 2.com htmlDocument. is there a way to achieve that. I achieve that in not viewmodel way.
private void WebBrowserControl_LoadCompleted(object sender, NavigationEventArgs e)
{
string[] tags = new string[]{};
if (e.Uri.OriginalString == "https://www.2.com")
{
MessageBox.Show("here");
var document = WebBrowserControl.Document as mshtml.HTMLDocument;
}
}
I did something like this
//view
cal:Message.Attach="[Event LoadCompleted]=[Action LoadCompleted(WebBrowserControl.Document,$eventArgs)]"
//ViewModel
public void LoadCompleted(mshtml.HTMLDocument x ,NavigationEventArgs e)
{
//it calls this method but the x is null
}
<WebBrowser x:Name="WebBrowserControl" Width="1000" Height="600" Source="https://www.1.com" cal:Message.Attach="[Event LoadCompleted]=[Action LoadCompleted($source,$eventArgs)]"/>
public void LoadCompleted(object sender ,NavigationEventArgs e)
{
WebBrowser x = (WebBrowser)sender;
var document = x.Document as mshtml.HTMLDocument;
}
I have a situation with my app whereby the ProgressBar displays correctly when OnNavigatedTo is executed. Then during the process of searching for and collating the data resuested, the ProgressBar hangs/freezes. Finally, when all data has been collected and displayed correctly in the list, the ProgressBar collapses correctly.
My XAML looks like this:
<ProgressBar
x:Name="progressbar"
IsIndeterminate="True"
Minimum="0"
Maximum="100"
Value="0"
Width="400"
Height="30"
Foreground="#FF117B0F"
/>
My C# looks like this:
List<GameLive> gameLive;
public MainPage()
{
InitializeComponent();
gameLive = new List<GameLive>();
ProgressBar progressbar = new ProgressBar();
}
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string htmlPageLive = "";
bool isError = false;
HttpClient client = new HttpClient();
try
{
htmlPageLive = await client.GetStringAsync("webpage");
}
catch (Exception)
{
isError = true;
}
if (isError)
{
MessageBox.Show("Unable to retrieve data");
return;
}
HtmlDocument htmlDocumentLive = new HtmlDocument();
htmlDocumentLive.LoadHtml(htmlPageLive);
foreach (var div in htmlDocumentLive.DocumentNode.SelectNodes("..nav.."))
{
bool isErrorTwo = false;
GameLive newGame = new GameLive();
try
{
newGame.Title = removed;
newGame.Summary = removed;
gameLive.Add(newGame);
}
catch (Exception)
{
isErrorTwo = true;
}
if (isErrorTwo)
{
NavigationService.Navigate(new Uri("/Temp.xaml", UriKind.Relative));
return;
}
}
lstGameLive.ItemsSource = gameLive;
progressbar.Visibility = Visibility.Collapsed;
progressbar.IsIndeterminate = false;
}
I've tried multiple solutions but have run out of options. Can somebody please point me in the right direction here?
Thank you.
It's a problem related to the async methods as they freeze the UI (as I think) till the execution finishes.
You might want to look at this and also this
For a much more cleaner code, try binding the progress bar visibility like this one and this
I have about 8 records that I want to print in one batch, each on a separate page. However, the UWP sample for this uses over 600 lines of code to accomplish it. It seems to me that it has to be much, much easier than that. I thought all we'd have to do is add each page to the PrintDocument and send the print job. Apparently not. I'm using this:
async void Print()
{
var printDocument = new PrintDocument();
var printDocumentSource = printDocument.DocumentSource;
var printMan = PrintManager.GetForCurrentView();
printMan.PrintTaskRequested += PrintTaskRequested;
var pages = new List<Page>();
foreach (var item in items)
{
(//Set up variables)
var printPage = new PageToPrint() { //Set properties };
printPage.Set_Up(); //Set up fields
pages.Add(printPage);
}
printDocument.SetPreviewPage(1, page);
printDocument.SetPreviewPageCount(pages.Count, PreviewPageCountType.Final);
foreach (var page in pages)
{
printDocument.AddPage(page);
}
printDocument.AddPagesComplete();
await PrintManager.ShowPrintUIAsync();
}
void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs e)
{
PrintTask printTask = null;
printTask = e.Request.CreatePrintTask("Kimble Print Job", sourceRequested =>
{
printTask.Completed += PrintTask_Completed;
sourceRequested.SetSource(printDocumentSource);
});
}
private async void PrintTask_Completed(PrintTask sender, PrintTaskCompletedEventArgs args)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
PrintManager printMan = PrintManager.GetForCurrentView();
printMan.PrintTaskRequested -= PrintTaskRequested;
});
}
However, it won't generate the print preview. It just sits there spinning and spinning, and if I hit "print" it doesn't succeed (PDF can't open, job never gets to a physical printer.)
I was hoping printing would be at least reasonably easy with the PrintDocument, and I still think it looks like it should be. Am I just missing it here, or does it really take 600+ lines of code to dispatch a simple print job?
However, it won't generate the print preview.
This is because the setPreview method printDocument.SetPreviewPage(1, page); must be put in printDocument.GetPreviewPageevent handle. So you should register the event handle firstly. Same with printDocument.AddPages event handle.You messed up the event handle register and callback function all in one.Here I do a little change of your code and I tested it works well.
protected PrintDocument printDocument;
protected IPrintDocumentSource printDocumentSource;
List<Page> pages = new List<Page>();
Page printPage = new PageToPrint();
public MainPage()
{
this.InitializeComponent();
RegisterForPrinting();
}
private async void BtnPrint_Click(object sender, RoutedEventArgs e)
{
await PrintManager.ShowPrintUIAsync();
}
public void RegisterForPrinting()
{
printDocument = new PrintDocument();
printDocumentSource = printDocument.DocumentSource;
pages.Add(printPage);
printDocument.GetPreviewPage += GetPrintPreviewPage;
printDocument.AddPages += AddPrintPages;
PrintManager printMan = PrintManager.GetForCurrentView();
printMan.PrintTaskRequested += PrintTaskRequested;
}
private void AddPrintPages(object sender, AddPagesEventArgs e)
{
foreach (var page in pages)
{
printDocument.AddPage(page);
}
printDocument.AddPagesComplete();
}
private void GetPrintPreviewPage(object sender, GetPreviewPageEventArgs e)
{
printDocument.SetPreviewPage(1, printPage);
printDocument.SetPreviewPageCount(pages.Count, PreviewPageCountType.Final);
}
void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs e)
{
PrintTask printTask = null;
printTask = e.Request.CreatePrintTask("Kimble Print Job", sourceRequested =>
{
printTask.Completed += PrintTask_Completed;
sourceRequested.SetSource(printDocumentSource);
});
}
private async void PrintTask_Completed(PrintTask sender, PrintTaskCompletedEventArgs args)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
PrintManager printMan = PrintManager.GetForCurrentView();
printMan.PrintTaskRequested -= PrintTaskRequested;
});
}
Although you may not need all the code of the sample, but I recommend you to follow the official sample structure and build a PrintHelper class.