Хобрук: Ваш путь к мастерству в программировании

Почему привязка моей ObservableCollection‹string› к Listbox не работает?

Когда я обновляю ObservableCollection<string> и вызываю RaisePropertyChanged(..), его содержимое почему-то не отображается в моем Listbox в WPF. Я понятия не имею, что я делаю неправильно.

Критическая часть — это то, где я обновляю свойство FileNames:

public class HistoricalDataViewRawDataViewModel : ViewModelBase
{
    private string _currentDirectory;
    private ObservableCollection<string> _fileNames;
    private List<string> _rawData;

    public ICommand ChangeDirectoryCommand { get; private set; }
    public string CurrentDirectory
    {
        get { return _currentDirectory; }
        set
        {
            if (_currentDirectory != value)
            {
                _currentDirectory = value;
                RaisePropertyChanged("CurrentDirectory");
            }
        }
    }
    public ObservableCollection<string> FileNames
    {
        get { return _fileNames; }
        set
        {
            if (_fileNames != value)
            {
                _fileNames = value;
                RaisePropertyChanged("FileNames");
            }
        }
    }

    public List<string> RawData
    {
        get { return _rawData; }
        set
        {
            if (_rawData != value)
            {
                _rawData = value;
                RaisePropertyChanged("RawData");
            }
        }
    }

    public HistoricalDataViewRawDataViewModel()
    {
        ChangeDirectoryCommand = new RelayCommand(ChangeDirectory);
        var fileDirectory = Properties.Settings.Default.HistoricalData_RawDataSourceDirectory;

        //set current directory
        CurrentDirectory = fileDirectory;

        //load all fileNames
        LoadAvailableFileNames(fileDirectory);
    }

    private void ChangeDirectory()
    {
        using (var folderDialog = new FolderBrowserDialog())
        {
            folderDialog.SelectedPath = CurrentDirectory;
            folderDialog.ShowDialog();

            //set current directory
            CurrentDirectory = folderDialog.SelectedPath;

            //save current directory to settings
            Properties.Settings.Default.HistoricalData_RawDataSourceDirectory = CurrentDirectory;
            Properties.Settings.Default.Save();

            //load files in chosen directory
            LoadAvailableFileNames(CurrentDirectory);
        }
    }

    private void LoadAvailableFileNames(string directory)
    {
        FileNames = new ObservableCollection<string>(FileIO.GetFileNamesInDirectory(directory, false, true));
    }
    private async void LoadRawData(string fileName)
    {

    }}

Это xaml-код. Это должно работать как есть, потому что я хочу отобразить ObservableCollection<string>. Я добавил пару элементов в список из кода программной части, и он отображался просто отлично:

DataContext="{Binding HistoricalDataViewRawDataViewModel, Source={StaticResource Locator}}">

<Grid>

    <Grid.RowDefinitions>
        <RowDefinition Height="50" />
        <RowDefinition />
    </Grid.RowDefinitions>

    <StackPanel Grid.Row="0" Orientation="Horizontal">
        <ToggleButton Margin="10"  HorizontalAlignment="Left" VerticalAlignment="Center" Content="Choose Directory" FontSize="18" Foreground="White" Command="{Binding ChangeDirectoryCommand}"/>
        <TextBlock 
            Margin="10"
            FontSize="18"
            Foreground="White"
            HorizontalAlignment="Stretch"
            VerticalAlignment="Stretch"
            TextAlignment="Center"
            Text="{Binding CurrentDirectory}"/>
    </StackPanel>

    <dxdo:DockLayoutManager Grid.Row="1">

        <dxdo:LayoutGroup Orientation="Vertical">

            <dxdo:LayoutPanel ItemHeight="7*">
                <dxdo:LayoutControlItem>
                    <ListBox Name="MyListBox" ItemsSource="{Binding FileNames}"/>
                </dxdo:LayoutControlItem>
            </dxdo:LayoutPanel>

            <dxdo:LayoutPanel Caption="Activity Log" ItemHeight="200" >
                <dxdo:LayoutControlItem>
                    <ListBox/>
                </dxdo:LayoutControlItem>
            </dxdo:LayoutPanel>

        </dxdo:LayoutGroup>

    </dxdo:DockLayoutManager>

</Grid>

  • @GrantWinney, с чего бы это? Я даже не связываю RawData. Но ради удовольствия поменял, без изменений. 26.12.2014
  • Список‹строка›? FileNames — это строка ObservableCollection, а не строка List. Я предполагаю, что ваш DataContext установлен неправильно, что тормозит вашу привязку в ItemsSource={Binding FileNames}. Можете ли вы показать, где вы установили свой контекст данных? 26.12.2014
  • @user1182735 user1182735, datacontext установлен правильно, я вижу ссылку на FileNames в моей модели просмотра. Но я также добавил строку кода xaml 26.12.2014
  • @GrantWinney, честный комментарий, меня беспокоит FileNames. Я отредактировал свой вопрос, чтобы сделать его более понятным. 26.12.2014
  • Могу я предложить вам создать минимальный пример? Судя по всему, ни RawData, ни то, что записи являются именами файлов, к сути проблемы отношения не имеют. И зависимость от некоторого пространства имен dxdo: не позволяет другим пытаться воспроизвести вашу проблему. 26.12.2014
  • @dkozl, что непонятно? Я рад отредактировать свой вопрос, но я никогда не говорил, что он касается файлов, это ObservableCollection<string>, и я хочу, чтобы его содержимое отображалось в списке. Что-то, что занимает 10 секунд в Winforms, вызывает сильную головную боль при изучении WPF. Спасибо 26.12.2014
  • @dkozl, да, я использую точно такой же подход в других представлениях/моделях представления. По сути, ViewModelLocator обернут вокруг ServiceLocator. Кстати, привязка CurrentDirectory к моему текстовому блоку работает просто отлично, что означает, что просматривается правильный экземпляр модели представления. 26.12.2014
  • Вместо того, чтобы использовать другой метод для установки свойства, вы можете инициализировать имена файлов непосредственно из c'tor. Просто чтобы посмотреть, работает ли привязка вообще. В c'tor: FileNames = new ObservableCollection‹string›{a, b, c}; Если это работает, попробуйте обновить существующий экземпляр ObservableCollection‹string› с помощью очистки и добавления/вставки вместо установки нового. 26.12.2014
  • @user1182735 user1182735, я уже пробовал, не повезло. Либо привязка не работает, либо контент приходится специально форматировать через DataTemplate (правда, надеюсь, что не на простой string коллекции). 26.12.2014
  • Можете ли вы установить контекст данных непосредственно в ListBox? ‹ListBox Name=MyListBox DataContext={Binding HistoricalDataViewRawDataViewModel, Source={Локатор статических ресурсов}} ItemsSource={Имена файлов привязки}/›. Если это работает, то это указывает на то, что контекст inhitance нарушен. 26.12.2014
  • @ user1182735, теперь это сработало, и я совершенно не понимаю, почему. Как вы можете видеть в моем коде xaml, контекст данных для текстового блока и переключателя работает отлично. Не могли бы вы пояснить, что вы имеете в виду под нарушением контекста наследования? 26.12.2014
  • Я предполагаю, что эти элементы управления dxdo являются нестандартными элементами управления? Я предполагаю, что эти элементы управления нарушают механизм наследования. Механизм наследования применяется ко всем объектам FrameworkElement. Это означает, что значения свойств наследуются по логическому дереву. Это позволяет вам установить свойство datacontext во внешнем Grid, но использовать его, например, в TextBlock. Значение, которое вы устанавливаете в сетке, наследуется StackPanel, а затем снова TextBlock. Это работает только для объектов, наследуемых от FrameworkElement. 26.12.2014
  • @ user1182735, боже мой, это сводит меня с ума. Как можно надежно изучить и закодировать WPF, если для этого требуются серьезные исправления ошибок и сломанные пользовательские элементы управления (это элементы управления DevEx, а не первая ошибка, с которой мне пришлось столкнуться за эту неделю изучения WPF). Он действительно хочет, чтобы я вернулся к Winforms, где все было более простым, но надежным. Но мне интересно, список здесь является стандартным элементом управления Windows. Честно говоря, я очень расстроен, и это только моя первая неделя в xaml и WPF... 26.12.2014
  • Посмотрите на это с положительной стороны. Вы разрабатываете wpf только одну неделю и уже знаете о контексте наследования. Мне потребовалось 2 года, чтобы добраться туда ;) Попробуйте выяснить, являются ли эти dxdo-классы производными от FrameworkElement. 26.12.2014
  • хорошо, постараюсь покопаться в этом немного больше. Спасибо, что указали мне в этом направлении. 26.12.2014

Ответы:


1

Согласно данному запросу в службу поддержки в DevExpress, простое удаление LayoutControlItem работает.

Я создал пример проекта, используя ваш XAML и некоторые фиктивные данные, и он отлично работает:

<Window x:Class="WpfApplication12.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking"
        Title="MainWindow" Height="350" Width="525">
    <dxdo:DockLayoutManager Grid.Row="1">

        <dxdo:LayoutGroup Orientation="Vertical">

            <dxdo:LayoutPanel ItemHeight="7*">
                <!-- notice the removal of LayoutControlItem here -->
                <ListBox ItemsSource="{Binding FileNames}"/>
            </dxdo:LayoutPanel>

            <dxdo:LayoutPanel Caption="Activity Log" ItemHeight="200" >
                <ListBox/>
            </dxdo:LayoutPanel>
        </dxdo:LayoutGroup>
    </dxdo:DockLayoutManager>
</Window>

Код позади:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        //Dummy data
        DataContext = new
        {
            FileNames = Enumerable.Range(0, 5).Select(x => "File" + x.ToString())
        };
    }
}

Результат:

введите здесь описание изображения

26.12.2014
  • В качестве примечания: вам нужно Snoop при работе с WPF (по крайней мере, до выпуска VS 2015 с аналогичные возможности). 26.12.2014
  • В качестве примечания 2: ваши ObservableCollection должны быть свойствами только для чтения ( {get {....}}) и создаваться только один раз в конструкторе виртуальных машин. Таким образом, вы уменьшите немного кода и гарантируете, что вы всегда привязываетесь к правильной коллекции. 26.12.2014
  • здорово, что получилось. На самом деле я изменил коллекцию строк на List<string>, потому что я никогда не меняю отдельные элементы внутри коллекции, каждый раз, когда привязывается совершенно новая коллекция [я последовал вашему совету и скорее очистил существующий список и добавил через AddRange. 27.12.2014

  • 2

    Я предполагаю, что эти элементы управления dxdo являются нестандартными элементами управления? Я предполагаю, что эти элементы управления нарушают механизм наследования. Механизм наследования применяется ко всем объектам FrameworkElement. Это означает, что значения свойств наследуются по логическому дереву. Это позволяет вам установить свойство datacontext во внешнем Grid, но использовать его, например, в TextBlock. Значение, которое вы устанавливаете в сетке, наследуется StackPanel, а затем снова TextBlock. Это работает только для объектов, наследуемых от FrameworkElement.

    Можете ли вы установить контекст данных непосредственно в ListBox? . Если это работает, то это указывает на то, что контекст inhitance нарушен.

    26.12.2014
  • это работает, но как можно нарушить контекст данных, если несколько строк выше того же контекста данных для textblock и button работают нормально? 26.12.2014
  • Наследование значения DataContext работает по логическому дереву. Textblock является дочерним элементом StackPanel, который является дочерним элементом Grid, который является дочерним элементом чего бы то ни было. Это все FrameworkElements, что означает, что они поддерживают механизм наследования. Ваш ListBox является дочерним элементом LayoutControlItem, который является дочерним элементом LayoutPanel, который является дочерним элементом LayoutGroup, который является дочерним элементом DockLayoutManager. Я не знаю, поддерживают ли эти DevEx-элементы наследование свойств. Похоже, что нет. 26.12.2014
  • Ах, теперь это имеет гораздо больше смысла. Спасибо, я проверю, может быть, мне удастся объявить datacontext выше в цепочке, если я смогу определить тот, который не наследуется правильно. 26.12.2014
  • Вы также можете использовать другие обходные пути: codeproject.com/Articles/27432 /. Это продвинутый wpf. удачи! 26.12.2014
  • Спасибо за вашу помощь. Оказалось, что DX LayoutControlItem вызвал проблемы. 27.12.2014
  • Новые материалы

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..

    Обзор 20 основных и современных методов работы с массивами в JavaScript
    Вы знаете их всех? В этом коротком посте я покажу сводку методов, доступных в JavaScript для работы с массивами. Я надеюсь, что вы найдете это полезным! В конце поста вы найдете ссылку на..

    Да, но я чувствую необходимость указать, что это или не единственные два.
    Да, но я чувствую необходимость указать, что это или не единственные два. Обучение с подкреплением (в качестве примера) также является важным.