WPF - Почему работает без OnPropertyChanged?
От: RainBoy  
Дата: 09.01.17 19:39
Оценка:
Разбираюсь с WPF

XAML:
<ListBox Name="lstLog"
                     runManager:ListBoxExtenders.AutoScrollToCurrentItem="True"
                     IsSynchronizedWithCurrentItem="True"
                     ItemsSource="{Binding MemLogEvents}" />


ViewModel очень простой

        private ObservableCollection<string> _memLogEvents;

        public ObservableCollection<string> MemLogEvents
        {
            get { return _memLogEvents; }
            set
            {
                _memLogEvents = value;
                OnPropertyChanged(nameof(MemLogEvents));
            } 
        }
        
        // .... Some code here
        
        private void GetMemLogEvent(object sender, MemLogEventArgs eventArgs)
        {
            MemLogEvents.Add(eventArgs.Log);
            OnPropertyChanged(nameof(MemLogEvents));
        }


GetMemLogEvent вызывается на каждуб новую строку в логе

Все работает, но есть два вопроса:

— Из сеттера метод OnPropertyChanged никогда не вызывается, навсолько я понимаю. Присваивание там идет только раз. Из GetMemLogEvent этот же метод вызывается постоянно. Если убрать OnPropertyChanged отовсюду логи так и продолжают появляться в ListBox. Откуда ListBox берет уведомление о изиенившемся содержимом?

— Как настроить, чтобы ListBox автоматом скроллился вниз до упора? Я попробовал использовать Extender для ListBox, что нашел, но безрезультатно.

Спасибо!
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Отредактировано 10.01.2017 5:56 AndrewVK . Предыдущая версия .
wpf
Re: WPF - Почему работает без OnPropertyChanged?
От: vmpire Россия  
Дата: 09.01.17 19:50
Оценка:
Здравствуйте, RainBoy, Вы писали:
RB> — Из сеттера метод OnPropertyChanged никогда не вызывается, навсолько я понимаю. Присваивание там идет только раз. Из GetMemLogEvent этот же метод вызывается постоянно. Если убрать OnPropertyChanged отовсюду логи так и продолжают появляться в ListBox. Откуда ListBox берет уведомление о изиенившемся содержимом?
Потому, что OnPropertyChanged не имеет отношения к добавлению/удалению элементов. Этим занимается INotifyCollectionChanged, который реализуеся в ObservableCollection

RB> — Как настроить, чтобы ListBox автоматом скроллился вниз до упора? Я попробовал использовать Extender для ListBox, что нашел, но безрезультатно.

Я бы попробовал вызывать ScrollIntoView, когда нужно.
Re[2]: WPF - Почему работает без OnPropertyChanged?
От: RainBoy  
Дата: 09.01.17 19:55
Оценка:
Здравствуйте, vmpire, Вы писали:


V>Потому, что OnPropertyChanged не имеет отношения к добавлению/удалению элементов. Этим занимается INotifyCollectionChanged, который реализуеся в ObservableCollection


Я менял ObservableCollection на обычный List, который не реализует INotifyCollectionChanged — тот же результат, все работает.

V>Я бы попробовал вызывать ScrollIntoView, когда нужно.


Спасибо, посмотрю
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[3]: WPF - Почему работает без OnPropertyChanged?
От: vmpire Россия  
Дата: 09.01.17 21:01
Оценка:
Здравствуйте, RainBoy, Вы писали:

RB> Я менял ObservableCollection на обычный List, который не реализует INotifyCollectionChanged — тот же результат, все работает.

И при этом не вызывали OnPropertyChanged(nameof(MemLogEvents)) и всё работало? Тогда не знаю
Re[4]: WPF - Почему работает без OnPropertyChanged?
От: RainBoy  
Дата: 09.01.17 21:17
Оценка:
V>И при этом не вызывали OnPropertyChanged(nameof(MemLogEvents)) и всё работало? Тогда не знаю

Нет, не вызывал.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re: WPF - Почему работает без OnPropertyChanged?
От: Loooser Россия  
Дата: 10.01.17 11:01
Оценка:
Здравствуйте, RainBoy, Вы писали:

RB> — Из сеттера метод OnPropertyChanged никогда не вызывается, навсолько я понимаю. Присваивание там идет только раз. Из GetMemLogEvent этот же метод вызывается постоянно. Если убрать OnPropertyChanged отовсюду логи так и продолжают появляться в ListBox. Откуда ListBox берет уведомление о изиенившемся содержимом?


ItemsSource="{Binding MemLogEvents}"


ListBox привязан к полю MemLogEvents вашего ViewModel, а не к экземпляру объекта ViewModel. Поэтому при добавлении элемента в него (MemLogEvents), там "внутри ObservableCollection" срабатывает свой OnPropertyChanged, которое и приводит к обновлению ListBox. Пойдет такая версия?
Re[2]: WPF - Почему работает без OnPropertyChanged?
От: RainBoy  
Дата: 10.01.17 15:44
Оценка:
L> ListBox привязан к полю MemLogEvents вашего ViewModel, а не к экземпляру объекта ViewModel. Поэтому при добавлении элемента в него (MemLogEvents), там "внутри ObservableCollection" срабатывает свой OnPropertyChanged, которое и приводит к обновлению ListBox. Пойдет такая версия?

Не совсем понимаю, где это "там внутри".
Да и если поменять Observable на обычный List<> все продолжает работать.
Я только разбираюсь, для меня это непонятно.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re: WPF - Почему работает без OnPropertyChanged?
От: MxMsk Португалия  
Дата: 11.01.17 14:24
Оценка:
Здравствуйте, RainBoy, Вы писали:

RB> — Из сеттера метод OnPropertyChanged никогда не вызывается, навсолько я понимаю. Присваивание там идет только раз. Из GetMemLogEvent этот же метод вызывается постоянно. Если убрать OnPropertyChanged отовсюду логи так и продолжают появляться в ListBox. Откуда ListBox берет уведомление о изиенившемся содержимом?

Про ObservableCollection уже объяснили, а про List<T> верится с трудом. Тут лучше полные исходники с примером кинуть.

RB> — Как настроить, чтобы ListBox автоматом скроллился вниз до упора? Я попробовал использовать Extender для ListBox, что нашел, но безрезультатно.

Binding в WPF не работает со списками напрямую, а использует абстракцию ICollectionView. Под капотом каждая прибиндинная коллекция получает свой collection view. Его можно запросить, вызвав GetDefaultView. При помощи collection view можно управлять UI списка из view model без привязки к конкретному типу контрола. Для этого надо устанавливать CurrentItem, используя любой из методов MoveCurrentTo*. В твоем случае вроде подходит MoveCurrentToLast. Правда, я советую, если нужно явное управление текущим элементом, стоит сразу делать свойство с типом ICollectionView, а в модели обернуть List через ListCollectionView.
Re[2]: WPF - Почему работает без OnPropertyChanged?
От: RainBoy  
Дата: 11.01.17 18:24
Оценка:
Здравствуйте, MxMsk, Вы писали:

MM>Здравствуйте, RainBoy, Вы писали:


RB>> постоянно. Если убрать OnPropertyChanged отовсюду логи так и продолжают появляться в ListBox. Откуда ListBox берет уведомление о изиенившемся содержимом?

MM>Про ObservableCollection уже объяснили, а про List<T> верится с трудом. Тут лучше полные исходники с примером кинуть.



<Window x:Class="ICC.RunManager.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
        xmlns:local="clr-namespace:ICC.RunManager.Classes"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:runManager="clr-namespace:ICC.RunManager.Classes"
        xmlns:viewModels="clr-namespace:ICC.RunManager.ViewModels"
        Title="ICC Run Manager"
        Width="880"
        Height="600"
        mc:Ignorable="d">
    <Window.DataContext>
        <!--  datacontext assigned to the new instance of MainWindowViewModel  -->
        <viewModels:MainWindowViewModel x:Name="mainWindowViewModel" />
    </Window.DataContext>
    <DockPanel LastChildFill="True">
        <Menu Name="mnuMain" DockPanel.Dock="Top">
            <MenuItem Name="menuItemFile" Header="File" />
            <MenuItem Name="menuItemView" Header="View" />
            <MenuItem Name="menuItemEdit" Header="Edit" />
            <MenuItem Name="menuItemHelp" Header="Help" />
        </Menu>
        <ToolBar Height="16" DockPanel.Dock="Top" />
        <StackPanel Height="16" DockPanel.Dock="Bottom" />
        <Button Width="100" DockPanel.Dock="Left" />
        <GroupBox Height="150"
                  DockPanel.Dock="Bottom"
                  Header="Log">
            <ListBox Name="lstLog"
                     runManager:ListBoxExtenders.AutoScrollToCurrentItem="True"
                     IsSynchronizedWithCurrentItem="True"
                     ItemsSource="{Binding ElementName=mainWindowViewModel,
                                           Path=MemLogEvents}" />
        </GroupBox>

        <!--  Put ESRI map here  -->

    </DockPanel>
</Window>



using Esri.ArcGISRuntime;
using System;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using ICC.Common;
using ICC.RunManager.ViewModels;

namespace ICC.RunManager
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {                                             
            InitializeComponent();
            // datacontext assigned in XAML
            //MainWindowViewModel viewModel = new MainWindowViewModel();            
            //this.DataContext = viewModel;
        }
    }
}


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ICC.Common;

namespace ICC.RunManager.ViewModels
{
    class MainWindowViewModel: BaseViewModel
    {

        // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
        private readonly Logger _logger;
        private List<string> _memLogEvents;

        public List<string> MemLogEvents
        {
            get { return _memLogEvents; }
            set
            {
                _memLogEvents = value;
                //OnPropertyChanged(nameof(MemLogEvents));
            } 
        }

        /// <summary>
        /// Default constructor
        /// </summary>
        public MainWindowViewModel()
        {
            _logger = Logger.GetInstance();

            // subscribe viewModel to the log events
            _logger.MemLog += this.GetMemLogEvent;

            // init log collection
            _memLogEvents = new List<string>();

            // grab all previous logs
            List<MemLogEventArgs> memLogList = _logger.GetMemLogList(true);
            foreach (var memLogItem in memLogList)
            {
                this.GetMemLogEvent(this, memLogItem);
            }
        }

        private void GetMemLogEvent(object sender, MemLogEventArgs eventArgs)
        {
            MemLogEvents.Add(eventArgs.Log);
            OnPropertyChanged(nameof(MemLogEvents));
        }

    }
}



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ICC.Common;

namespace ICC.RunManager.Classes
{
    public class BaseClass
    {
        private readonly Logger _logger = Logger.GetInstance();
        private readonly AppHelper _appHelper = new AppHelper();

        

        public void Run()
        {
            _logger.WriteLine("");
            _logger.WriteLine("****************************************************************************************");
            _logger.WriteLine("*                                                                                      *");
            _logger.WriteLine("*                                 Application started                                  *");
            _logger.WriteLine("*                                   ICC Run Manager                                    *");
            _logger.WriteLine("*                                                                                      *");
            _logger.WriteLine("****************************************************************************************");
            _logger.WriteLine("");
            _logger.WriteLine("Application Name: " + _appHelper.GetApplicationName() + ".exe ver. " + _appHelper.GetApplicationVersionInfo().ProductVersion);
            _logger.WriteLine("Run Path: " + AppSettings.ApplicationPath);
            _logger.WriteLine("");
        }

        public void Close()
        {
            _logger.WriteLine("");
            _logger.WriteLine("****************************************************************************************");
            _logger.WriteLine("*                                                                                      *");
            _logger.WriteLine("*                                Application terminated                                *");
            _logger.WriteLine("*                                                                                      *");
            _logger.WriteLine("****************************************************************************************");
            _logger.WriteLine("");
            _logger.CloseLogFile();
        }


    }
}



Комментируя все OnPropertyChanged все равно работает

... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.