UWP: use 1 MediaSource for several Players - c#

I have some uwp application, where several videos are played simultaneously. I thought it might be a good idea to use 1 media source to improve performance. But I'm not sure why this idea doesn't work.
MainPage.xaml:
<Page
x:Class="UWP_OneMediaSourceForSeveralPlayers.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWP_OneMediaSourceForSeveralPlayers"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Loaded="Page_Loaded">
<StackPanel Orientation="Vertical" Background="Yellow" Height="400" Width="200">
<MediaPlayerElement x:Name="Player1" Height="200" Width="200" />
<MediaPlayerElement x:Name="Player2" Height="200" Width="200" />
</StackPanel>
</Page>
MainPage.xaml.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Media.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace UWP_OneMediaSourceForSeveralPlayers
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var uri = new Uri(BaseUri, "/Assets/Videos/video.mp4");
var mediaSource = MediaSource.CreateFromUri(uri);
Player1.Source = mediaSource;
Player2.Source = mediaSource;
Player1.MediaPlayer.Play();
Player2.MediaPlayer.Play();
}
}
}
That's what I see:
So, looks like the first video is loaded. But not the second...
NOW question: why I can not use 1 media source for 2 players? How to make it work? Or do you have any other idea how to run the same file in several players?
P.S. creating 2 media sources from URI is not a good solution for me, because my app can have a lot (10+) videos running at the same time. If I create a media source for each player it will hurt performance.

So what you can actually do here is share a MediaPlayer between the two MediaPlayerElement instance.
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var uri = new Uri(BaseUri, "/Assets/Videos/video.mp4");
var mediaSource = MediaSource.CreateFromUri(uri);
var player = new MediaPlayer
{
Source = new MediaPlaybackItem(mediaSource)
};
Player1.SetMediaPlayer(player);
Player2.SetMediaPlayer(player);
player.Play();
}
If you don't require the transport controls from MediaPlayerElement, it's probably better to use MediaPlayerPresenter instead.

Related

Getting and setting large amount of text data in clipboard

I'm making simple tool app in C#, I have a textbox and I want to paste some text (around 300k lines), but this makes the app unresponsive. I waited like 10 minutes and nothing moved forward.
Is there some way to handle paste and copy operations on large data sets in smoother way? For example pasting and copying same amount of data in Windows Notepad takes just few seconds.
I use
Windows.ApplicationModel.DataTransfer.Clipboard.GetContent()
and at this app hangs.
Example Code
Xaml
<Window
x:Class="App2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="1" Grid.RowSpan="1">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" Grid.RowSpan="1" Margin="5" VerticalScrollBarVisibility="Visible" >
<TextBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch" IsReadOnly="False" Header="Query Result" Text='{x:Bind pasteResult, Mode=TwoWay}' PlaceholderText="Paste results here" TextWrapping="Wrap"/>
</ScrollViewer>
</Grid>
</Window>
cs file
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace App2
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
public string pasteResult;
public MainWindow()
{
this.InitializeComponent();
}
}
}
As #Simon Mourier mentioned in the comments, the performance problem is not related to the clipboard, but the TextBox control processing that amount of data.
So, let me give you another option using the ItemsRepeater which comes with virtualization built-in. (In my laptop) it takes approx. 3 secs to show 500K lines of text from the clipboard.
MainWindow.xaml
<Window
x:Class="ClipboardTests.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid RowDefinitions="Auto,*">
<StackPanel
Grid.Row="0"
Orientation="Horizontal">
<Button
Click="PasteButton_Click"
Content="Paste" />
<Button
Click="ClearButton_Click"
Content="Clear" />
<TextBlock
x:Name="MessageTextBox"
VerticalAlignment="Center" />
</StackPanel>
<ScrollViewer Grid.Row="1">
<ItemsRepeater x:Name="TextItemsRepeaterControl" />
</ScrollViewer>
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;
namespace ClipboardTests;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private static async Task<IEnumerable<string>> GetTextLinesFromClipboard()
{
DataPackageView dataPackageView = Clipboard.GetContent();
if (dataPackageView.Contains(StandardDataFormats.Text) is true)
{
string text = await dataPackageView.GetTextAsync();
string[] lines = text
.ReplaceLineEndings()
.Split(Environment.NewLine, StringSplitOptions.None);
return lines;
}
return Enumerable.Empty<string>();
}
private async void PasteButton_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
IEnumerable<string> lines = await GetTextLinesFromClipboard();
this.TextItemsRepeaterControl.ItemsSource = lines;
stopwatch.Stop();
this.MessageTextBox.Text = $"Pasted {this.TextItemsRepeaterControl.ItemsSourceView.Count} items in {stopwatch.Elapsed.TotalSeconds} s.";
}
private void ClearButton_Click(object sender, RoutedEventArgs e)
{
this.TextItemsRepeaterControl.ItemsSource = null;
}
}

How do I append a Run to an existing Paragraph in a FlowDocument?

I need to expand an existing Run to include some new text (with different formatting) without adding an additional paragraph. Is this possible?
When I inspect the properties of the FirstBlock on the Document, I do not see any property which allow me to drill down into the paragraph so that I can add a Run to it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.flowdoc.Document = new FlowDocument();
Run r = new Run("Hello ");
r.Background = new SolidColorBrush(Colors.Yellow);
r.FontSize = 14;
Paragraph p = new Paragraph(r);
flowdoc.Document.Blocks.Add(p);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Run r = new Run("World");
r.Background = new SolidColorBrush(Colors.LightCyan);
//Append run to existing run
//
}
}
}
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<FlowDocumentReader Grid.Column="0" x:Name="flowdoc"></FlowDocumentReader>
<Button Grid.Column="1" Content="append" Click="Button_Click"></Button>
</Grid>
</Window>
To get your Paragraph you may iterate the Blocks property of the document.
You can then easily add a new Run to the Block's Inlines collection.
private void Button_Click(object sender, RoutedEventArgs e)
{
Run r = new Run("World");
r.Background = new SolidColorBrush(Colors.LightCyan);
//Append run to existing run
var p = flowdoc.Document.Blocks.OfType<Paragraph>().First();
p.Inlines.Add(r);
}

WPF MediaElement: Video opened twice

I want to use a MediaPlayer for videos and pictures with the MediaElement. I have already done tests, the MediaElement can also display pictures.
Currently I have the problem that the MediaElement seems to open twice.
Here is the example code:
XAML:
<Window x:Class="TestMediaElement.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestMediaElement"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Margin="10">
<MediaElement Name="mediaPlayer" MediaOpened="media_MediaOpened" LoadedBehavior="Play" UnloadedBehavior="Manual"/>
</Grid>
</Window>
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TestMediaElement
{
public partial class MainWindow : Window
{
int currentMediaIndex = 0;
string[] Documents;
public MainWindow()
{
InitializeComponent();
Documents = System.IO.Directory.GetFiles("C:/Users/Feller/Desktop/Test/");
Uri first = new Uri(Documents[0], UriKind.RelativeOrAbsolute);
mediaPlayer.Source = first;
mediaPlayer.MediaOpened += media_MediaOpened;
}
private void media_MediaOpened(object sender, RoutedEventArgs e)
{
Console.WriteLine("Video opened");
}
}
}
Another problem is that images close automatically after about 5 seconds.
Can anyone help me with these problems?
Thank you very much!
MediaOpened event is subscribed twice. Either remove from the xaml or remove from code.
//mediaPlayer.MediaOpened += media_MediaOpened;

Changing image in Window Application (WPF)

Goal: Changing an image to another one when a red button is clicked.
But there is a problem when changing initial panda image to animation character image.
panda image is set up in the mainpage.xaml.
<Page
x:Class="App3.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App3"
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}">
<Button Height="100" Width="100" Click="Button_Click" Background="#FFEA2323"/>
<Image x:Name="image" Source="Images/panda1.png" Margin="436,170,418,174"/>
</Grid>
</Page>
And character image is set up when button is clicked
This is "mainpage.xaml.cs" code
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Shapes;
using Windows.UI;
using System.Windows;
// 빈 페이지 항목 템플릿에 대한 설명은 http://go.microsoft.com/fwlink/?LinkId=234238 에 나와 있습니다.
namespace App3
{
/// <summary>
/// 자체에서 사용하거나 프레임 내에서 탐색할 수 있는 빈 페이지입니다.
/// </summary>
///
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
image.Source = new BitmapImage(new Uri(#"C:\Users\hinon\OneDrive\Documents\2016-2\04. 놀이방\03. 키넥트 제스처 기반 게임 제작 환경 구성\03. 데이터베이스\01. 이미지\닥터슬럼프 아리.bmp", UriKind.Absolute));
//BitmapImage bitmapImage = new BitmapImage();
//bitmapImage.UriSource = new Uri(#"C:\Users\hinon\OneDrive\Documents\2016-2\04. 놀이방\03. 키넥트 제스처 기반 게임 제작 환경 구성\03. 데이터베이스\01. 이미지\닥터슬럼프 아리.bmp", UriKind.Absolute);
//image.Source = bitmapImage;
}
}
}
But it is not successful. If I click the button, initial panda image is dissapeared, and no image opend. just black. What I missed in this code?
Before click the red button
After click the red button

Data binding to hub section in C# XAML

I'm trying to design a very basic application for windows phone (C#/XAML). At first, I used the hub template, but I got lost so I decided to go step by step and started from a blank app and added a hub.
I managed to bind data between two distinct pages, with line codes such as TextBox.DataContext = DataContext ... but I'd like to bind data properly to hub sections, which is not as easy since hub elements lie in "DataTemplate" which cannot be accessed.
I spent the last two weeks reading documentation, tutorials, etc... Now I've read so many different sources that I am totally lost and do not know what I should do.
Here is my app : A "Players" class (player name and score); and a simple page with a hub control that has 2 sections.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace App1
{
class Players
{
private string playerName;
public string PlayerName
{
get { return playerName; }
set { playerName = value; }
}
private int playerScore;
public int PlayerScore
{
get { return playerScore; }
set { playerScore = value; }
}
}
}
The code behind is very basic, I just created a list of that I populated with only two players. All I want to do, for now, is to understand how the data binding can work.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace App1
{
public sealed partial class MainPage : Page
{
public MainPage()
{
List<Players> players = new List<Players>();
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
Players player1 = new Players(); Players player2 = new Players();
player1.PlayerName = "Vince"; player1.PlayerScore = 2; player2.PlayerName = "Mike"; player2.PlayerScore = 42;
players.Add(player1); players.Add(player2);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}
}
All I'd like to do, for now, is to understand how the data binding works.
For example, I would like to have a TextBox in my hub that would display player1.PlayerName.
My XAML code, as of today looks as :
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<local:Players x:Key="PlayerDataSource"/>
</Page.Resources>
<Page.DataContext>
<Binding Source="{StaticResource PlayerDataSource}"/>
</Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Hub x:Name="Hub1" Grid.Row="1" DataContext="Players">
<HubSection x:Name="HubSec1">
<DataTemplate>
<StackPanel>
<TextBlock Text="Hello"></TextBlock>
<TextBox x:Name="tb1"></TextBox>
</StackPanel>
</DataTemplate>
</HubSection>
<HubSection x:Name="HubSec2">
<DataTemplate>
<StackPanel>
<TextBlock Text="Section2"></TextBlock>
<TextBlock Text="Trying to bind"/>
<TextBlock Text="{Binding PlayerName}"/>
</StackPanel>
</DataTemplate>
</HubSection>
</Hub>
</Grid>
I know how to bind a full list to an element such as a listbox (with a ItemsSource={Binding} in XAML + ListBox.DataContext = players in code behind), but here I would like to display only one given element of my players...
I have tried to add a xmlns:data="clr-namespace" but this does not seem to be working (the IDE does not propose any auto-completion)
I am probably doing something wrong somewhere... but can't figure out where exactly. As mentionned above, I have tried so many different options that I am totally lost now...
OK, I finally found out what was wrong in my code.
First, in the xaml, for each list box, make sure to add the binding
<ListBox x:Name="lb" ItemsSource="{Binding}" Grid.Column="1">
Then, for example, to bind to a textbox, I simply added :
<TextBlock Text="{Binding PlayerName}" FontSize="32" Grid.Column="0"/>
And in the code behind, a simple :
MainHub.DataContext = players;
And that was it, everything binds perfectly now. Thanks for your help

Categories