Reputation: 642
I have an app for displaying songs on an overhead projector. The way it is currently written is with a stack panel of text boxes with each line of each verse displaying on each. There is also an option to display chords above each line.
The problem I am facing is that people often want to make the font smaller or larger for both the song text and the chord text. Every time this changes, the songs have to be edited to line up the chords again. The alignment of text would also effect the position of the chords.
I am looking for a more robust way to line up a chord with a section of text and keep it in the same location relative to it's word. I have thought of using a canvas, but wouldn't know how to line the text up with the correct word.
I am kind of at a loss as to what would work best for this and would appreciate any advice.
Upvotes: 4
Views: 70
Reputation: 11201
I figured it'd be a good idea to keep the Chord
and the Txt Char
it references together. A StackPanel
holding 2 TextBlocks
would do. From there it's just thinking outwards.
Each line could be an ItemsControl
stacking these StackPanels
horizontally.
A regular ListBox
can then hold these lines.
ViewModels.cs
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
namespace Karaoke
{
public class ChordData
{
public ChordData(string chord, int position)
{
Chord = chord;
Position = position;
}
#region Chord Property
private String _chord;
public String Chord
{
get { return _chord; }
set { if (value != _chord) _chord = value; }
}
#endregion Chord Property
#region Position Property
private int _position;
public int Position
{
get { return _position; }
set { if (value != _position) _position = value; }
}
#endregion Position Property
}
public class KaraokeChar
{
public KaraokeChar(char txt)
{
Txt = txt;
Chord = "";
}
#region Txt Property
private Char _txt;
public Char Txt
{
get { return _txt; }
set { if (value != _txt) _txt = value; }
}
#endregion Txt Property
#region Chord Property
private String _chord;
public String Chord
{
get { return _chord; }
set { if (value != _chord) _chord = value; }
}
#endregion Chord Property
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] String propName = null)
{
// C#6.O
// PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
public class ItemViewModel : ViewModelBase
{
public ItemViewModel(string txt, List<ChordData> chordList)
{
foreach (char c in txt)
{
Line.Add(new KaraokeChar(c));
}
foreach (ChordData chord in chordList)
{
Line[chord.Position].Chord = chord.Chord;
}
}
#region Line Property
private ObservableCollection<KaraokeChar> _line = new ObservableCollection<KaraokeChar>();
public ObservableCollection<KaraokeChar> Line
{
get { return _line; }
set
{
if (value != _line)
{
_line = value;
OnPropertyChanged();
}
}
}
#endregion Line Property
}
public class MainViewModel : ViewModelBase
{
#region Song Property
private ObservableCollection<ItemViewModel> _song = new ObservableCollection<ItemViewModel>();
public ObservableCollection<ItemViewModel> Song
{
get { return _song; }
set
{
if (value != _song)
{
_song = value;
OnPropertyChanged();
}
}
}
#endregion Song Property
#region TextFont Property
private int _textFont;
public int TextFont
{
get { return _textFont; }
set
{
if (value != _textFont)
{
_textFont = value;
OnPropertyChanged();
}
}
}
#endregion TextFont Property
#region ChordFont Property
private int _chordFont;
public int ChordFont
{
get { return _chordFont; }
set
{
if (value != _chordFont)
{
_chordFont = value;
OnPropertyChanged();
}
}
}
#endregion ChordFont Property
}
}
MainWindow.xaml.cs
using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Karaoke
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel.TextFont = 25;
ViewModel.ChordFont = 20;
SongData();
}
private void SongData()
{
ObservableCollection<ItemViewModel> Song = new ObservableCollection<ItemViewModel>();
Song.Add(new ItemViewModel("The dog and the cat",
new List<ChordData>() { new ChordData("D", 0) }));
Song.Add(new ItemViewModel("They take up the middle",
new List<ChordData>()));
Song.Add(new ItemViewModel("Where the honey bee hums",
new List<ChordData>() { new ChordData("A", 8) }));
Song.Add(new ItemViewModel("And Coyote howls",
new List<ChordData>() { new ChordData("A", 2), new ChordData("D", 9) }));
ViewModel.Song = Song;
}
// C#6.O
// public MainViewModel ViewModel => (MainViewModel)DataContext;
public MainViewModel ViewModel
{
get { return (MainViewModel)DataContext; }
}
}
}
MainWindow.xaml
<Window x:Class="Karaoke.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Karaoke"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<StackPanel Background="Black">
<Label Foreground="Yellow" FontSize="{Binding TextFont}" HorizontalAlignment="Center">All God's Critters</Label>
<ListBox ItemsSource="{Binding Song}"
Background="Transparent"
BorderBrush="Transparent"
Margin="40,0">
<ListBox.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Line}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Chord}" FontSize="{Binding DataContext.ChordFont, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Foreground="Purple"/>
<TextBlock Text="{Binding Txt}" FontSize="{Binding DataContext.TextFont, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Foreground="White"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
Changing fontsizes as simple as changing a ViewModel prop:
Upvotes: 1
Reputation: 1165
The way I would do this, would be to have each word that has a chorus, to be in a two line textblock (use "Run" and "Linebreak" inside TextBlock content). This would mean that you will have to split your lines into lines with chorus and lines without.
If a line has chorus, than you have to have it split into textblocks without chorus, followed by textblocks with chorus, and then textblocks without chorus. (you could use a stackpanel with Horizontanl orientation to have it all on the same line)
It not a simple matter, but it could make it so that you have what you want.
Upvotes: 0