c# - 如何垂直 "center"对齐多行文字

标签 c# wpf user-controls

抱歉,如果标题有点误导,我很难给它命名。它也不完全是“中心”。 (更多解释见下文)

嗨,我打算做一个项目来编写编号的乐谱。但是我发现了调平“字体”的障碍。 (视觉效果见下图)

enter image description here

  1. 我需要 1 2 3 3 1 2 3 4 4 成一条直线
  2. 我使用 WrapPanel 作为笔记的容器(如在屏幕中)。

主要问题出在音高点上,它位于音符上方或下方EITHER。 Pitch 绑定(bind)到音符,所以我需要在 1 User Control 中处理它。但是,如果是这样,那么我无法控制注释“字体”的位置在一行中。

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

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. 我觉得我没有把我的问题解释清楚,因为我也很困惑,所以,请澄清任何你需要的。

谢谢

其他详细信息:

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

最佳答案

您应该使用 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;
}

为了可视化音高,我建议使用一个 Path 对象和一个由许多 EllipseGeometries 组成的 Geometry。为此,您将实现一个绑定(bind)转换器,将音高数转换为几何图形,如下所示。它使用 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 中,您将创建此转换器的一个实例,例如主窗口资源:

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

关于c# - 如何垂直 "center"对齐多行文字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24270720/

相关文章:

c# - 单击按钮后为我的表选择一个类? (HTML 表格)

c# - WPF UI 在事件触发时挂起

sql-server - 什么 .sdf 文件。它与 .mdf 文件有何不同。它可以与 linq to Sql 一起使用吗?

wpf - 将用户控件添加到 wpf 窗口

c# - OLEDB 命令未生成有效查询

C#如何读取节点xml中的单个项目

wpf - 跨 Windows 的 HotTest?

c# - UserControl引发异常“跨线程操作无效”

asp.net - 是否可以在前端将对象传递给用户控件?

c# - 类型初始化异常 : The type initializer for 'vService.CheckService' threw an exception