Is there possible add aero effect in Control itself in WPF? - c#

I have a WriteableBitmap that would display video in the Window.
Currently I need to covert the subtitle in the bottom. I want to use a aero effect, but only cover the bottom of the WriteableBitmap.
Which mean, this is the original :
And I want to add a Grid Control which has aero effect, like This:
But I don't know how, I already think about take the picture bottom place and use some effect , and then put it on the top, But It looks a low performance for writableBitmap, because which mean it need to process 25 around picture every second...
Update:
Here is my code:
<Grid>
<Image x:Name="imgFrame"/>
</Grid>
private WriteableBitmap bitmap;
private void OnVideoFrameRunning(FrameArgs e)
{
if (null == bitmap)
{
bitmap = new WriteableBitmap(e.Width, e.Height, 72, 72,PixelFormats.Bgr24, null);
imgFrame.Source = bitmap;
}
bitmap.WritePixels(new Int32Rect(0, 0, e.Width, e.Height), e.Buffer, e.Width * 3, 0, 0);
}

You could do something like this:
<Grid>
<Grid.Effect>
<BlurEffect Radius="18"/>
</Grid.Effect>
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Bottom"
FontSize="40"
Foreground="#444444"
Text="Some Subtitles"
Margin="0 0 0 20"/>
</Grid>
Radius="0":
Radius="18":

Related

UI performance issue when I try to change the width of the ItemsControl in WPF

I'm making a dynamic lyrics player using the ItemsControl from the answer to this question.
I use two ItemsControls that are stacked on top of each other and both are placed in different Canvas (to get the actual width of the text currently being displayed), Canvas are placed in the Grid to ensure that the Canvas will appear in the correct position in the window, like this:
<Grid>
<Canvas x:Name="MainBackLineCanvas" Grid.Column="0" Grid.Row="0">
<ItemsControl x:Name="MainBackLine" ItemsSource="{Binding}">
...
</ItemsControl>
</<Canvas>
<Canvas x:Name="MainColorLineCanvas" Grid.Column="0" Grid.Row="0">
<ItemsControl x:Name="MainColorLine" ItemsSource="{Binding}">
...
</ItemsControl>
</Canvas>
</Grid>
The effect of this window is as follows:
The MainBackLine(the blue text) will be fully displayed at the beginning, the width of the MainColorLine(the yellow text) is set to 0 at the beginning, and the width will increase as the song time increases.
I used a DispatcherTimer to change its width. My code is as follows:
DispatcherTimer TextFlashTimer = new DispatcherTimer();
TextFlashTimer.Interval = new TimeSpan(0, 0, 0, 0, 100);
TextFlashTimer.Tick += TextFlash;
private void TextFlash(object? sender, EventArgs e)
{
//playTimePercent is the percentage of the playback progress of the current sentence, which is a Double variable
MainColorLine.Width = ((playTimePercent > 0 && playTimePercent <1) ? playTimePercent : 1) * MainBackLine.ActualWidth;
}
When I run this code, the width of the MainColorLine will change very slowly and not smoothly. I've tried changing the DispatcherTimer's Interval, or using DoEvents, but nothing works.
Is there any way to make it smoother please.
Try to use a DoubleAnimation to animate the width, e.g.:
double width = ((playTimePercent > 0 && playTimePercent < 1) ? playTimePercent : 1) * MainBackLine.ActualWidth;
DoubleAnimation doubleAnimation = new DoubleAnimation(width, new Duration(TimeSpan.FromMilliseconds(100)));
MainColorLine.BeginAnimation(FrameworkElement.WidthProperty, doubleAnimation);

How do I get a Rectangle drawn on an adorner to scale with the Image element its bound to when the window size is changed?

I'm using a rectangle drawn on an adorner to mark a region of interest on an image. The issue is that if I resize the window, the rectangle doesn't change size.
I'm new to WPF, so I've done a bunch of research, googling what I can with multiple different search terms. I actually just learned adorners that way, and I've gotten this far on that, but I've hit a wall on how to finish this last piece. I know that my problem is based in the size of the rectangle, but I don't know what to capture/look for to adjust it, since wpf resizes the actual image object on window resize, so there's no scale factor to look at.
Here's the XAML for the application I'm testing things in.
<Window x:Class="TestingAdorners.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:TestingAdorners"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid ClipToBounds="True">
<AdornerDecorator>
<Image Name="Btn" Source="nyan.png" Stretch="Uniform"/>
</AdornerDecorator>
</Grid>
</Window>
The adorner class:
class RoiAdorner : Adorner
{
public Rect rectangle = new Rect();
public RoiAdorner(UIElement adornedElement) : base(adornedElement)
{
rectangle.Height = 30;
rectangle.Width = 100;
IsHitTestVisible = false;
}
protected override void OnRender(DrawingContext drawingContext)
{
Pen pen = new Pen(Brushes.Green, 5);
drawingContext.DrawRectangle(null, pen, rectangle);
}
}
And the Xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AdornerLayer.GetAdornerLayer(Btn).Add(new RoiAdorner(Btn));
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
The desired result is that the rectangle scales with the image object so that it always covers the same region of the image. The problem is I don't know how to capture a scale factor to scale it up and down as the window resizes.
Update: After thinking through Frenchy's suggestion I realized the answer is simply: "Normalize your coordinates"
you just adapt your render method like this:
class RoiAdorner : Adorner
{
public double factorX = 0d;
public double factorY = 0d;
public Rect rectangle = new Rect();
public RoiAdorner(UIElement adornedElement) : base(adornedElement)
{
rectangle.Height = 30;
rectangle.Width = 100;
IsHitTestVisible = false;
}
protected override void OnRender(DrawingContext drawingContext)
{
if (factorY == 0)
factorY = rectangle.Height / AdornedElement.DesiredSize.Height;
if (factorX == 0)
factorX = rectangle.Width / AdornedElement.DesiredSize.Width;
var r = new Rect(new Size(AdornedElement.DesiredSize.Width * factorX, AdornedElement.DesiredSize.Height * factorY));
//Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize);
drawingContext.DrawRectangle(null, new Pen(Brushes.Red, 5), r);
}
this.AdornedElement.DesiredSize gives you the size of image.
The approach I would use is to render the picture and rectangle to the same thing. Then that one thing is stretched, scaled or whatever.
One way to do this would be to use a DrawingImage. Drawing methods are extremely efficient if rather low level.
<Grid ClipToBounds="True">
<AdornerDecorator>
<Image Name="img" Stretch="Uniform">
<Image.Source>
<DrawingImage PresentationOptions:Freeze="True">
<DrawingImage.Drawing>
<DrawingGroup>
<ImageDrawing Rect="0,0,595,446" ImageSource="DSC00025.jpg"/>
<GeometryDrawing Brush="Green">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,100,30" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</AdornerDecorator>
</Grid>
Another is with a visualbrush. Controls inherit from visual - this is somewhat higher level coding.
<Grid ClipToBounds="True">
<AdornerDecorator>
<Rectangle Name="rec">
<Rectangle.Fill>
<VisualBrush Stretch="Uniform">
<VisualBrush.Visual>
<Grid Height="446" Width="595">
<Image Source="DSC00025.jpg" Stretch="Fill"/>
<Rectangle Height="30" Width="100" Fill="Green"
VerticalAlignment="Top" HorizontalAlignment="Left"/>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
</AdornerDecorator>
</Grid>
Note that both of these are quick and dirty illustrations to give you the idea. The image I picked at random off my hard drive is sized 446 * 595. You could calculate sizes or bind or stretch as suits your requirement best.

how to check if an element is going out of its parent in c# wpf

I am creating shapes at run time on canvas in my app and except all the shapes, ellipse is going out of canvas. How do I restrict it to canvas? All other shapes are contained in canvas because of the control points at their vertices. How do I keep a check as to not let ellipse go out of canvas without clipping. I have used ClipToBounds and it doesn't meet my needs.
Also, an alternate solution is if I can add a controlpoint at the left side of ellipse of radiusX property. I can't add a controlpoint to left side of radiusX on ellipse. If you could help me with either of that?
radiusXcp = new ControlPoint(this, EllipseGeom, EllipseGeometry.RadiusYProperty, 1, true, false);
radiusXcp.RelativeTo = EllipseGeometry.CenterProperty;
shape_ControlPoints.Add(radiusXcp);
radiusXcp = new ControlPoint(this, EllipseGeom, EllipseGeometry.RadiusXProperty, 0, true, false);
radiusXcp.RelativeTo = EllipseGeometry.CenterProperty;
shape_ControlPoints.Add(radiusXcp);
//EllipseGeom.RadiusX = -EllipseGeom.RadiusX;
//radiusXcp = new ControlPoint(this, EllipseGeom, EllipseGeometry.RadiusXProperty, 0, true, false);
//radiusXcp.RelativeTo = EllipseGeometry.CenterProperty;
//shape_ControlPoints.Add(radiusXcp);
//EllipseGeom.RadiusX = -EllipseGeom.RadiusX;
Here is a quick example of what i would do. It could be improved on and the code is mainly written to be easy to read and follow. It also does not handle the possibility to if the shape's size is bigger than the Canvas (not sure if that is a use case in your project).
For the example I used the "Loaded" event on the Canvas, to reset the position before drawing. You would want this check before you draw the Ellipse object.
private void TestCanvas_Loaded(object sender, RoutedEventArgs e)
{
//canvas = 450 x 800
Ellipse test_ellipse = new Ellipse();
test_ellipse.Width = 100;
test_ellipse.Height = 100;
test_ellipse.Fill = Brushes.Red;
Canvas.SetLeft(test_ellipse, 700);
Canvas.SetTop(test_ellipse, -500);
Reset_Ellipse_Bounds(TestCanvas, ref test_ellipse);
TestCanvas.Children.Add(test_ellipse);
}
private void Reset_Ellipse_Bounds(Canvas myCanvas, ref Ellipse myEllipse)
{
var left = Canvas.GetLeft(myEllipse);
var top = Canvas.GetTop(myEllipse);
//handle too far right
if (left + myEllipse.Width > myCanvas.ActualWidth)
Canvas.SetLeft(myEllipse, myCanvas.ActualWidth - myEllipse.Width);
//handle too far left
if(left < 0)
Canvas.SetLeft(myEllipse, 0);
//handle too far up
if (top < 0)
Canvas.SetTop(myEllipse, 0);
//handle too far down
if (top + myEllipse.Height > myCanvas.ActualHeight)
Canvas.SetTop(myEllipse, myCanvas.ActualHeight - myEllipse.Height);
}
For Completeness the XAML:
<Grid>
<Canvas x:Name="TestCanvas" Loaded="TestCanvas_Loaded" />
</Grid>
The idea is to check the bounding box against the Canvas edges. There are ways to improve this, but i figured the simplest solution is easier to follow.
Within each if statement you could add more logic or a method to do further processing, but this should answer the general question of knowing if it is outside the parent.
Just set ClipToBounds="true" to its father control, it avoids the canvas to be drawn outside of it.
In my case I set it to Grid as followed :
<Grid x:Name="MainGrid" Background="WhiteSmoke" ClipToBounds="true">
<Canvas Margin="10" Background="Transparent"
SizeChanged="ViewportSizeChanged"
MouseLeftButtonDown="ViewportMouseLeftButtonDown"
MouseLeftButtonUp="ViewportMouseLeftButtonUp"
MouseMove="ViewportMouseMove"
MouseWheel="ViewportMouseWheel">
<Canvas x:Name="canvas" Width="1000" Height="600"
HorizontalAlignment="Left" VerticalAlignment="Top">
<Canvas.RenderTransform>
<MatrixTransform x:Name="transform"/>
</Canvas.RenderTransform>
</Canvas>
<Canvas x:Name="canvas2" Width="1000" Height="600"
HorizontalAlignment="Left" VerticalAlignment="Top">
<Canvas.RenderTransform>
<MatrixTransform x:Name="transform2"/>
</Canvas.RenderTransform>
</Canvas>
</Canvas>
</Grid>

Zooming into all content of a Canvas

I have this Xaml:
<Grid x:Name="DrawingGrid" Visibility="Collapsed">
<AppBarButton x:Name="ExitDrawingButton" Icon="Cancel" Click="ExitDrawingButton_Click"></AppBarButton>
<ScrollViewer x:Name="DScrollViewer" ManipulationMode="All" MaxZoomFactor="2.0" MinZoomFactor="1.0" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" DoubleTapped="DScrollViewer_DoubleTapped" Width="1140" Height="770">
<Canvas x:Name="inkCanvas" Background="Transparent" Width="1140" Height="770">
<StackPanel x:Name="DStackPanel" Orientation="Horizontal" Margin="0,0,0,0">
<Image x:Name="DImage0" HorizontalAlignment="Left" Source="{Binding nextImage}" Width="570" Canvas.ZIndex="0"/>
<Image x:Name="DImage1" HorizontalAlignment="Left" Source="{Binding nextImage}" Width="570" Canvas.ZIndex="0"/>
</StackPanel>
</Canvas>
</ScrollViewer>
</Grid>
I am drawing on Canvas using the CanvasManager.cs class and it is working fine.
Now I need to zoom on the Canvas: Zoom the Canvas (the ink) and Zoom what it contains (the StackPanel + the Images) together.
On doubleTapping the ScrollViewer containing the Canvas I have this method:
private async void DScrollViewer_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
Point point = e.GetPosition(DScrollViewer);
if (DScrollViewer.ZoomFactor == 1)
{
await Task.Delay(300);
DScrollViewer.ChangeView(point.X, point.Y, 2.0F, false);
}
else
{
await Task.Delay(300);
DScrollViewer.ChangeView(point.X, point.Y, 1.0F, false);
}
}
The result is: only the Canvas (its Ink) is Zooming and the StackPanel and its Images are left at the place, same scale, intact!
What Am I doing wrong?
Your mistake is you are assign a constant value for the width of each image (Width="570") so that it will never zoomed in or out unless you have change it's width programmatically.
The best way to change the image's width is bind a variable value for it, you can create a converter that divide the width of canvas over two (canvas.width / 2) and bind the width of each image to this converter..
Then your zoom will work perfectly.

Rectangle visible only in certain grid area

How to make the green rectangle to be visible only inside the blue one? The blue is a border of a grid. I want to cut off everything that is not inside this grid. Notice that the green rectangle will be moving.
As there is no ClipToBounds property in Silverlight, you would have to set the Clip property to a RectangleGeometry.
When the Grid's size is fixed, you may simply set a fixed size rectangle:
<Grid>
<Grid.Clip>
<RectangleGeometry Rect="0,0,400,600"/>
</Grid.Clip>
...
</Grid>
When the Grid's size can change, you may set the Clip property in a SizeChanged handler:
<Grid SizeChanged="GridSizeChanged">
...
</Grid>
The handler code:
private void GridSizeChanged(object sender, SizeChangedEventArgs e)
{
((UIElement)sender).Clip =
new RectangleGeometry
{
Rect = new Rect(0, 0, e.NewSize.Width, e.NewSize.Height)
};
}
<Grid Width="200" Height="100">
<Grid.Clip>
<RectangleGeometry Rect="0, 0, 200, 100"/>
</Grid.Clip>
source: http://www.scottlogic.co.uk/blog/colin/2009/05/silverlight-cliptobounds-can-i-clip-it-yes-you-can/

Categories