我为这个问题道歉了。通常情况下我会尽量避免要求其他人为我做调试,但是我已经进入了第二天这个问题,而且我承认我需要一些帮助。
我有一个Silverlight AudioVisualizer用户控件,它有一个FrameSource依赖属性,定义如下:
public short[] FrameSource
{
get { return (short[])GetValue(FrameSourceProperty); }
set { SetValue(FrameSourceProperty, value); }
}
public static readonly DependencyProperty FrameSourceProperty =
DependencyProperty.Register("FrameSource", typeof(short[]), typeof(AudioVisualizer), new PropertyMetadata(OnFrameSourceChanged));
private static void OnFrameSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
try
{
short[] source = e.NewValue as short[];
AudioVisualizer instance = d as AudioVisualizer;
if (source != null && source.Length > 0 && instance != null)
{
instance.RenderVisualization(source);
}
}
catch (System.Exception ex)
{
ClientLogger.LogDebugMessage(ex.ToString());
}
}
我希望这个属性的源是一个由实现INotifyPropertyChanged的AudioStatistics类公开的short []数组,如下所示:
private short[] latestFrame;
public Array LatestFrame
{
get
{
return latestFrame;
}
set
{
if (++FrameCount % ReportingInterval == 0 && value != null)
{
if (latestFrame == null || Buffer.ByteLength(latestFrame) != Buffer.ByteLength(latestFrame))
{
latestFrame = new short[Buffer.ByteLength(value) / sizeof(short)];
}
Buffer.BlockCopy(value, 0, latestFrame, 0, Buffer.ByteLength(value));
OnPropertyChanged("LatestFrame");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
});
}
(四个未成年人,但我相信上述代码无关紧要:(1)我只向音频可视化器发送一小部分音频帧,以保持CPU负载最小化;(2)音频处理总是在后台线程上进行,所以我需要将PropertyChanged事件编组到UI线程上;(3)最小化GC问题,我在分配期间将数据复制到缓冲区,而不是仅仅分配新的引用;(4)我将short [] latestFrame字段暴露为Array属性而不是short [],因为有时数据以byte []数组形式表示short,有时是short []数组,但是这没关系,因为#3。我也认为这段代码工作正常,因为其他绑定工作正常 - 见下文。仅包括完整性。)
我已经设置了这样的绑定:
<ItemsControl x:Name="audioVisualizersListBox" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<sdk:Label Content="{Binding Name}" />
<av:AudioVisualizer FrameSource="{Binding LatestFrame, Converter={StaticResource debugConverter}}" Margin="4" Height="200" Width="300" VolumeFactor="3" />
<sdk:Label Content="{Binding LatestFrame, Converter={StaticResource debugShortsToStringConverter}}" />
<sdk:Label Content="{Binding FrameCount, StringFormat='Frames: {0}'}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ItemsControl绑定到MediaController类的ObservableCollection属性:
AudioStatistics speakerStatistics = new AudioStatistics("Sent to Speaker");
AudioStatistics microphoneStatistics = new AudioStatistics("Received from Microphone");
AudioStatistics cancelledStatistics = new AudioStatistics("Echo Cancelled");
AudioStats = new ObservableCollection<AudioStatistics>();
AudioStats.Add(speakerStatistics);
AudioStats.Add(microphoneStatistics);
AudioStats.Add(cancelledStatistics);
DataContext的实际分配发生在我的主窗体的代码隐藏中,看起来就像你期望的那样:
audioVisualizersListBox.ItemsSource = mediaController.AudioStats;
除AudioVisualizer.FrameSource绑定之外的所有绑定都正常工作。例如,每次源对象的LatestFrame属性更改时,带有debugShortsToStringConverter转换器的标签都会更新,每次FrameCount更改时,绑定到FrameCount属性的标签也会更改。
问题是在我的AudioVisualizer控件中,OnFrameSourceChanged回调只被调用两次。在这两次调用之后,它再也不会被调用。它有点像“FrameSource”依赖属性没有正确注册INotifyPropertyChanged通知。我想我可以在OnFrameSourceChanged方法中以某种方式自己手动注册它们,但是我需要反思和什么不是,这似乎不对。
最后一个数据点:在某些时候在之后我得到一个提交给FrameSource DependencyProperty的正确帧,我在VS调试窗口中得到了这个错误:
System.Windows.Data错误:BindingExpression路径错误:在'Alanta.Client.Controls.AudioVisualizer.AudioVisualizer''Alanta.Client.Controls.AudioVisualizer.AudioVisualizer'(HashCode = 63535124)上找不到'LatestFrame'属性。 BindingExpression:Path ='LatestFrame'DataItem ='Alanta.Client.Controls.AudioVisualizer.AudioVisualizer'(HashCode = 63535124); target元素是'Alanta.Client.Controls.AudioVisualizer.AudioVisualizer'(Name =''); target属性是'FrameSource'(类型'System.Int16 []')..
这是一个非常奇怪的错误消息,因为它似乎表明它正在尝试绑定到AudioVisualizer.LatestFrame属性,这当然不存在。 AudioStatistics类具有LatestFrame属性,但Audiovisualizer类仅具有FrameSource属性。非常令人困惑,但我已经仔细检查了我的绑定(和三重检查),它们似乎被正确定义。
对我在这里做错了什么的想法?这是我第一次尝试使用自己的依赖属性实现数据绑定,所以我很可能错过了一些明显的东西。
(11/9/10 - 编辑添加了我第一次遗漏的更多代码,因为它已经太长了。)
答案 0 :(得分:0)
好的,我明白了。我犯了两个错误,一个是愚蠢的,一个是太聪明一半。
(1)在AudioVisualizer代码隐藏(我最初从其他地方借来的)的内部某处,它将DataContext重新分配给它自己。这是上面BindingExpression错误的来源。
(2)显然,DependencyProperty子系统试图了解它转发到其依赖项属性的INotifyPropertyChanged事件。除此之外,它似乎检查对象引用是否实际指向新对象。如果不是,则无需调用PropertyChangedCallback委托。因此,当我通过将新数据复制到其中来更新数组时,而不是更改引用,它不会像DependencyProperty子系统那样改变任何内容,因此它不会转发事件。