Это мой первый набег в WPF, и я пытался внимательно следить за MVVM, чтобы все было правильно. Контекст здесь такой, что у меня есть представление, которое должно отображать различные наборы сообщений, все из которых хранятся в ObservableCollection<T>
.
Это код в моем представлении (это UserControl, который размещается в другое представление, поэтому я могу перемещаться между различными представлениями во время выполнения)
Пространство имен "i" равно xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
<ListBox ItemsSource="{Binding Messages}">
<i:Interaction.Behaviors>
<behaviours:ScrollOnNewItemBehaviour />
</i:Interaction.Behaviors>
<ListBox.ItemTemplate>
<DataTemplate DataType="entities:DisplayedUserMessage">
<!-- Removed for brevity -->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Это код поведения (собранный из других вопросов SO) Я просматривал, пытаясь разобраться с этой концепцией):
public sealed class ScrollOnNewItemBehaviour : Behavior<ListBox>
{
protected override void OnAttached()
{
AssociatedObject.Loaded += OnLoaded;
AssociatedObject.Unloaded += OnUnLoaded;
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= OnLoaded;
AssociatedObject.Unloaded -= OnUnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
var incc = AssociatedObject.ItemsSource as INotifyCollectionChanged;
if (incc == null) return;
incc.CollectionChanged += OnCollectionChanged;
}
private void OnUnLoaded(object sender, RoutedEventArgs e)
{
var incc = AssociatedObject.ItemsSource as INotifyCollectionChanged;
if (incc == null) return;
incc.CollectionChanged -= OnCollectionChanged;
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
var border = (Border)VisualTreeHelper.GetChild(AssociatedObject, 0);
var scrollViewer = (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
// Only scroll when we're scrolled to the bottom of the listbox
if (scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight)
{
scrollViewer.ScrollToBottom();
}
}
}
}
Итак, вот где возникает моя конкретная проблема c - привязка работает просто отлично. Когда я изменяю _selectedChannel
(я удалил нерелевантный код из представленной ниже модели представления), представление обновляется новыми сообщениями (_messages
- это словарь, содержащий различные экземпляры ObservableCollection), а когда я добавляю в них новые сообщения, пользовательский интерфейс также обновления.
Проблема в том, что ни в коем случае не срабатывает поведение, которое я зарегистрировал в ListBox, что является проблемой, так как я полагаюсь на это, чтобы держать прокрутку. Мое лучшее предположение состояло в том, что, возможно, он не поддерживает связанный ItemSource, а тот факт, что ItemSource изначально имеет значение null (словарь будет заполняться асинхронно, поэтому по умолчанию он не установлен) означает, что он не был зарегистрирован должным образом / должен быть перерегистрировать каждый раз, когда привязка обновляется?
public MessagesViewModel : ViewModelBase
{
private ObservableCollection<DisplayedUserMessage> _displayedMessages;
private Channel _selectedChannel;
public IList<DisplayedUserMessage> Messages
{
get
{
return _displayedMessages;
}
set
{
if (_displayedMessages == value)
{
return;
}
_displayedMessages = value;
NotifyPropertyChanged();
}
}
public Channel SelectedChannel
{
get
{
return _selectedChannel;
}
set
{
if (_selectedChannel == value)
{
return;
}
_selectedChannel = value;
Messages = _messages[_selectedChannel.Id];
NotifyPropertyChanged();
}
}
}
Поведение работает, если оно выполняется (я проверил, что оно не работает с точками останова), поэтому, если у кого-то есть идея относительно того, что я должен изменить чтобы заставить это работать с изменением ItemSources, пожалуйста, дайте мне знать!