Adding a line to a textblock programmatically - c#

I know how to add a control to the canvas/grid/layout- simply by calling canvas.Childern.Add(). However, when I want to embed something inside a textblock, I can't seem to find the method for it. A textblock doesn't contain a Add method or anything, so I'm at a bit of a lost.
The XAML I'm trying to turn into C# is:
<TextBlock x:Name="textBlock">
<Line X1="0" Y1="0" X2="100" Y2="0" Stroke="Black" StrokeThickness="4" x:Name="line1"/>
<TextBlock Text="Hello there!" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="innerTextBlock" />
<Line X1="0" Y1="0" X2="100" Y2="0" Stroke="Black" StrokeThickness="4" x:Name="line2"/>
</TextBlock>
EDIT: I think the best way to do it (besides the answer) is to simply create a WPF User control and reference that.

You have to use inlines property (as stated before) so to reproduce your xaml it is enough to do the following (where LayoutRoot is the name of your parent control):
var t = new TextBlock();
t.Inlines.Add(new Line { X1 = 0, Y1 = 0, X2 = 100, Y2 = 0, Stroke = new SolidColorBrush(Colors.Black), StrokeThickness = 4.0 });
t.Inlines.Add("Hello there!");
t.Inlines.Add(new Line { X1 = 0, Y1 = 0, X2 = 100, Y2 = 0, Stroke = new SolidColorBrush(Colors.Black),StrokeThickness = 4.0});
LayoutRoot.Children.Add(t);

I Believe if you have multiple lines you must use the Inlines property which is a collection that contains a list of inline elements. You can't directly add text to it, you must add it to an Inline object - such as a Run.

Related

WPF Rectangle Label Blurry

I'm using the code below to draw text over a rectangle in a WPF canvas but it seems to stretch/squash the text and sometimes the back colour does not fill the entire box.
I'm looking for a way to make sure the box is always filled and the text is clear. Probably some form of dynamic font sizing?
Thanks.
Rectangle elip = new Rectangle();
elip.Height = 6;
elip.Width = 6;
Brush brush = new SolidColorBrush(Color.FromRgb(n.Value.R,
n.Value.G, n.Value.B));
Label TB = new Label();
TB.HorizontalAlignment = HorizontalAlignment.Stretch;
TB.Margin = new Thickness(0, 0, 0, 0);
TB.Background = brush;
TB.FontSize = 12;
TB.HorizontalContentAlignment = HorizontalAlignment.Center;
TB.Content = n.Value.Stations[0].TrackId;
BitmapCacheBrush bcb = new BitmapCacheBrush(TB);
elip.Fill = bcb;
elip.Stroke = Brushes.Black;
elip.StrokeThickness = 0.5;
elip.MouseDown += ElipOnMouseDown;
Canvas.SetTop(elip, n.Value.Y - elip.Width / 2);
Canvas.SetLeft(elip, n.Value.X - elip.Height / 2);
cMain.Children.Add(elip);
You can stretch the text using a viewbox.
I recommend using a usercontrol to encapsulate all your markup for things like this. You would have to have many thousands of these things before the overhead of a usercontrol vs building rectangles and whatnot would be significant.
I built this usercontrol:
Height="18" Width="24">
<Border BorderBrush="Black"
BorderThickness="1"
Background="Magenta"
>
<Viewbox Stretch="Uniform">
<TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=UserControl}}"
TextAlignment="Center"
Margin="2"
/>
</Viewbox>
</Border>
</UserControl>
Might not be precisely what you want in terms of width, height or whatever.
You could instantiate one of these, set it's Tag, canvas top and left and add it to the canvas.
Here's the equivalent markup I used to prove it:
Title="MainWindow" >
<Grid>
<Canvas>
<local:StationView Canvas.Left="20"
Canvas.Top="100"
Tag="16B"/>
</Canvas>
</Grid>
</Window>

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>

Uwp more deep shadow effect for text or textblock

I am trying to make more deep black shadow effect to make readable text , infront white background .
I am using this code
c# void method of loaded
var compositor = ElementCompositionPreview.GetElementVisual(this.grid).Compositor;
var brush = compositor.CreateColorBrush(Colors.Black);
var spriteVisual = compositor.CreateSpriteVisual();
spriteVisual.Size = this.grid.RenderSize.ToVector2();
var dropshadow = compositor.CreateDropShadow();
dropshadow.Mask = txtBlock.GetAlphaMask();
dropshadow.Color = brush.Color;
dropshadow.BlurRadius = 9.5f;
dropshadow.Opacity = 1.9f;
spriteVisual.Shadow = dropshadow;
ElementCompositionPreview.SetElementChildVisual(this.grid, spriteVisual);
xaml
<Grid Background="White">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid Loaded="grid_Loaded"
x:Name="grid" />
<TextBlock IsTextSelectionEnabled="True"
x:Name="txtBlock"
Text="Drop Shadow"
Foreground="White" Style="{StaticResource BodyTextBlockStyle}"
HorizontalAlignment="Center"
VerticalAlignment="Center" ></TextBlock>
</Grid>
</Grid>
Result
[![Result image][1]][1]
My Problem it is not enough black color shadow effect or opacity to watch the text .
Result
http://i.imgur.com/d6FglQT.png
I am trying to make that black effect more deep,
My Target to make shadow effect like this
Target
http://i.imgur.com/bOiRnCS.png

Change the parent of a WPF element after a rotation (setting new coordinates issue)

I need to change the parent of elements. (for group/ungroup shapes)
But I cant set the new position for an element if it has a rotation.
I saw this ,this , this and this pages and many other ways, but none worked correctly.
Please see my sample project and the following image:
The parent of Rect1 is ChildCanvas1 and The parent of Rect2 is ChildCanvas2, I want to move the Rect1 and Rect2 to the MainCanvas. (and remove the ChildCanvas and ChildCanvas2)
I don't have any problem to do that for the Rect1 because it has not any rotation.
But the Rect2 has a rotation (-20 degree) and I cant set the new coordinates for it correctly.
Please see this image:
How to change the parent of an element after a rotation and setting new coordinates correctly?
UPDATE:
Note I need a general way (for the group/ungroup elements in a big app that each element (maybe) has TranslateTransform and SkewTransform and RotateTransform and ScaleTransform)
XAML:
<Canvas x:Name="MainCanvas">
<Canvas x:Name="ChildCanvas1" Width="500" Height="250" Background="Bisque" Canvas.Top="54">
<Rectangle x:Name="Rect1" Width="200" Height="100" Fill="Red" Canvas.Left="150" Canvas.Top="100"/>
</Canvas>
<Canvas Name="ChildCanvas2" Width="500" Height="250" Background="Bisque" Canvas.Left="516" Canvas.Top="54">
<Rectangle Name="Rect2" Width="200" Height="100" Fill="Red" Canvas.Left="150" Canvas.Top="100">
<Rectangle.RenderTransform>
<TransformGroup>
<SkewTransform AngleX="-40"/>
<RotateTransform Angle="-20"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
<Button Name="btn1" Click="btn1_Click" Content="Move Rect1 to MainCanvas and Remove ChildCanvas1" Width="356" Height="30" Canvas.Left="59" Canvas.Top="310"/>
<Button Name="btn2" Click="btn2_Click" Content="Move Rect2 to MainCanvas and Remove ChildCanvas2" Width="350" Height="30" Canvas.Left="590" Canvas.Top="310"/>
C# code :
GeneralTransform transform = Rect2.TransformToVisual(MainCanvas);
Rect rect = transform.TransformBounds(new Rect(0, 0, Rect2.Width, Rect2.Height));
double ChildCanvas2Left = Canvas.GetLeft(ChildCanvas2);
double ChildCanvas2Top = Canvas.GetLeft(ChildCanvas2);
ChildCanvas2.Children.Remove(Rect2);
MainCanvas.Children.Add(Rect2);
Canvas.SetLeft(Rect2, rect.Left);
Canvas.SetTop(Rect2, rect.Top);
MainCanvas.Children.Remove(ChildCanvas2);
You need to recalculate the Rect2 after moving the Rect2 to the MainCanvas. Like this:
Canvas.SetTop(Rect2, Canvas.GetTop(Rect2) + Canvas.GetTop(ChildCanvas2));
The complete code:
private void btn2_Click(object sender, RoutedEventArgs e)
{
GeneralTransform transform = Rect2.TransformToVisual(MainCanvas);
Rect rect = transform.TransformBounds(new Rect(0, 0, Rect2.Width, Rect2.Height));
ChildCanvas2.Children.Remove(Rect2);
MainCanvas.Children.Remove(ChildCanvas2);
Canvas.SetLeft(Rect2, rect.Left);
Canvas.SetTop(Rect2, Canvas.GetTop(Rect2) + Canvas.GetTop(ChildCanvas2));
MainCanvas.Children.Add(Rect2);
}
However Rect2.TransformToVisual and transform.TransformBounds are not necessary in your case and you can do it more cleaner and easier without them and get same result. Like this:
ChildCanvas2.Children.Remove(Rect2);
MainCanvas.Children.Remove(ChildCanvas2);
Canvas.SetLeft(Rect2, Canvas.GetLeft(Rect2) + Canvas.GetLeft(ChildCanvas2));
Canvas.SetTop(Rect2, Canvas.GetTop(Rect2) + Canvas.GetTop(ChildCanvas2));
MainCanvas.Children.Add(Rect2);
EDIT: A general way:
ChildCanvas2.Children.Remove(Rect2);
MainCanvas.Children.Remove(ChildCanvas2);
Canvas.SetLeft(Rect2, Canvas.GetLeft(Rect2) + Canvas.GetLeft(ChildCanvas2));
Canvas.SetTop(Rect2, Canvas.GetTop(Rect2) + Canvas.GetTop(ChildCanvas2));
Canvas.SetRight(Rect2, Canvas.GetRight(Rect2) + Canvas.GetRight(ChildCanvas2));
Canvas.SetBottom(Rect2, Canvas.GetBottom(Rect2) + Canvas.GetBottom(ChildCanvas2));
MainCanvas.Children.Add(Rect2);
I have changed RenderTransform to LayoutTransform of Rect2:
<Rectangle Name="Rect2" Width="200" Height="100" Fill="Red" Canvas.Left="150" Canvas.Top="100">
<Rectangle.LayoutTransform>
<RotateTransform Angle="-20"/>
</Rectangle.LayoutTransform>
</Rectangle>
Now Rect2 looks like below:
When I press the second button I see this result:
So, Rect2 stays at the original position.

best way for clickable image map in wpf

I have an image with many parts in c# WPF
I want make each part click make think
I have tried to split the image to parts and make event on each
image but the problem is the nested part of images
what is the best way to make image map ?
You can do it easily with Expression Design which is included in Microsoft Expression Studio. This is steps you gonna do:
Add image to Expression Design.
Then you can use PaintBrush tool for split image into parts as you want.
Then you must export this to xaml. In export window you can choose Xaml Silverlight 3
Canvas as format and Paths as Text.
As you understand, it automatically converts objects you draw on your image to path object with all coordinates on it.
Then you can copy exported xaml and paste it to your application.
You can download Expression Studio from Dreamspark for free.
I have just made sample and exported it to xaml:
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="Untitled1" Width="62" Height="62" Clip="F1 M 0,0L 62,0L 62,62L 0,62L 0,0" UseLayoutRounding="False">
<Canvas x:Name="Layer_1" Width="62" Height="62" Canvas.Left="0" Canvas.Top="0">
<Image x:Name="Image" Source="Untitled1_files/image0.png" Width="1920" Height="1080" Canvas.Left="0" Canvas.Top="0">
<Image.RenderTransform>
<TransformGroup>
<MatrixTransform Matrix="1,0,0,1,-929.667,-510.667"/>
</TransformGroup>
</Image.RenderTransform>
</Image>
<Path x:Name="Path" Width="159.722" Height="161.743" Canvas.Left="82.757" Canvas.Top="-0.415951" Stretch="Fill" Fill="#FFE7DEDE" Data="F1 M 82.8307,30.8333C 81.8859,46.01 90.3304,60.6249 90.3304,75.831C 90.3304,88.8304 91.9427,101.93 90.3304,114.829C 89.0281,125.247 87.0101,136.367 90.3304,146.327C 95.3301,161.327 119.518,161.327 135.328,161.327C 157.018,161.327 175.778,144.86 193.825,132.828C 209.523,122.363 235.198,120.495 241.823,102.83C 243.994,97.0391 240.326,90.2367 237.323,84.8306C 230.656,72.8294 223.759,60.756 214.824,50.3323C 205.057,38.9377 205.748,18.0458 192.325,11.3342C 183.723,7.03329 173.332,8.29683 163.827,6.83447C 144.945,3.92956 125.479,-3.30947 106.83,0.834766C 94.3289,3.61269 83.6265,18.0524 82.8307,30.8333 Z "/>
</Canvas>
</Canvas>
The exported part is Path object. You can do whatever you want on it. For example you can handle MouseClick event for this path and change background of path....
You could overlay the image with a set of transparent shapes:
<Canvas>
<Image Source="..."/>
<Ellipse Canvas.Left="50" Canvas.Top="50" Width="100" Height="50"
Fill="Transparent" MouseDown="Ellipse_MouseDown"/>
<Path Data="..." MouseDown="Path_MouseDown"/>
</Canvas>
These shapes could be simple Rectangles or Ellipses, or more or less complex Polygons or Paths.
Its very simple. rather than go to any complicated way follow this,It's simplest
1)First declare your image in XAML
<Grid Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Name="imagePath" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
2)Find mouse position on image itself to get x and y co-ordinate
String[] arr = new String[2];
var mousePos = Mouse.GetPosition(imagePath);
arr = mousePos.ToString().Split(',');
double x = Double.Parse(arr[0].ToString());
double y = Double.Parse(arr[1].ToString());
3)Declare area where you wants to get clickable area or mousehover
if (x >= 10 && y >= 10 && x <= 20 && y <= 20//this declares square area with x1,y1,x2,y2
{
//do whatever you want to do
//don't forget to add left and top each time
left = left +x;//x=10 i.e x1
top = top + y;//y=20 i.e y1
}
4)Add this x1 and y1 each time you move on image
int left = 0;
int top = 0;
Entire code;ll look like this
InitializeComponent();
imagePath.MouseMove += new MouseEventHandler(myMouseMove);
private void myMouseMove(object sender, MouseEventArgs e)
{
String[] arr = new String[2];
var mousePos = Mouse.GetPosition(imagePath);
arr = mousePos.ToString().Split(',');
double x = Double.Parse(arr[0].ToString());
double y = Double.Parse(arr[1].ToString());
int x = (int)xx;
int y = (int)yy;
int left = 0;
int top = 0;
Console.WriteLine("Screen Cordinate-------------------------" + x + ", " + y);
for (int i = 0; i < n; i++)
{
if (x >= x1 && y >= y1 && x <= x2 && y <= y2
{
Mouse.OverrideCursor = Cursors.Hand;
left = left + x1;
top = top + y1;
break;
}
else
{
Mouse.OverrideCursor = Cursors.Arrow;
}
}
where x1,y1,x2,y2 are the cordinates on image
Thats it!!!
I think use Adorner to implement the funtions is best way.
Here is smaple for add control on image (image annotating) and Adorner overview in MSDN

Categories