在 WPF 中创建旋转文本
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12628576/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating rotated text in WPF
提问by Jim
I am trying to create some rotated text and save that image to a PNG file. The resulting PNG should be no larger than needed (or minimal padding). I have it working as long as there is no rotation, but as soon as I rotate the text, it is getting clipped off in the file. I am sure it has something to do with adjusting the either the CenterX and CenterY of the RotateTransform or creating a TranslateTransform, but I can't find anything on how to do it correctly and my trial-and-error testing has turned into trial-and-frustration.
我正在尝试创建一些旋转文本并将该图像保存到 PNG 文件。生成的 PNG 不应大于所需(或最小填充)。只要没有旋转,我就让它工作,但是一旦我旋转文本,它就会在文件中被剪掉。我确定这与调整 RotateTransform 的 CenterX 和 CenterY 或创建 TranslateTransform 有关,但我找不到任何关于如何正确执行此操作的信息,并且我的试错测试已变成试验-和 -挫折。
My sample code is below. I looking for a solution that would work with an arbitrary angle and not just -45 degrees.
我的示例代码如下。我正在寻找一种可以使用任意角度而不仅仅是 -45 度的解决方案。
Finally, if someone knows how to meet these requirements, but say using an "old style" Graphics object instead of WPF tools, I am open to that solution to that too.
最后,如果有人知道如何满足这些要求,但说使用“旧式”图形对象而不是 WPF 工具,我也愿意接受该解决方案。
private static void CreateImageFile()
{
FormattedText ft;
Geometry textBox;
string fontName;
Typeface face;
DrawingVisual viz;
RotateTransform rt;
TranslateTransform tt;
Rect rect;
RenderTargetBitmap bmp;
PngBitmapEncoder encoder;
ft = CreateText("Lorem ipsum dolor sit amet, consectetur adipisicing" + Environment.NewLine + "elit, sed do eiusmod tempor", "Verdana", 12, false, false);
textBox = ft.BuildHighlightGeometry(new Point());
fontName = "Arial";
face = new Typeface(fontName);
// now create the visual we'll draw them to
viz = new DrawingVisual();
rt = new RotateTransform() { Angle = -45 };
rect = rt.TransformBounds(ft.BuildHighlightGeometry(new Point(0, 0)).Bounds);
using (DrawingContext dc = viz.RenderOpen())
{
dc.PushTransform(rt);
dc.DrawText(ft, new Point(0, 0));
dc.Pop();
}
bmp = new RenderTargetBitmap((int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Pbgra32);
bmp.Render(viz);
encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
using (FileStream file = new FileStream("TextImage.png", FileMode.Create))
encoder.Save(file);
}
private static FormattedText CreateText(string text, string typeface, double fontSize, bool bold, bool italic)
{
FontStyle fontStyle = FontStyles.Normal;
FontWeight fontWeight = FontWeights.Medium;
if (bold == true) fontWeight = FontWeights.Bold;
if (italic == true) fontStyle = FontStyles.Italic;
// Create the formatted text based on the properties set.
FormattedText formattedText = new FormattedText(
text,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(new FontFamily(typeface),
fontStyle,
fontWeight,
FontStretches.Normal),
fontSize,
Brushes.Black, // This brush does not matter since we use the geometry of the text.
null,
TextFormattingMode.Display
);
return formattedText;
}
Update
更新
Based upon some of the suggestions below, I decided to try a different tack and experiment in the GUI. I created a window like this:
根据下面的一些建议,我决定在 GUI 中尝试不同的策略和实验。我创建了一个这样的窗口:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="160" Width="160" Loaded="Window_Loaded">
<Grid>
<Canvas Name="WorkCanvas" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-45"/>
<TranslateTransform/>
</TransformGroup>
</TextBlock.RenderTransform>This is a test</TextBlock>
</Canvas>
</Grid>
</Window>
As you can see, it uses both the RotateTransformand the suggested RenderTransformOrigin, and the result is like the image below. And, as you can see, the text does notgo through the middle of the Canvas. And thatseems to be my entire problem. How to rotate the text andget it correctly centered.
如您所见,它同时使用了RotateTransform和建议RenderTransformOrigin,结果如下图所示。而且,正如你所看到的,文中未通过的中间去Canvas。而这似乎是我的整个问题。如何旋转文本并使其正确居中。


Update 2
更新 2
I decided to try a Gridinstead of a Canvasthis time and I can now get the text properly centered in the grid, but since I can't use a FormattedTextobject, I can't seem to measure the actual bounding box. All measurements of the TextBlockor the Gridcome back as if it was not rotated at all (looking at ActualWidth, ActualHeight, and DesiredSize). If I can't get the rotated bounding box size, I can't save the PNG without it getting clipped.
这次我决定尝试 aGrid而不是 a Canvas,现在我可以使文本正确地居中在网格中,但是由于我无法使用FormattedText对象,因此我似乎无法测量实际的边界框。的所有测量TextBlock或Grid回来,如果它是不是在所有旋转(在看ActualWidth,ActualHeight和DesiredSize)。如果我无法获得旋转的边界框大小,我将无法保存 PNG 而不会被剪裁。
Oh, and I tried rotating the text in an unrotated grid and rotating the grid itself, both give the same results when trying to determine the dimensions.
哦,我尝试在未旋转的网格中旋转文本并旋转网格本身,在尝试确定尺寸时都给出了相同的结果。
回答by kbo4sho88
You could try to wrap your text in an element that has a rendertransformOrigin. Make you changes to that element. Try a canvas or a grid.
您可以尝试将文本包装在具有 rendertransformOrigin 的元素中。使您更改该元素。试试画布或网格。
回答by Jim
After much poking around and with help from Matt and kbo4sho88, I finally found the correct way of doing it. In addition to the help from the other posters, I finally found that I need to call TransformToVisual and TransformBounds to get the bounding box that I need for the correct file size. But, before that, I had to call Measure and Arrange since these objects are not shown on a screen.
经过多次摸索并在 Matt 和 kbo4sho88 的帮助下,我终于找到了正确的方法。除了其他海报的帮助,我终于发现我需要调用 TransformToVisual 和 TransformBounds 来获取正确文件大小所需的边界框。但是,在此之前,我不得不调用 Measure 和Arrange,因为这些对象没有显示在屏幕上。
Phew!
呼!
private static void CreateImageFile()
{
Grid workGrid;
TextBlock workTextBlock;
RenderTargetBitmap bitmap;
PngBitmapEncoder encoder;
Rect textBlockBounds;
GeneralTransform transform;
workGrid = new Grid()
{
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center
};
workTextBlock = new TextBlock()
{
Text = "Lorem ipsum dolor sit amet, consectetur adipisicing" + Environment.NewLine + "elit, sed do eiusmod tempor",
FontFamily = new FontFamily("Verdana"),
FontSize = 36,
TextAlignment = TextAlignment.Center,
RenderTransformOrigin = new Point(0.5, 0.5),
LayoutTransform = new RotateTransform(-45)
};
workGrid.Children.Add(workTextBlock);
/*
* We now must measure and arrange the controls we just created to fill in the details (like
* ActualWidth and ActualHeight before we call TransformToVisual() and TransformBounds()
*/
workGrid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
workGrid.Arrange(new Rect(0, 0, workGrid.DesiredSize.Width, workGrid.DesiredSize.Height));
transform = workTextBlock.TransformToVisual(workGrid);
textBlockBounds = transform.TransformBounds(new Rect(0, 0, workTextBlock.ActualWidth, workTextBlock.ActualHeight));
/*
* Now, create the bitmap that will be used to save the image. We will make the image the
* height and width we need at 96DPI and 32-bit RGBA (so the background will be transparent).
*/
bitmap = new RenderTargetBitmap((int)textBlockBounds.Width, (int)textBlockBounds.Height, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(workGrid);
encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (FileStream file = new FileStream("TextImage.png", FileMode.Create))
encoder.Save(file);
}
Of course this is just a sample (but working) method and the final one will be parameterized.
当然,这只是一个示例(但有效)方法,最后一个将被参数化。
Final part of the puzzle was found at WPF: Getting new coordinates after a Rotation
在WPF中找到了拼图的最后一部分:在旋转后获取新坐标
回答by Matt Burland
I think what you are missing is that you need to set RenderTransformOriginto 0.5,0.5 so that your rotation transformation is around the center of you image and not the upper left-hand edge.
我认为您缺少的是您需要将RenderTransformOrigin设置为 0.5,0.5,以便您的旋转变换围绕图像的中心而不是左上边缘。
Update
更新
In response to your update above. The problem is using canvas. If you remove your transform altogether, you'll see your TextBlock isn't centered to start with. It actually is rotating around it's center, it's just that the center isn't the center of the canvas. Try this:
回应您的上述更新。问题是使用画布。如果您完全删除您的转换,您会看到您的 TextBlock 一开始就没有居中。它实际上是围绕它的中心旋转,只是中心不是画布的中心。尝试这个:
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock TextAlignment="Center" RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<RotateTransform Angle="-45"/>
</TextBlock.RenderTransform>
This is a test
</TextBlock>
</Grid>

