如何垂直"中心"对齐多行文本

时间:2014-06-17 18:18:00

标签: c# wpf user-controls

对不起,如果标题有点误导,我很难授权。它也不完全是“中心”。 (见下文进一步说明)

嗨,我打算制作一个编写乐谱的项目。但是我找到了平衡“字体”的障碍。 (见下图为视觉)

enter image description here

  1. 我需要1 2 3 3 1 2 3 4 4处于直线“行”
  2. 我使用WrapPanel作为笔记的容器(如屏幕中所示)。
  3. 主要问题在于音高点,它位于音符上方或下方 EITHER 。 Pitch绑定到音符,因此我需要在1个用户控件中处理它。但是,如果是这样,那么我就无法控制音符“font”的位置在一行中。

    以下是我的笔记用户控制代码:

    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;
                }
            }
        }
    }
    

    注意:

    1. 我不受那个代码的束缚,所以如果你们中的任何人用完全不同的方法更有效地处理这样的事情,那么它总是受欢迎的。
    2. 很抱歉出现语法错误,如果有的话,因为英语不是我的第一语言。
    3. 我觉得我没有真正清楚地解释我的问题,因为我也很困惑,所以,请澄清你需要的任何东西。
    4. 由于

      其他细节:

      1. 理论上点没有一定的限制,但在实践中,它通常只有3个最大值(顶部3个或底部3个 - 不是两个)
      2. 在我的上面的代码中,笔记用户控件分为3个网格行,顶部用于堆叠顶部点,中间一个用于可视化笔记(数字),而机器人用于堆叠机器人点
      3. 请澄清任何内容,我会添加更多内容。

1 个答案:

答案 0 :(得分:2)

您应该使用ItemsControl执行此操作,该控件使用适当的ItemTemplate作为数字注释。

首先,创建一个定义“数字注释”项集合的ViewModel:

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的实例,如下所示:

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;
}

为了可视化音高,我建议使用带有Geometry的Path对象,该Geometry由许多EllipseGeometries组成。要实现这一点,您需要实现一个绑定转换器,将音高编号转换为几何,如下所示。它使用Convert方法的parameter参数为正或负音高值创建几何。

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中,您将在例如XAML中创建此转换器的实例。 MainWindow Resources

<Window.Resources>
    <local:NotePitchConverter x:Key="NotePitchConverter"/>
</Window.Resources>

并编写ItemsControl,如下所示。请注意,DataTemplate对Path元素使用固定高度,显示顶部和底部音高。如果你需要显示三个以上的点,你需要增加它们的高度。

<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>