如何垂直“居中"对齐多行文本 [英] How to Vertically "center" align the multi line text
问题描述
对不起,如果标题有点误导,我很难授予它.它也不完全是中心".(更多解释见下文)
Sorry if the title a bit misleading, I have difficulty to entitle it. It's also not exactly "center". (see below for further explanations)
您好,我要制作一个项目来编写编号的乐谱.但是我发现了调平字体"的障碍.(视觉见下图)
Hi, I am going to make a project to write numbered musical scores. But I found an obstacle on leveling the "font". (See below picture for visual)
- 我需要
1 2 3 3 1 2 3 4 4
在一条直线上 - 我使用
WrapPanel
作为我的笔记容器(如在屏幕中).
- I need
1 2 3 3 1 2 3 4 4
to be in a straight "line" - I use
WrapPanel
as the container of my notes (as in the screen).
主要问题在于音高点,它位于音符上方或下方EITHER.音高绑定到音符,所以我需要在 1 User Control 中处理它.但是,如果是这样,那么我无法控制注释字体"的位置在一行中.
The main problem is on the pitch dot, which located EITHER above or below the note. Pitch is bound to the note, so I need process it in 1 User Control. But, if so, then I can't control the placement of the note "font" to be in one line.
下面是我的笔记用户控制代码:
Below is my note user control code :
XAML
<UserControl x:Class="RevisiConverter.NumericNoteBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Width="{Binding ActualWidth, ElementName=txbNote}"
Height="{Binding ActualHeight, ElementName=mainStack}">
<StackPanel Name="mainStack">
<StackPanel Name="topStack">
</StackPanel>
<Canvas Name="canvas" Width="{Binding ActualWidth, ElementName=txbNote}" Height="{Binding ActualHeight, ElementName=txbNote}"
HorizontalAlignment="Center">
<TextBlock Name="txbNote" Text="1" Foreground="Black" FontSize="15"
Margin="0,0,0,-3" FontFamily="Courier" Canvas.Top="0" Canvas.Left="0"/>
</Canvas>
<StackPanel Name="botStack">
</StackPanel>
</StackPanel>
XAML.CS
public partial class NumericNoteBox : UserControl
{
private Note _child;
private Line _sharp;
public Note Child
{
get { return _child; }
set
{
_child = value;
Update();
}
}
public NumericNoteBox()
{
InitializeComponent();
_sharp = null;
}
public void Update()
{
txbNote.Text = _child.MainNote.ToString();
if (_sharp != null)
{
_sharp.Visibility = Visibility.Hidden;
}
topStack.Children.Clear();
botStack.Children.Clear();
if (_child != null)
{
if (_child.Pitch > 0)
{
for (int i = 0; i < _child.Pitch; i++)
{
topStack.Children.Add(new Ellipse());
(topStack.Children[topStack.Children.Count - 1] as Ellipse).Width = 3;
(topStack.Children[topStack.Children.Count - 1] as Ellipse).Height = 3;
(topStack.Children[topStack.Children.Count - 1] as Ellipse).Fill = Brushes.Black;
if (_child.Accidental != Note.Accidentals.Flat)
{
(topStack.Children[topStack.Children.Count - 1] as Ellipse).Margin = new Thickness(0, 1, 0, 0);
}
else
{
(topStack.Children[topStack.Children.Count - 1] as Ellipse).Margin = new Thickness(8, 1, 0, 0);
}
}
}
else if (_child.Pitch < 0)
{
for (int i = 0; i < Math.Abs(_child.Pitch); i++)
{
botStack.Children.Add(new Ellipse());
(botStack.Children[botStack.Children.Count - 1] as Ellipse).Width = 3;
(botStack.Children[botStack.Children.Count - 1] as Ellipse).Height = 3;
(botStack.Children[botStack.Children.Count - 1] as Ellipse).Fill = Brushes.Black;
if (_child.Accidental != Note.Accidentals.Flat)
{
(botStack.Children[botStack.Children.Count - 1] as Ellipse).Margin = new Thickness(0, 1, 0, 0);
}
else
{
(botStack.Children[botStack.Children.Count - 1] as Ellipse).Margin = new Thickness(8, 1, 0, 0);
}
}
}
if (_child.Accidental == Note.Accidentals.Flat)
{
txbNote.Text = "b" + _child.MainNote.ToString();
}
else if (_child.Accidental == Note.Accidentals.Sharp)
{
if (_sharp == null)
{
_sharp = new Line();
_sharp.X1 = 10;
_sharp.Y1 = 2.5;
_sharp.X2 = -2.5;
_sharp.Y2 = 12.5;
_sharp.StrokeThickness = 1;
_sharp.Stroke = Brushes.Black;
canvas.Children.Add(_sharp);
}
_sharp.Visibility = Visibility.Visible;
}
}
}
}
注意:
- 我不受这些代码的约束,所以如果你们中的任何人用完全不同的方法更有效地处理了这样的事情,那么它总是受欢迎的.
- 对于一些语法错误,如果有的话,抱歉,因为英语不是我的第一语言.
- 我觉得我没有把我的问题解释清楚,因为我也很困惑,所以,请澄清任何你需要的东西.
谢谢
其他细节:
- 点理论上没有一定的限制,但在实践中,它通常最多只有 3 个(顶部 3 个或底部 3 个 - 不能两者兼而有之)
- 在我上面的代码中,note 用户控件被分成 3 个网格行,顶部一个用于堆叠顶部点,中间一个用于可视化音符(数字),bot 一个用于堆叠 bot 点.
- 请澄清任何事情,我会补充更多.
- The dot theoretically doesn't have certain limitation, but in practice, it's usually only 3 maximum (either 3 on top or 3 on bottom - not both)
- In my code above, the note user control is divided into 3 grid row, the top one is for stacking the top dot, the mid one is for visualizing the note (numbers) and the bot one is for stacking the bot dot.
- Please clarify anything, and I'll add more.
推荐答案
您应该使用 ItemsControl 来执行此操作,该控件为数字注释使用适当的 ItemTemplate
.
You should do this with an ItemsControl which use an appropriate ItemTemplate
for the numeric notes.
首先,创建一个 ViewModel,它定义了一个数字笔记"项目的集合:
First, create a ViewModel that defines a collection of "numeric note" items:
public class NumericNoteItem
{
public int Number { get; set; }
public int Pitch { get; set; }
}
public class ViewModel
{
public ViewModel()
{
NumericNotes = new ObservableCollection<NumericNoteItem>();
}
public ObservableCollection<NumericNoteItem> NumericNotes { get; private set; }
}
在 MainWindow 的构造函数中,您可以将 DataContext 设置为此 ViewModel 的实例,如下所示:
In the constructor of your MainWindow, you may set the DataContext to an instance of this ViewModel like this:
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
vm.NumericNotes.Add(new NumericNoteItem { Number = 1, Pitch = 0 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 2, Pitch = 1 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 3, Pitch = -1 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 4, Pitch = 0 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 5, Pitch = 2 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 6, Pitch = -2 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 4, Pitch = 0 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 5, Pitch = 3 });
vm.NumericNotes.Add(new NumericNoteItem { Number = 6, Pitch = -3 });
DataContext = vm;
}
为了可视化音高,我建议使用 Path
对象和由许多 EllipseGeometries 组成的 Geometry.为了实现这一点,您将实现一个绑定转换器,将音高数转换为几何体,如下所示.它使用 Convert 方法的 parameter
参数为正或负螺距值创建几何体.
For visualizing the pitch I'd recommend to use a Path
object with a Geometry that consists of a number of EllipseGeometries. To achieve this you would implement a binding converter that converts the pitch number into a Geometry, like shown below. It uses the parameter
argument of the Convert method to create a Geometry for either positive or negative pitch values.
public class NotePitchConverter : IValueConverter
{
private const double radius = 1.5;
private const double distance = 2 * radius + 1;
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
var pitch = (int)value;
var geometry = new GeometryGroup();
if (parameter as string == "Bottom")
{
pitch = -pitch;
}
for (int i = 0; i < pitch; i++)
{
geometry.Children.Add(new EllipseGeometry(
new Point(radius, radius + i * distance), radius, radius));
}
return geometry;
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
现在,在 XAML 中,您将创建此转换器的实例,例如MainWindow Resources
:
Now, in XAML you would create an instance of this converter in e.g. the MainWindow Resources
:
<Window.Resources>
<local:NotePitchConverter x:Key="NotePitchConverter"/>
</Window.Resources>
并编写如下所示的 ItemsControl.请注意,DataTemplate 对显示顶部和底部间距的 Path 元素使用固定高度.如果您需要显示三个以上的点,则需要增加它们的高度.
and write the ItemsControl like shown below. Note that the DataTemplate uses a fixed height for the Path elements that show the top and bottom pitch. If you need to show more than three dots, you'd need to increase their height.
<ItemsControl ItemsSource="{Binding NumericNotes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="12"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="12"/>
</Grid.RowDefinitions>
<Path Grid.Row="0" Fill="Black" HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Data="{Binding Pitch,
Converter={StaticResource NotePitchConverter}}"/>
<TextBlock Grid.Row="1" Text="{Binding Number}"/>
<Path Grid.Row="2" Fill="Black" HorizontalAlignment="Center"
VerticalAlignment="Top"
Data="{Binding Pitch,
Converter={StaticResource NotePitchConverter},
ConverterParameter=Bottom}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
这篇关于如何垂直“居中"对齐多行文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!