I need a WPF Control with 300000000 Width and Height, on which I will draw Rectangles, based on some input. I am drawing the Rectangle on the OnRender of the control. For testing purpose I have put the control inside a ScrollViewer and am drawing Rectangles on each pixels from (0,0) to (1000,1000). If the Width & Height is 1000000, the Rectangles are drawn properly, shown below, (the Rectangles are plotted on all the pixels from (0,0) to (1000,1000), so it looks like the on in the below link)
https://social.msdn.microsoft.com/Forums/getfile/950012
But If I increase the size to 3000000, few lines are missing in the drawing like below,
https://social.msdn.microsoft.com/Forums/getfile/950018
My Xaml:
<ScrollViewer x:Name="MainScrollViewer"
Grid.Row="2"
Grid.ColumnSpan="2"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<waferControl:WaferMapControl x:Name="WaferControl"
Grid.Row="2"
Grid.ColumnSpan="2"
Width="30000000"
Height="30000000"
Margin="10"
ColumnCount="{Binding WaferInfo.ColumnCount}"
RowCount="{Binding WaferInfo.RowCount}"
SelectedCells="{Binding SelectedCellCollection}"
Visibility="Collapsed" />
</ScrollViewer>
I am using the following code in OnRender of WaferMapControl.
private void RenderBackgroud(IEnumerable lists`enter code here`)
{
using (DrawingContext dc = _backgroundDv.RenderOpen())
{
SolidColorBrush brush = Brushes.Black;
if (brush.CanFreeze)
brush.Freeze();
foreach (GridCellInfo cellInfo in lists)
{
double length = cellInfo.GridRange;
var point = new Point(
cellInfo.RowIndex + 1,
ActualHeight - cellInfo.ColumnIndex - length);
RenderBackgroud(dc, point, brush, length);
}
}
}
private void RenderBackgroud(DrawingContext dc, Point point, Brush brush, double length)
{
var rect = new Rect(point, new Size(length, length));
dc.DrawRectangle(brush, null, rect);
}
The cellInfo contains the x,y coordinates. The point calculation is done to draw from the bottom left. The length is 1 in my sample.
Can someone help me out, what is the issue here. Let me know if you require anything from my side.
Related
I have a ContentDialog where I have an image and another image overlayed to place a watermark on it:
<StackPanel Orientation="Vertical">
<Grid>
<Image x:Name="ImageOriginal" Source="{x:Bind OriginalImage, Mode=OneWay}" MinHeight="120" MinWidth="160"/>
<Image x:Name="ImageOverlay" Source="{x:Bind OverlayImage, Mode=OneWay}" MinHeight="120" MinWidth="160" PointerPressed="ImageOverlay_PointerPressed" />
</Grid>
...
So the goal was, to place a text (centered horizontally and vertically) on the clicked position.
private void ImageOverlay_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
e.Handled = true;
var point = e.GetCurrentPoint(ImageOverlay);
var pointPosition = new Windows.Foundation.Point((int)point.Position.X, (int)point.Position.Y);
Position = new PointF((float)pointPosition.X, (float)pointPosition.Y);
UpdateOverlayImage();
}
(PointF) is a floating point point of ImageSharp. With the code above my text placement is always a bit off, like 50 pixels in each direction.
I also tried it with the following code, but this just makes the text placed wrongly in the opposite direction:
GeneralTransform transform = ImageOverlay.TransformToVisual(Window.Current.Content);
Windows.Foundation.Point coordinatePointToWindow = transform.TransformPoint(pointPosition);
Update:
Someone wanted to know, how I draw the text,s o here it is, this code is inside UpdateOverlayImage():
var watermarkedImage = OverlayImageBase.Clone(ctx => ctx.ApplyWatermark(font, tbWatermarkText.Text, color, Position));
OverlayImage = watermarkedImage.ToBitmap();
And the ApplyWatermark extension method looks like this:
public static IImageProcessingContext ApplyWatermark(this IImageProcessingContext processingContext,
Font font,
string text,
Color color,
PointF position)
{
var textGraphicOptions = new TextGraphicsOptions(true)
{
ColorBlendingMode = SixLabors.ImageSharp.PixelFormats.PixelColorBlendingMode.Normal,
HorizontalAlignment = SixLabors.Fonts.HorizontalAlignment.Center,
VerticalAlignment = SixLabors.Fonts.VerticalAlignment.Center,
};
return processingContext.DrawText(textGraphicOptions, text, font, color, position);
}
Any ideas why my coordinates are off?
I found out what was wrong. As the displayed image inside the ContentDialog is scaled, the coordinates of the drawn text cannot be directly the same coordintes as received from the pointer pressed event (of course).
So we have to calculate the relative to the size of the scaling. This is how I could solve it:
private void ImageOverlay_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
e.Handled = true;
PointerPoint ptrPt = e.GetCurrentPoint(ImageOverlay);
// As the image is scaled we have to caluclate the position inside the image based on the position of the pointer pressed
float imagePressedX = (float)(OriginalImage.PixelWidth / ImageOverlay.ActualWidth * ptrPt.Position.X);
float imagePressedY = (float)(OriginalImage.PixelHeight / ImageOverlay.ActualHeight * ptrPt.Position.Y); ;
Position = new PointF(imagePressedX, imagePressedY);
UpdateOverlayImage();
}
What I really want is a way to have a negative stroke Thickness value on a WPF shape such as an ellipse, so that the stoke outline paints outwards towards LEFT and TOP of Shape, rather than inside of the shape, over writing my text when I make the thinkness of the stroke too thick... I want the radius of my ellipse to stay constant, but the stroke to grow outwards with increased thinkness, and the LEFT, TOP placement of the shape to remain contant with the inner fill staying the same size and not getting covered up by stroke as it is increased in size.
I tried DropShadowEffect, but its kind of too blurry and not well defined enough...and looks kind of messy... really I just want a solid line going around the outside of the shape...
As you can see from attached picture above, I tried to put shadow around two the ellipses using this code below. the problem is that I want it to be a solid color around the outside like a scaletransform of another ellipse of a different color.
var e = new Ellipse();
DropShadowEffect effect = new DropShadowEffect();
effect.Color =Colors.Orange;
effect.Direction = 0;
effect.BlurRadius = 30;
effect.ShadowDepth = 4;
effect.Opacity=0;
e.Effect = effect;
t.Text = string.Format("abc");
t.Measure(new Size(gwin.XStep, gwin.YStep));
t.Arrange(new Rect(t.DesiredSize));
e.StrokeThickness = 2;
e.Stroke = Brushes.Black;
canvas.Children.Add(e);
canvas.Children.Add(t);
Another possible direction towards solving the problem:
<Ellipse RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform/>
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
Convert to c# code and place one scaletransform ellipse centered inside another scaled transform ellipse of different colors... not sure how to set it up though.
Solution:
Based on suggestion below. I tried creating a grid, setting the width and height of the grid to the size of my ellipse, then adding two ellipses to the grid with different colors and one with a margin set to -10. and it works perfectly ... just need to place the larger ellipse with margin -10 behind the other ellipse when adding it to the grid...here's what it looks like now..
Solution is in here somewhere:
g = new Grid();
e = new Ellipse();
h = new Ellipse();
t = new TextBlock();
t.HorizontalAlignment = HorizontalAlignment.Center;
t.VerticalAlignment = VerticalAlignment.Center;
t.FontWeight = FontWeights.ExtraBold;
g.Children.Add(h);
g.Children.Add(e);
g.Children.Add(t);
gwin.canvas.Children.Add(g);
t.Text = String.Format("{0}.{1}", x, y);
g.Width = gwin.XStep;
g.Height = gwin.YStep;
Canvas.SetLeft (g, gwin.X1 + gwin.XStep*x*2);
Canvas.SetTop (g, gwin.Y1 + gwin.YStep*y*2);
e.StrokeThickness = 2;
e.Stroke = Brushes.Black;
h.Margin = new Thickness(-10);
You can use double ellipses inside a grid overlaying each other like this:
<Grid Width="100" Height="100">
<Ellipse Fill="Black" Margin="-10"/>
<Ellipse Fill="Red" />
</Grid>
The size of this compound is still 100x100 even though the first ellipse is bigger and rendered out of its boundaries.
You may also use a Path and then do this
I think there is something like border. Or you can draw one elipse and then a second one in smaller that has the background color.
I have a series of rectangles in which the user can add images to, by dragging the images in.
The image is then scaled down in proportion and the rectangle is then filled with an ImageBrush.
I need for the user to be able to manipulate the image within the rectangle to fit their needs. Like any photo collage app does.
My question is: How can I show the full, unmasked image on top of the rectangle so that the user can manipulate it to their needs? I don't know where to start with this one.
private async void Mask_Drop(object sender, DragEventArgs e)
{
Rectangle maskSq = e.OriginalSource as Rectangle;
var maskW = maskSq.Width.ToSingle();
var maskH = maskSq.Height.ToSingle();
double maskX = Canvas.GetLeft(maskSq);
double maskY = Canvas.GetTop(maskSq);
// Image sizes for bounding to mask
float boundH = Convert.ToSingle(size.Height);
float boundW = Convert.ToSingle(size.Width);
maskSq.Fill = new ImageBrush
{
ImageSource = new BitmapImage(new Uri("ms-appdata:///local/" + SelectedImage.Name, UriKind.Absolute)),
Stretch = Stretch.UniformToFill
};
}
private void Tap_Collage(object sender, TappedRoutedEventArgs e)
{
//Gets the full image from ImageBrush
ImageBrush brush = (ImageBrush)(((Rectangle)sender).Fill);
Rectangle rect = sender as Rectangle;
//Mask sure rectangle does not drag, just the image brush
rect.CanDrag = false;
rect.StrokeThickness = 6;
//Drag Image Functionality
rect.ManipulationDelta += ImageManipulation.Resize_ImageEdit;
ImageManipulation.ImageEdit_Drag = new TranslateTransform();
brush.Transform = ImageManipulation.ImageEdit_Drag;
//Zoom Image Functionality
ImageManipulation.ImageEdit_Zoom = new ScaleTransform();
brush.RelativeTransform = ImageManipulation.ImageEdit_Zoom;
}
Class
public static class ImageManipulation
{
public static TranslateTransform ImageEdit_Drag;
public static ScaleTransform ImageEdit_Zoom;
public static RotateTransform ImageEdit_Rotate;
public static void Resize_ImageEdit(object sender, ManipulationDeltaRoutedEventArgs e)
{
ImageEdit_Drag.X += e.Delta.Translation.X;
ImageEdit_Drag.Y += e.Delta.Translation.Y;
ImageEdit_Zoom.ScaleX *= e.Delta.Scale;
ImageEdit_Zoom.ScaleY *= e.Delta.Scale;
if (ImageEdit_Zoom.ScaleX < 1.0)
{
ImageEdit_Zoom.ScaleX = 1.0;
ImageEdit_Zoom.ScaleY = 1.0;
}
ImageEdit_Rotate.Angle += e.Delta.Rotation;
}
}
XAML
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:CollageGrid">
<Rectangle Width="{Binding CollageW}" Height="{Binding CollageH}" AllowDrop="True" CanDrag="True" Fill="Transparent"
Drop="Mask_Drop"
DragOver="Mask_DragOver"
ManipulationMode="TranslateX, TranslateY" Stroke="Black" StrokeThickness="2" DragEnter="Mask_DragEnter" DragLeave="Mask_DragLeave" Tapped="Tap_Collage">
<Rectangle.RenderTransform>
<TranslateTransform X="{Binding CollageX}" Y="{Binding CollageY}"/>
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
Example of what I'm looking to acheive:
How it looks currently:
You can try to replace the Rectangle with the Polyline to draw the Rectangle so that you can access the Image which is on the bottom of the Rectangle.
<Grid>
<Image Source="Assets/image1.jpg" Width="800"
Height="400" Tapped="Image_Tapped" />
<Polyline Stroke="Black" HorizontalAlignment="Center"
VerticalAlignment="Center"
StrokeThickness="4" Tapped="Polyline_Tapped"
Points="0,0,0,200,200,200,200,0,0,0" />
</Grid>
---Update---
UniformToFill will cause the image is resized to fill the destination dimensions while it preserves its native aspect ratio. If the aspect ratio of the destination rectangle differs from the source, the source content is clipped to fit in the destination dimensions. So it is not suitable for your requirement. You should manually scale the Image to make image fit one of your Rectangle's border and keep other part out of the Rectangle.
It seems you put the image as the Rectangle's background image brush, there are no other place to display the image out of the Rectangle. So I think, we may take a considerition for a new scenario.
As my pervious reply, using Image control to display the image and Polylines to draw a Rectangle above the Image so that we can operate the Image which is below the Rectangle using the manipulation events to fit the rectangle, meanwhile we can also use the community toolkit BackdropBlurBrush on the xaml layout to Blur the outer image.
I'm having issues trying to not only clip, but to "extract" a portion of a WPF Canvas. So basically I would like the "Clip" to expand to the full size of the window, or convert the clipped item to separate UI Element for exporting to PNG. I write pseudocode because the real code comes from an Autocad model.
double oPrintWidth=1169;
Canvas c = new Canvas();
c.Width = oPrintWidth * 2.54;
c.Height = c.Width * ratio;
// Define the path to clip
string thisPathData = "M12233 M222333 M3443" // fake
c.Clip = Geometry.Parse(thisPathData);
At this point I have the same size canvas but everything other than my path is now black. And the path is still in the original position. I need to now make the clip the entire canvas.
I have played with RenderTransform but I'm lost as what to do next, I'm not so good with matrix calculations.
Original Canvas (Collection of UI Elements)
AFTER CLIP
DESIRED RESULT
Ultimately this would be printed but would prefer to keep it in WPF until last minute to retain VECTOR properties for translating to SVG/XPS/ETC
To make a Clip of the entire Canvas and then apply that Clip to the Canvas I recommend you let WPF do it for you be setting the ClipToBounds property:
Canvas c = new Canvas();
c.ClipToBounds = true;
If that doesn't suit your needs, I would look at the Margin, ActualWidth, and ActualHeight properties to determine the clip region. Then create a RectangleGeometry that matches the size of your Canvas.
EDIT in response to your comments.
Well, I've had some time to work at it some more. What I have been able to do is create a clip region, then I transformed the canvas so that the clip region filled the canvas as much as possible. I think this is what you are after...
First of all I needed to measure the clipped region:
Rect bounds = canvas.Clip.Bounds;
double scaleX = c.Width / (bounds.Right - bounds.Left);
double scaleY = c.Height / (bounds.Bottom - bounds.Top);
This scaling information is used to make the clipped region fit exactly to the size of the canvas.
Now, we need to apply transformations to the canvas:
TransformGroup group = new TransformGroup();
TranslateTransform move = new TranslateTransform(-bounds.Left, -bounds.Top);
ScaleTransform scale = new ScaleTransform(scaleX, scaleY);
group.Children.Add(move);
group.Children.Add(scale);
canvas.RenderTransform = group;
So what is happening here? First of all, the objective is to apply a couple transformations. We need to center the clipped region (translation) and we need to make the clipped region larger (scale). Now, when I say clipped region, I mean the contents of that region. In actuality, we are moving the canvas's rendered output. Moving the region bounds is not what we want to do.
To do this in WPF, we need to add each transformation we want to a child of a TransformGroup.
In this case, we are translating the canvas's output so that its top-left corner is (0, 0) This is necessary because afterwards we will scale the rendered output. So, now, we need to scale the canvas's output so that the image fits as large as it can. To do this, we need to create a ratio that compares the canvas size to the clipped region size.
Here is the formula for scaling the output:
ratio = canvasSize / clippedSize
scaledSize = clippsedSize * ratio
Now, scaling the canvas's output will allow the clipped region to appear as large as possible.
Take a look at the results. Here are images demonstrating the canvas's output before and after the transformations are applied:
Before
After
I figure I might as well give you all the code I used:
Canvas c = new Canvas();
double oPrintWidth=100;
double ratio = .89;
c.Width = oPrintWidth * 2.54;
c.Height = c.Width * ratio;
c.Background = new ImageBrush((ImageSource)FindResource("TestImage")) { Stretch = Stretch.UniformToFill };
// Define the path to clip
string newPath = "M 64,64 L 64,128 128,128, 128,64 Z";
c.Clip = Geometry.Parse(newPath);
Rect bounds = c.Clip.Bounds;
double scaleX = c.Width / (bounds.Right - bounds.Left);
double scaleY = c.Height / (bounds.Bottom - bounds.Top);
TransformGroup group = new TransformGroup();
TranslateTransform move = new TranslateTransform(-bounds.Left, -bounds.Top);
ScaleTransform scale = new ScaleTransform(scaleX, scaleY);
group.Children.Add(move);
group.Children.Add(scale);
c.RenderTransform = group;
MyBorder.Child = c;
And the XAML:
<Window.Resources>
<BitmapImage UriSource="uvtest.jpg" x:Key="TestImage"/>
</Window.Resources>
<Grid Background="Gray">
<Border x:Name="MyBorder" Background="White" BorderBrush="Black" BorderThickness="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
I have the following rectangle, which is filled with color. i want to add some text inside in middle of the box. please suggest some solution.
var rect1 = new Rectangle
{
Stroke = new SolidColorBrush(Colors.Red),
Fill = new SolidColorBrush(Colors.Black),
Width = 150,
Height = 100,
VerticalAlignment = System.Windows.VerticalAlignment.Top,
HorizontalAlignment = System.Windows.HorizontalAlignment.Left
};
Grid.SetRow(rect, 0);
TGrid2.Children.Add(rect1);
Basically i want to make a drag and drop feature. where rect1 will be dragged over rect2; which will only have border color and transparent Fill-body and on drop replaces rect2 with dropped rect1.
Thats why i made a rectangle and now trying to figure how to add text in rectangle as content in center, similar like a button.
If you absolutely must use a rectangle, you could fake it!
<Grid>
<Rectangle Fill="Red"/>
<TextBlock Text="Hi there" Margin="5"/>
</Grid>
Replace the Rectangle by a Grid or a Border and put a TextBlock inside.