简体   繁体   中英

DataGrid data binding and MVVM in WPF

A WPF application with a Window containing a DataGrid , trying to implement the MVVM architecture. There is a single Model class:

public class Book
{
    public int id {get; set;}
    public string title {get; set;}
    public string isbn {get; set;}
}

This is the ViewModel:

class BookViewModel
{
    public ObservableCollection<Book> Books;

    public BookViewModel()
    {
        Books = new ObservableCollection<Book>();

        // TODO: execute LoadData comand
    }
}

A partial snippet of XAML code for markup for the View, which consists of a single Window:

<Window.CommandBindings>
    <CommandBinding Command="self:CustomCommands.LoadData" CanExecute="LoadDataCommand_CanExecute" Executed="LoadDataCommand_Executed"/>
</Window.CommandBindings>

<DataGrid Name="BooksDataGrid>
    <DataGrid.Columns>
        <DataGridTextColumn Header="Title" Width="200" Binding="{Binding title}"/>
        <DataGridTextColumn Header="isbn" Width="200" Binding="{Binding isbn}"/>
    </DataGrid.Columns>
</DataGrid>

The code behind for the View above:

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

        // TODO: execute LoadData command
    }

    private void LoadDataCommand_CanExecute(object sender, CanExecuteRoutedEventArgs args)
    {
        args.CanExecute = true;
    }

    private void LoadDataCommand_Executed(object sender, ExecutedRoutedEventArgs args)
    {
       // TODO: ask the model for the data
    }
}

public static class CustomCommands
{
    public static readonly RoutedUICommand LoadData = new RoutedUICommand
        (
            "LoadData",
            "LoadData",
            typeof(CustomCommands),
            new InputGestureCollection()
            {
                // allow Ctrl+L to perform this command
                new KeyGesture(Key.L, ModifierKeys.Control)
            }
        );
}

The codebehind for App.xaml:

public partial class App : Application
{
    [STAThread()]
    public static void Main()
    {
        App app = new App();
        app.InitializeComponent();
        app.Run();
    }

    // bind application and show main window on startup
    // data context is default source of bindings
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        View.MainWindow mainWin = new View.MainWindow();
        ViewModel.BookViewModel bookViewModel = new ViewModel.BookViewModel();
        mainWin.DataContext = bookViewModel;
        mainWin.Show();
    }
}

I am transitioning from WinForms to WPF, and find certain aspects of WPF confusing. What is trying to be achieved, and the problems faced, are as follows:

  • the LoadData command should clean the GridView , then fill it again from a database. The database connection has already been implemented. So far, I can't see how to get the databinding working.
  • the LoadData command should be executed from both a menu and button (already implemented).
  • the LoadData command should be executed when the application starts. This is so the DataGrid would be filled when it starts.

step one: you created a window, its view model and connected them via DataContext. Already done

View.MainWindow mainWin = new View.MainWindow();
ViewModel.BookViewModel bookViewModel = new ViewModel.BookViewModel();
mainWin.DataContext = bookViewModel;

step two: fix (add) binding in xaml:

<DataGrid Name="BooksDataGrid" ItemsSource="{Binding Books}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Title" Width="200" Binding="{Binding title}"/>
        <DataGridTextColumn Header="isbn" Width="200" Binding="{Binding isbn}"/>
    </DataGrid.Columns>
</DataGrid>

step three: fix view model for Binding to work - Binding works with properties:

class BookViewModel
{
    public ObservableCollection<Book> Books { get; private set; }

    public BookViewModel()
    {
        Books = new ObservableCollection<Book>();
    }
}

step four: implement load data method in the view model amd call it from command, and after mainWin.Show(); to load initial data

You shouldn't have much code in the code-behind of the window, other than initializing the component and setting the data context.

public MainWindow()
{
   IntializeComponent();
   DataContext = new BookViewModel();
}

and your loadData command would be implemented in the BookViewModel

private bool _canLoadDataExecute;
private ICommand _loadDataCommand;
public Icommand LoadDataCommand => _loadDataCommand ?? _loadDataCommand = new RelayCommand((obj) => LoadDataCommand = LoadData(), canLoadDataExecute);

public void LoadData()
{
}

For the code above to work you would also need to implement RelayCommand.cs, code for this can be found here: Full implementation of Relay Command - can it be applied to all cases? . Additionally, there are many other useful answers and resources online with RelayCommand implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM