Привязать уже сфомированный List к ListView WPF

Подскажите пожалуйста, как привязать коллекцию, которая у меня сформирована в другом классе, чтобы при нажатии на кнопку заполнился ListView, не используя в основном коде программы свойство ItemSource, то есть чтобы через привязку это делалось автоматически, или какие вообще пути решения есть. Везде по-разному пишут. Хотелось точно понять как это делать. Спасибо!

P.S. сейчас ListView заполняется, но используется при этом в коде свойство ItemSource

XAML

<Grid>
    <StackPanel Orientation="Vertical" Margin="5">
        <Button x:Name="Go" Content="Поехали" Height="25" Margin="5" Click="Go_Click"/>
        <ListView  Margin="5" Height="100" ItemsSource="{Binding}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Stolb1" Width="100"/>
                </GridView>
            </ListView.View>
        </ListView>
    </StackPanel>
</Grid>

Код в основной части:

public partial class MainWindow : Window
{
    public ObservableCollection<string> mylist { get; set; }
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Go_Click(object sender, RoutedEventArgs e)
    {
        ListUp listUp = new ListUp();
        ListView.ItemsSource = listUp.Numbers;
    }
    private class ListUp : INotifyPropertyChanged
    {
        public List<string> Numbers = new List<string> {"1", "2","3" };

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

Ответы (1 шт):

Автор решения: aepot

У контролов есть свойство DataContext все биндинги работают относительно того объекта, на который этот DataContext указывает.

С точки зрения подхода MVVM ваша задача решается так:

1) Создайте класс MainViewModel, он будет содержать свойства для биндингов главного окна.

public class MainViewModel : INotifyPropertyChanged
{
    private ObservableCollection<string> _myList;

    public ObservableCollection<string> MyList
    {
        get => _myList;
        set
        {
            _myList = value;
            OnPropertyChanged();
        }
    }

    public MainViewModel()
    {

    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

2) Подключите его к окну, самый простой (но не лучший) способ - сделать это в конструкторе класса окна:

private MainViewModel VM;

public MainWindow()
{
    InitializeComponent();
    VM = new MainViewModel();
    this.DataContext = VM;
}

К свойствам VM из класса окна можно обращаться например вот так:

string str = VM.MyList[0];

3) Задайте привязку в xaml

<ListView Margin="5" Height="100" ItemsSource="{Binding MyList}">

Ну и (чтобы меня тут не побили другие читатели) стоит учитывать, что MVVM - это когда в классе окна или контрола работа с данными не производится. У меня даже в больших проектах в классе MainWindow пусто. Даже ни одного обработчика событий. Взаимодействие интерфейса и кода происходит только через биндинги, и никак иначе. Вам просто будет без надобности обращаться к контролам явно, так как они сами будут себя обновлять через биндинги.


Что же делать, когда нажата кнопка в MVVM, если не использовать обработчики события? Использовать команды!

Вот класс, позволяющий удобно использовать команды, просто положите его в проект (очень давно взял его здесь).

public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
    public void Execute(object parameter) => _execute(parameter);
}

А использовать его можно, добавив в MainViewModel вот такой код

private ICommand _myCommand;

public ICommand MyCommand => _myCommand ?? (_myCommand = new RelayCommand(parameter =>
{
    MyList = new ObservableCollection<string>() { "Элемент 1", "Элемент 2", "Элемент 3" };
    MyList.Add("Элемент 4");
}));

В разметке тоже ничего сложного

<Button Content="Поехали" Height="25" Margin="5" Command="{Binding MyCommand}"/>
→ Ссылка