我有一个显示音频波形的画布,它是使用很多行创建的。每一行都标有它的时间码,所以我可以识别它在音频中的位置。
我想根据存储在可观察集合中的数据在画布上放置一个矩形 基本上,Timespan起点和终点,所以我可以显示一个音频块。
我遇到的问题是要显示矩形,我必须知道Canvas左边的值和宽度 我可以通过扫描画布中的子项来获取这些,直到找到正确的行并获得其X1值,但我不知道如何在绑定中执行此操作。
我想绑定可观察的集合ItemsControl,以便我可以在画布上显示矩形。
是否可以绑定到基于可观察集合中的数据进行计算的函数或其他东西?
XAML:
<ScrollViewer Grid.Row="2" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden">
<Canvas Name="waveform" >
<ItemsControl Name="RectArea"> <!-- Where I hope to have the rectangles appear on top of the waveform canvas -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Stroke="Yellow" Fill="Yellow" Opacity="0.2" Height="200" Width="{Binding Width}" Canvas.Left="{Binding Left}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</ScrollViewer>
创建波形:
Dim seconds As Integer = 0
lines = New Dictionary(Of String, Line)
Using Reader As New AudioFileReader(openfile.FileName)
Dim samples = Reader.Length / (Reader.WaveFormat.Channels * Reader.WaveFormat.BitsPerSample / 8)
Dim f = 0.0F
Dim max = 0.0F
Dim batch As Integer = Math.Max(10, samples / samplecount)
Dim mid = 100
Dim yScale = 100
Dim buffer(batch) As Single
Dim read As Integer
Dim xPos = 0
read = Reader.Read(buffer, 0, batch)
While read = batch
For n As Integer = 0 To read
max = Math.Max(Math.Abs(buffer(n)), max)
Next
Dim line As New Line
line.X1 = xPos
line.X2 = xPos
line.Y1 = mid + (max * yScale)
line.Y2 = mid - (max * yScale)
line.Tag = Reader.CurrentTime
line.StrokeThickness = 1
line.Stroke = Brushes.DarkGray
AddHandler line.MouseDown, AddressOf Line_MouseDown
waveform.Children.Add(line)
' lines is a dictionary that holds all of the line information. nothing is bound to it, it just allows me to search against time code so I can highlight the line as the audio is playing'
If Not lines.ContainsKey(Reader.CurrentTime.Hours.ToString().PadLeft(2, "0") & Reader.CurrentTime.Minutes.ToString().PadLeft(2, "0") & Reader.CurrentTime.Seconds.ToString().PadLeft(2, "0") & Reader.CurrentTime.Milliseconds.ToString().PadLeft(3, "0").Substring(0, 1)) Then
lines.Add(Reader.CurrentTime.Hours.ToString().PadLeft(2, "0") & Reader.CurrentTime.Minutes.ToString().PadLeft(2, "0") & Reader.CurrentTime.Seconds.ToString().PadLeft(2, "0") & Reader.CurrentTime.Milliseconds.ToString().PadLeft(3, "0").Substring(0, 1), line)
End If
' Draw a tall black line and show timecode every 10 seconds to make it easier to see where you are on the code '
If Reader.CurrentTime.TotalSeconds > (seconds + 10) Then
seconds = Reader.CurrentTime.TotalSeconds
line = New Line
line.X1 = xPos
line.X2 = xPos
line.Y1 = mid + yScale
line.Y2 = mid - yScale
line.StrokeThickness = 1
line.Stroke = Brushes.Black
waveform.Children.Add(line)
Dim textblock As New TextBlock
textblock.Text = Reader.CurrentTime.Hours.ToString().PadLeft(2, "0") & ":" & Reader.CurrentTime.Minutes.ToString().PadLeft(2, "0") & ":" & Reader.CurrentTime.Seconds.ToString().PadLeft(2, "0") & "," & Reader.CurrentTime.Milliseconds.ToString().PadLeft(3, "0")
textblock.Foreground = Brushes.Black
Canvas.SetLeft(textblock, xPos)
Canvas.SetTop(textblock, yScale)
waveform.Children.Add(textblock)
End If
max = 0
xPos += 1
read = Reader.Read(buffer, 0, batch)
End While
waveform.Width = xPos
End Using
的ObservableCollection:
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Public Class ocAudioSelection
Implements INotifyPropertyChanged
Private _Changed As Boolean
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Protected Overridable Sub OnPropertyChanged(ByVal Propertyname As String)
On Error GoTo sError
If Not Propertyname.Contains("Changed") Then
Changed = True
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Propertyname))
Exit Sub
sError:
MsgBox(ErrorToString)
End Sub
Public Property Changed() As Boolean
Get
Return _Changed
End Get
Set(ByVal value As Boolean)
If _Changed <> value Then
_Changed = value
OnPropertyChanged("Changed")
End If
End Set
End Property
Private _startTime As String
Private _endTime As String
Public Sub New()
End Sub
Public Sub New(startTime As String)
_startTime = startTime
End Sub
Public Sub New(startTime As String, endTime As String)
_startTime = startTime
_endTime = endTime
End Sub
Public Property StartTime As String
Get
Return _startTime
End Get
Set(value As String)
If value <> _startTime Then
_startTime = value
OnPropertyChanged("StartTime")
End If
End Set
End Property
Public Property EndTime As String
Get
Return _endTime
End Get
Set(value As String)
If value <> _endTime Then
_endTime = value 'TimeSpan.Parse()
OnPropertyChanged("EndTime")
End If
End Set
End Property
End Class