How to set Grid.Background to MediaPlayer in UWP? - mobile

How to add video to Grid.Background in UWP app?
In previous version, I used
<Grid.Background>
<ImageBrush ImageSource="ms-appx:///Assets/carosel3.jpg" Stretch="UniformToFill"/>
</Grid.Background>

There are two ways how to accomplish this in UWP.
Composition APIs
The Composition APIs offer CompositionSurfaceBrush with which you can paint an area with pixels rendered using a video through MediaPlayer. Example from the documentation:
Compositor _compositor = ElementCompositionPreview.GetElementVisual(MyGrid).Compositor;
SpriteVisual _videoVisual;
CompositionSurfaceBrush _videoBrush;
// MediaPlayer set up with a create from URI
_mediaPlayer = new MediaPlayer();
// Get a source from a URI. This could also be from a file via a picker or a stream
var source = MediaSource.CreateFromUri(new Uri("https://www.example.com/video.mp4"));
var item = new MediaPlaybackItem(source);
_mediaPlayer.Source = item;
_mediaPlayer.IsLoopingEnabled = true;
// Get the surface from MediaPlayer and put it on a brush
_videoSurface = _mediaPlayer.GetSurface(_compositor);
_videoBrush = _compositor.CreateSurfaceBrush(_videoSurface.CompositionSurface);
_videoVisual = _compositor.CreateSpriteVisual();
_videoVisual.Brush = _videoBrush;
Check the docs for more information on look for one of many tutorials on using Composition APIs on the internet (like here).
Separate `MediaPlayer element
A simpler solution would be to just place a MediaPlayerElement under the Grid itself. When the Grid has Transparent background, the video will appear below the Grid.
<Grid>
<MediaPlayerElement x:Name="mediaPlayer"
AreTransportControlsEnabled="False"
Source="ms-appx:///SomeVideo.mp4" />
<Grid>
... your content
</Grid>
</Grid>

Related

WPF hit testing a rectangular area

I have a WrapPanel containing an arbitrary number of jagged sized elements. I'd like to implement drag select for my items.
It seems pretty obvious how to HitTest for a point, but how can I find all items within a rectangular area?
You may use VisualTreeHelper.HitTest with a GeometryHitTestParameters argument and a HitTestFilterCallback that checks if a Visual is a direct child of the Panel.
Something like this:
var selectedElements = new List<DependencyObject>();
var rect = new RectangleGeometry(...);
var hitTestParams = new GeometryHitTestParameters(rect);
var resultCallback = new HitTestResultCallback(
result => HitTestResultBehavior.Continue);
var filterCallback = new HitTestFilterCallback(
element =>
{
if (VisualTreeHelper.GetParent(element) == panel)
{
selectedElements.Add(element);
}
return HitTestFilterBehavior.Continue;
});
VisualTreeHelper.HitTest(
panel, filterCallback, resultCallback, hitTestParams);
It looks a little complicated, but the HitTestFilterCallback is necessary to get all Visuals in the visual tree, not only those that actually got hit. For example if your panel contains Label controls, the HitTestResultCallback will only be called for the Border and TextBlock child Visuals of each Label.
The option for controlling hit test visibility is the IsHitTestVisible property. This property allows you to control hit test visibility regardless of the brush with which the UIElement is rendered.
Also, You want to set the Fill to Transperent
<Rectangle Width="200" Height="200" Margin="170,23,12,35" Fill="Transparent" IsHitTestVisible="True" />

WPF, C#: Draw a line onto existing bitmap in image control

I have a bitmap image in an image control
I need to draw a red line on the bitmap each time I click with the mouse onto it, at the place where I clicked the mouse.
I first thought of creating a Line object, but found out, that I cannot add the Line. I would need a canvas. But if I put my image in a canvas, my bitmap does not stretch over the whole canvas (I found out, that the coordinates of the bitmap determine the place on the canvas, so my bitmap is wrongly displayed.)
Then I tried using graphics
Graphics graphics = Graphics.FromImage(bitmapImg);
graphics.DrawLine(new System.Drawing.Pen(System.Drawing.Color.Red), 0, 0, bitmapImg.Width, bitmapImg.Height); //not the line yet, just for testing
graphics.DrawImage(bitmapImg, 0, 0, bitmapImg.Width,bitmapImg.Height);
graphics.Dispose();
However, I don`t get anything painted onto my bitmap........
Now I think, I probably have to get the bitmap into an array and then change the pixel color to get the line in the bitmap. I believe, that this would be very slow.
I am now trying something with visualDrawing, however, I have not got it to work yet:-(
What is a good way to get a line onto an existing bitmap in WPF C#???? and how to remove it?
I would be glad for any help! Thank you! I posted it already on the MS forum page, but no answer so far.
When you do Graphics.FromImage, this Graphics class (and also the System.Drawing.Pen) do not belong to WPF, they are part from WinForms and they are internally using Windows' GDI+ calls to draw and cannot draw on top of WPF.
If you didn't got an error when compiling the first line of your code, then probably your bitmapImg is a System.Drawing.Image (from WinForms) not an Image control from WPF (System.Window.Controls.Image).
As adrianm mentioned, the easiest way will probably be to use a Grid:
<Grid>
<Image Source="your image" />
<Line Name="line" Visibility="Hidden" Stroke="Red" StrokeThickness="1" />
</Grid>
Then, in your click event handler you can make the line visible and give it the coordinates you want:
line.Visibility = Visible;
line.X1 = mouse_x;
line.Y1 = mouse_y;
line.X2 = ...;
line.Y2 = ...;
You can place a canvas with the background as transparent on top of your BitmapImage and then draw the line as required.
Code from xaml file:
<Grid>
<Image Source="C:\Users\sm143444\Desktop\download.jpg" />
<Canvas Background="Transparent" x:Name="draw" />
</Grid>
Code from Xaml.cs:
public MainWindow()
{
InitializeComponent();
Point startPoint = new Point(50, 50);
Line newLine = new Line();
newLine.Stroke = Brushes.Black;
newLine.Fill = Brushes.Black;
newLine.StrokeLineJoin = PenLineJoin.Bevel;
newLine.X1 = startPoint.X;
newLine.Y1 = startPoint.Y;
newLine.X2 = startPoint.X + 100;
newLine.Y2 = startPoint.Y + 100;
newLine.StrokeThickness = 2;
this.draw.Children.Add(newLine);
}
output
Or you can even add a ZIndex to your image and Line so that they are laid on different layers on canvas.

RenderTargetBitmap + Resource'd VisualBrush = incomplete image

I've found a new twist on the "Visual to RenderTargetBitmap" question!
I'm rendering previews of WPF stuff for a designer. That means I need to take a WPF visual and render it to a bitmap without that visual ever being displayed. Got a nice little method to do it like to see it here it goes
private static BitmapSource CreateBitmapSource(FrameworkElement visual)
{
Border b = new Border { Width = visual.Width, Height = visual.Height };
b.BorderBrush = Brushes.Black;
b.BorderThickness = new Thickness(1);
b.Background = Brushes.White;
b.Child = visual;
b.Measure(new Size(b.Width, b.Height));
b.Arrange(new Rect(b.DesiredSize));
RenderTargetBitmap rtb = new RenderTargetBitmap(
(int)b.ActualWidth,
(int)b.ActualHeight,
96,
96,
PixelFormats.Pbgra32);
// intermediate step here to ensure any VisualBrushes are rendered properly
DrawingVisual dv = new DrawingVisual();
using (var dc = dv.RenderOpen())
{
var vb = new VisualBrush(b);
dc.DrawRectangle(vb, null, new Rect(new Point(), b.DesiredSize));
}
rtb.Render(dv);
return rtb;
}
Works fine, except for one leeetle thing... if my FrameworkElement has a VisualBrush, that brush doesn't end up in the final rendered bitmap. Something like this:
<UserControl.Resources>
<VisualBrush
x:Key="LOLgo">
<VisualBrush.Visual>
<!-- blah blah -->
<Grid
Background="{StaticResource LOLgo}">
<!-- yadda yadda -->
Everything else renders to the bitmap, but that VisualBrush just won't show. The obvious google solutions have been attempted and have failed. Even the ones that specifically mention VisualBrushes missing from RTB'd bitmaps.
I have a sneaky suspicion this might be caused by the fact that its a Resource, and that lazy resource isn't being inlined. So a possible fix would be to, somehow(???), force resolution of all static resource references before rendering. But I have absolutely no idea how to do that.
Anybody have a fix for this?
You have two problems:
You didn't set a PresentationSource on your visual so Loaded events won't fire.
You didn't flush the Dispatcher queue. Without flushing the Dispatcher queue, any functionality that uses Dispatcher callbacks won't work.
The immediate cause of your problem is failure to flush the Dispatcher queue, since VisualBrush uses it, but you will probably run into the PresentationSource problem before long so I would fix both of these.
Here is how I do it:
// Create the container
var container = new Border
{
Child = contentVisual,
Background = Brushes.White,
BorderBrush = Brushes.Black,
BorderThickness = new Thickness(1),
};
// Measure and arrange the container
container.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
container.Arrange(new Rect(container.DesiredSize));
// Temporarily add a PresentationSource if none exists
using(var temporaryPresentationSource = new HwndSource(new HwndSourceParameters()) { RootVisual = (VisualTreeHelper.GetParent(container)==null ? container : null) })
{
// Flush the dispatcher queue
Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
// Render to bitmap
var rtb = new RenderTargetBitmap((int)b.ActualWidth, (int)b.ActualHeight, 96, 96, PixelFormats.Pbgra32);
rtb.Render(container);
return rtb;
}
FYI, StaticResource lookup is never delayed under any circumstances: It is processed the moment the XAML is loaded and immediately replaced with the value retrieved from the ResourceDictionary. The only way StaticResource could possibly be related is if it picked up the wrong resource because two resources had the same key. I just thought I should explain this -- it has nothing to do with your actual problem.
Well to inline it, you could just do something like this:
<Grid>
<Grid.Background>
<VisualBrush>
<VisualBrush.Visual>
<!-- blah blah -->
</VisualBrush.Visual>
</VisualBrush>
</Grid.Background>
</Grid>
If that doesn't work, my guess would be that it must be something specific with the Visual instance you are using (and that will require further code to better diagnose).

Printing of WPF Window on one page

I am able to print the current Window using the following code:
PrintDialog printDialog = new PrintDialog();
if (printDialog.ShowDialog().GetValueOrDefault(false))
{
printDialog.PrintVisual(this, this.Title);
}
However if the Window does not fit the page it get truncated.
How do I make the Window fit the Page ?
I guess I need to make a graphics element first and check if this graphics fits the page, but I have found nothing so far.
There is one solution out there that lots of people are reposting as their own. It can be found here:
http://www.a2zdotnet.com/View.aspx?id=66
The problem w/ that is that it does resize your UI. So this next link takes the previous solution and resizes back to the original size when it's done. This does work, although I can't help but to think there's likely a more elegant solution out there somewhere:
http://www.slickthought.net/post/2009/05/26/Visual-Tree-Printing-in-WPF-Applications.aspx
Slickthought.net domain is defunct. Wayback Machine to the rescue.
https://web.archive.org/web/20130603071346/http://www.slickthought.net/post/2009/05/26/Visual-Tree-Printing-in-WPF-Applications.aspx
<Button Content="Print" Command="{Binding Path=PrintCommand}" CommandParameter="{Binding ElementName=ReportPanel}"></Button>
There are two important things to note here. First, I am using a WPF command to start the printing process. You don't have to do it this way, but it lets me tie the presenter to the UI pretty cleanly. The second thing is the CommandParameter. It is passing in a reference to the the ReportPanel. ReportPanel is just a WPF Grid control that wraps the title TextBlock and a Listbox that contains the actual charts. The simplified XAML is:
<Grid x:Name="ReportPanel" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock />
<ListBox/>
</Grid>
With that UI established, lets jump to the code. When the user clicks the Print button, the following WPF command is executed:
this.PrintCommand = new SimpleCommand<Grid>
{
CanExecuteDelegate = execute => true,
ExecuteDelegate = grid =>
{
PrintCharts(grid);
}
};
This is pretty simple stuff. SimpleCommand implements the ICommand interface and lets me pass in some lambda expressions defining the code I want to run when this command is fired. Clearly, the magic happens in the PrintCharts(grid) call. The code shown below is basically the same code you would find in Pankaj’s article with a couple of modification highlighted in red.
private void PrintCharts(Grid grid)
{
PrintDialog print = new PrintDialog();
if (print.ShowDialog() == true)
{
PrintCapabilities capabilities = print.PrintQueue.GetPrintCapabilities(print.PrintTicket);
double scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / grid.ActualWidth,
capabilities.PageImageableArea.ExtentHeight / grid.ActualHeight);
Transform oldTransform = grid.LayoutTransform;
grid.LayoutTransform = new ScaleTransform(scale, scale);
Size oldSize = new Size(grid.ActualWidth, grid.ActualHeight);
Size sz = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
grid.Measure(sz);
((UIElement)grid).Arrange(new Rect(new Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight),
sz));
print.PrintVisual(grid, "Print Results");
grid.LayoutTransform = oldTransform;
grid.Measure(oldSize);
((UIElement)grid).Arrange(new Rect(new Point(0, 0),
oldSize));
}
}
All right, what are these modifications? The most obvious is that I am replacing the use of the original this object (which represented the entire application window in the original code) with the Grid control that was passed in as part of the Command. So all of the measurements and transforms are executed using the Grid. The other change is that I have save the original Transform and Size of the Grid as well. The reason is that when you transform the Grid to fit to the printing page, it causes the actual application UI to change as well. This doesn't look so good on your screen, so after sending the Grid to the printer, I transform it back to its original screen layout.

Is it possible to start a video playing in a tooltip in Silverlight?

In Silverlight a tooltip can have as many elements in it as you want.
However, it doesn't receive focus so you can't have user interactivity in it.
Could you, though, start a video playing as soon as the tooltip opens and have the video stop as soon as the tooltip closes?
This is my first answer on Stack Overflow so I ask for your good humor.
I think you could run your video in the tooltip by using a video brush.
Here's some code I used to paint a fire video on the bar in the chart that represented heating with corn. ( long story) right here, you can see it is set to the fill of an ellipse.
#region video brush setup
protected void setupVideo()
{
VideoBrush _vb;
MediaElement mevideo;
_vb = new VideoBrush();
mevideo = new MediaElement();
mevideo.SetValue(Grid.NameProperty, "video");
Uri videoUri = new Uri("http://www.faxt.com/videos/ezburnboilerfire.wmv", UriKind.Absolute);
mevideo.Source = videoUri;
mevideo.Visibility = Visibility.Collapsed;
mevideo.MediaEnded += new RoutedEventHandler(me_MediaEnded);
MediaRoot.Children.Add(mevideo);
_vb.SetSource(mevideo);
Ellipse el = new Ellipse();
el.Width = 100;
el.Height = 100;
el.Fill = _vb;
MediaRoot.Children.Add(el);
}
You could do it with a VideoBrush as suggested by BPerreault, but you could also just set Tooltip.Content to a MediaElement.
That is because the Content property of Tooltip inherits from ContentControl and the Content property of a ContentControl can be any type of object, such as a string, a UIElement, or a DateTime. When Content is set to a UIElement (like MediaElement), the UIElement is displayed in the ContentControl. When Content is set to another type of object, a string representation of the object is displayed in the ContentControl. (from documentation)
It should be something like this:
<TextBlock x:Name="myText" Text="MouseOver and you'll get a ToolTip!">
<ToolTipService.ToolTip>
<MediaElement x:Name="myVideo" Source="Butterfly.wmv" Width="300" Height="300" />
</ToolTipService.ToolTip>
</TextBlock >

Resources