A TextBlock should be centered to a position x (or y when Orientation is vertical). I implemented:
TextBlock应居中于位置x(或方向为垂直时为y)。我实施了:
TextBlock text = new TextBlock();
// Some code to define text, font, etc. here
// Turn if Orientation is vertical
if (Orientation == Orientation.Vertical)
{
text.RenderTransform = new RotateTransform() { Angle = 270 };
}
// Update, then ActualWidth is set correctly
text.UpdateLayout();
// Position of label centered to given position
double halfWidth = text.ActualWidth / 2;
double x1 = (Orientation == Orientation.Horizontal) ? x - halfWidth : x;
double y1 = (Orientation == Orientation.Horizontal) ? y : y + halfWidth;
Canvas.SetLeft(text, x1);
Canvas.SetTop(text, y1);
Children.Add(text); // Add to Canvas
This works actual fine, but is it possible to do this without UpdateLayout
. If I remove UpdateLayout
, then I do not get the position I am looking for, because ActualWidth is (of course) zero.
这工作正常,但是可以在没有UpdateLayout的情况下执行此操作。如果我删除UpdateLayout,那么我没有得到我正在寻找的位置,因为ActualWidth(当然)为零。
2 个解决方案
#1
3
You might be able to do that by binding the Canvas.Top
/Canvas.Left
values to the TextBlock's ActualWidth
/ActualHeight
and using a Converter
.
您可以通过将Canvas.Top/Canvas.Left值绑定到TextBlock的ActualWidth / ActualHeight并使用转换器来实现。
Here's an example. I'm using a custom MathConverter
that I usually use for mathematical formulas (code can be found here), but you could also use a plain converter that returns half of whatever value it gets passed.
这是一个例子。我正在使用我通常用于数学公式的自定义MathConverter(代码可以在这里找到),但你也可以使用一个普通的转换器,它返回传递的任何值的一半。
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Canvas.Left"
Value="{Binding RelativeSource={RelativeSource Self},
Path=ActualWidth,
Converter={StaticResource MathConverter},
ConverterParameter=@VALUE/2}" />
</Trigger>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="Canvas.Top"
Value="{Binding RelativeSource={RelativeSource Self},
Path=ActualHeight,
Converter={StaticResource MathConverter},
ConverterParameter=@VALUE/2}" />
</Trigger>
</Style.Triggers>
</Style>
Edit
编辑
Just re-read the question and realized you're trying to center the TextBlock at a specific x,y coordinate on the Canvas. In that case, you'll need to implement a MultiConverter
instead of a regular Converter
so that you can pass it two parameters: the X/Y value, and the ActualHeight/ActualWidth value
只需重新阅读该问题,并意识到您正试图将TextBlock置于Canvas上特定的x,y坐标中心。在这种情况下,您需要实现MultiConverter而不是常规Converter,以便您可以传递两个参数:X / Y值和ActualHeight / ActualWidth值
#2
0
I assume that you wrote this code in the constructor. It would be a reason why ActualWidth has no value. All the code hasn't been tested (no IDE available). You need to do this after the Loaded Event, after WPF has built the layout. This is a routed event, btw.
我假设你在构造函数中编写了这段代码。这就是为什么ActualWidth没有值的原因。所有代码都未经过测试(没有IDE可用)。在WPF构建布局之后,您需要在Loaded事件之后执行此操作。这是一个路由事件,顺便说一下。
public class Class1{
public Class1 ()
{
this.Loaded += (sender, args) =>
{
TextBlock text = new TextBlock();
if (Orientation == Orientation.Vertical)
{
text.RenderTransform = new RotateTransform() { Angle = 270 };
}
double halfWidth = text.ActualWidth / 2;
double x1 = (Orientation == Orientation.Horizontal) ? x - halfWidth : x;
double y1 = (Orientation == Orientation.Horizontal) ? y : y + halfWidth;
Canvas.SetLeft(text, x1);
Canvas.SetTop(text, y1);
Children.Add(text);
};
}
This code will probably work. Of course, the way I read your code, this code seems to be situated in the constructor from a class that you derived from Canvas. There should normally be no need to do so, except when you really need to extend the core functionality of the canvas control. You can make reusable components on top of existing controls by creating a UserControl. You should especially use this approach if you don't need to override any method of the Canvas.
这段代码可能会有效。当然,在我阅读代码的方式中,这段代码似乎位于您从Canvas派生的类的构造函数中。通常应该没有必要这样做,除非您真的需要扩展canvas控件的核心功能。您可以通过创建UserControl在现有控件之上创建可重用组件。如果您不需要覆盖Canvas的任何方法,则应特别使用此方法。
Alternatively, if you just want to have a centered item inside a container, the following xaml will do that just fine:
或者,如果你只想在容器中有一个居中的项目,下面的xaml就可以做到这一点:
<Grid>
<TextBlock Text="Haha!" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
#1
3
You might be able to do that by binding the Canvas.Top
/Canvas.Left
values to the TextBlock's ActualWidth
/ActualHeight
and using a Converter
.
您可以通过将Canvas.Top/Canvas.Left值绑定到TextBlock的ActualWidth / ActualHeight并使用转换器来实现。
Here's an example. I'm using a custom MathConverter
that I usually use for mathematical formulas (code can be found here), but you could also use a plain converter that returns half of whatever value it gets passed.
这是一个例子。我正在使用我通常用于数学公式的自定义MathConverter(代码可以在这里找到),但你也可以使用一个普通的转换器,它返回传递的任何值的一半。
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Canvas.Left"
Value="{Binding RelativeSource={RelativeSource Self},
Path=ActualWidth,
Converter={StaticResource MathConverter},
ConverterParameter=@VALUE/2}" />
</Trigger>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="Canvas.Top"
Value="{Binding RelativeSource={RelativeSource Self},
Path=ActualHeight,
Converter={StaticResource MathConverter},
ConverterParameter=@VALUE/2}" />
</Trigger>
</Style.Triggers>
</Style>
Edit
编辑
Just re-read the question and realized you're trying to center the TextBlock at a specific x,y coordinate on the Canvas. In that case, you'll need to implement a MultiConverter
instead of a regular Converter
so that you can pass it two parameters: the X/Y value, and the ActualHeight/ActualWidth value
只需重新阅读该问题,并意识到您正试图将TextBlock置于Canvas上特定的x,y坐标中心。在这种情况下,您需要实现MultiConverter而不是常规Converter,以便您可以传递两个参数:X / Y值和ActualHeight / ActualWidth值
#2
0
I assume that you wrote this code in the constructor. It would be a reason why ActualWidth has no value. All the code hasn't been tested (no IDE available). You need to do this after the Loaded Event, after WPF has built the layout. This is a routed event, btw.
我假设你在构造函数中编写了这段代码。这就是为什么ActualWidth没有值的原因。所有代码都未经过测试(没有IDE可用)。在WPF构建布局之后,您需要在Loaded事件之后执行此操作。这是一个路由事件,顺便说一下。
public class Class1{
public Class1 ()
{
this.Loaded += (sender, args) =>
{
TextBlock text = new TextBlock();
if (Orientation == Orientation.Vertical)
{
text.RenderTransform = new RotateTransform() { Angle = 270 };
}
double halfWidth = text.ActualWidth / 2;
double x1 = (Orientation == Orientation.Horizontal) ? x - halfWidth : x;
double y1 = (Orientation == Orientation.Horizontal) ? y : y + halfWidth;
Canvas.SetLeft(text, x1);
Canvas.SetTop(text, y1);
Children.Add(text);
};
}
This code will probably work. Of course, the way I read your code, this code seems to be situated in the constructor from a class that you derived from Canvas. There should normally be no need to do so, except when you really need to extend the core functionality of the canvas control. You can make reusable components on top of existing controls by creating a UserControl. You should especially use this approach if you don't need to override any method of the Canvas.
这段代码可能会有效。当然,在我阅读代码的方式中,这段代码似乎位于您从Canvas派生的类的构造函数中。通常应该没有必要这样做,除非您真的需要扩展canvas控件的核心功能。您可以通过创建UserControl在现有控件之上创建可重用组件。如果您不需要覆盖Canvas的任何方法,则应特别使用此方法。
Alternatively, if you just want to have a centered item inside a container, the following xaml will do that just fine:
或者,如果你只想在容器中有一个居中的项目,下面的xaml就可以做到这一点:
<Grid>
<TextBlock Text="Haha!" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>