Событие - Когда добавляется что либо в StackPanel

164
17 декабря 2018, 14:30

Я прикрутил к StackPanel скролл, но он не так пока работает как мне хотелось бы. Он стоит на месте, когда содержимого больше чем высота StackPanel. Мне же хочется всегда видеть последний добавленный элемент в StackPanel.

Как я понял, мне нужно опускать скролл программно при добавлении элемента в StackPanel, как отловить это событие ? на MSDN про StackPanel что то не нашёл.

Короче говоря: Как называется событие у StackPanel, когда я добавляю в него элемент ?

Answer 1

Универсальный совет, изучающим WPF - почитайте и разберитесь как использовать паттерн MVVM (необязательно по этой ссылке). Большая вероятность, что после освоения MVVM у вас разрешиться множество вопросов, касаемо WPF.

Что касается вашего вопроса: скорее всего, вы идете неверным путем, но, так как вводных данных мало, будем работать с тем, что есть.

StackPanel не имеет события изменения коллекции детей, поэтому мы не можем отследить когда был добавлен или удален элемент. Опять-таки, скорее всего, вам нужно использовать другой контрол, но в качестве альтернативы можно приспособить для этих целей событие LayoutUpdated. Это событие происходит, когда обновляется layout элемента.

Подписываемся на событие и пишем в обработчике логику прокрутки ScrollViewer к последнему элементу:

Xaml:

<ScrollViewer x:Name="sv" Grid.Row="1">
     <StackPanel x:Name="stackPanel" />
</ScrollViewer>
<Button Click="AddToStackPanel">Add item to StackPanel</Button>

Code-behind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        stackPanel.LayoutUpdated += OnLayoutUpdated;
    }
    private bool _needToScroll = true;
    private void OnLayoutUpdated(object sender, EventArgs e)
    {
        if (_needToScroll)
        {
            sv.ScrollToEnd();
            _needToScroll = false;
        }
    }
    private void AddToStackPanel(object sender, RoutedEventArgs e)
    {
        var tb = new TextBlock {Text = DateTime.Now.ToString("hh:mm:ss.fff tt")};
        stackPanel.Children.Add(tb);
        _needToScroll = true;
    }
}

Поле _needToScroll отвечает за то, нужно ли прокручивать ScrollViewer к последнему элементу. Это нужно чтобы пользователь мог вручную использовать прокрутку (не забываем, что при прокрутке layout контрола также обновляется и если флаг не использовать, то контрол будет прокручен всегда к последнему элементу). Соответственно этот флаг нужно устанавливать при добавлении/удалении элементов.

Интуитивно кажется, что вам больше подошел бы ListBox или ItemsControl на которые уже можно навешать поведение прокрутки к последнему элементу. Или же унаследоваться от стандартных и сделать контрол со своим поведением.

Xaml:

<ListBox Grid.Row="1" ItemsSource="{Binding Items}" >
    <i:Interaction.Behaviors>
        <local:ScrollToLastItemBehavior/>
    </i:Interaction.Behaviors>
</ListBox>
<Button Grid.Row="2" Command="{Binding AddCommand}">Add item to ListBox</Button>

ScrollToLastItemBehavior:

public class ScrollToLastItemBehavior : Behavior<ListBox>
{
    protected override void OnAttached()
    {
        var listBox = AssociatedObject;
        ((INotifyCollectionChanged)listBox.Items).CollectionChanged += OnListBox_CollectionChanged;
    }
    protected override void OnDetaching()
    {
        var listBox = AssociatedObject;
        ((INotifyCollectionChanged)listBox.Items).CollectionChanged -= OnListBox_CollectionChanged;
    }
    private void OnListBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        var listBox = AssociatedObject;
        if (listBox.Items.Count > 0)
        {
            var lastItem = listBox.Items[listBox.Items.Count - 1];
            listBox.ScrollIntoView(lastItem);
        }
    }
} 

Во VieModel ничего особенного нет:

public class MainVm : INotifyPropertyChanged
{
    public MainVm()
    {
        AddCommand = new RelayCommand(AddItem);
    }
    public ObservableCollection<string> Items { get; set; } = new ObservableCollection<string>();
    public ICommand AddCommand { get; set; }
    private void AddItem()
    {
        Items.Add("Current time: " + DateTime.Now.ToString("hh:mm:ss.fff tt"));
    }
    #region INPC
    public event PropertyChangedEventHandler PropertyChanged;
    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion INPC
}

READ ALSO
Записать в log.txt о Mixed Content на странице?

Записать в log.txt о Mixed Content на странице?

Я использую ssl но иногда на страницах сайта встречаются внешнее изображения, добавлены пользователями, с протоколом http, как записать в logtxt...

228
Curl разный ответ сервера: 200 и 403

Curl разный ответ сервера: 200 и 403

Пытаюсь разобраться с Curl/ Запускаю следкод:

203
PHP Laravel не видит app.js и app.css в app.blade.php

PHP Laravel не видит app.js и app.css в app.blade.php

Не работает логин и регистрация

324
Сравнение, анализ прайсов поставщиков

Сравнение, анализ прайсов поставщиков

Я дилетант в этом деле прошу подсказать схему реализации

241