Команда вызова WPF DataGrid, привязка - PullRequest
0 голосов
/ 06 августа 2020

Я только вчера начал изучать WPF, и моя цель - создать окно с простой сеткой с информацией о бронировании отелей. Пока есть только номер комнаты, количество гостей, даты и столбцы «Действие». В столбце «Действия» есть кнопка «Сохранить». Он должен иметь возможность сохранять обновления или создавать новое бронирование при нажатии в новой строке. Проблема в том, что когда я нажимаю кнопку «Сохранить», метод SaveBooking не вызывается. Я также не уверен, как правильно привязать к объекту CurrentBooking. Поскольку я новичок в WPF, я попытался понять это из нескольких руководств. Вот что я создал.

XAML:

<Window x:Class="HotelApp.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:HotelApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="800" Width="1000">
    <Grid>
        <TabControl>
            <TabItem Header="Bookings">
                <DataGrid AutoGenerateColumns = "False" ItemsSource="{Binding Bookings}">

                    <DataGrid.Columns>
                        <DataGridTextColumn Header = "Room" Binding = "{Binding Room, Mode=TwoWay}" />
                        <DataGridTextColumn Header = "Floor" Binding = "{Binding NumOfGuests, Mode=TwoWay}" />
                        <DataGridTextColumn Header = "From" Binding = "{Binding From, Mode=TwoWay}"/>
                        <DataGridTextColumn Header = "To" Binding = "{Binding To, Mode=TwoWay}"/>
                        <DataGridTemplateColumn Header = "Actions">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <Button Content="Save" Command="{Binding DataContext.SaveBookingCommand }" />
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </TabItem>
            <TabItem Header="Guests" />
        </TabControl>
        
    </Grid>
</Window>

МОДЕЛЬ:

public class BookingModel : ObservableObject
{
    private int _room;
    public int Room
    {
        get => _room;
        set
        {
            if (value != _room)
            {
                _room = value;
                OnPropertyChanged("Room");
            }
        }
    }

    private int _numOfGuests;
    public int NumOfGuests
    {
        get => _numOfGuests;
        set
        {

            _numOfGuests = value;
            OnPropertyChanged("NumOfGuests");
        }
    }

    private DateTime _from;
    public DateTime From
    {
        get => _from;
        set
        {

            _from = value;
            OnPropertyChanged("From");
        }
    }

    private DateTime _to;

    public DateTime To
    {
        get => _to;
        set
        {

            _to = value;
            OnPropertyChanged("To");
        }
    }
}

VIEWMODEL:

public class MainWindowVM : ObservableObject
{
    private readonly IBookingService _bookingService;

    private ICommand _saveBookingCommand;
    public ICommand SaveBookingCommand
    {
        get
        {
            if (_saveBookingCommand == null)
            {
                _saveBookingCommand = new RelayCommand(
                    param => SaveBooking(),
                    param => (CurrentBooking != null)
                );
            }
            return _saveBookingCommand;
        }
    }

    private ObservableCollection<BookingModel> _Bookings { get; set; }

    private BookingModel _currentBookng;
    public BookingModel CurrentBooking
    {
        get { return _currentBookng; }
        set
        {
            if (value != _currentBookng)
            {
                _currentBookng = value;
                OnPropertyChanged("CurrentBooking");
            }
        }
    }

    public ObservableCollection<BookingModel> Bookings
    {
        get { return _Bookings; }
        set { _Bookings = value; }
    }

    public MainWindowVM(IBookingService bookingService)
    {
        _bookingService = bookingService;
        BrowseBookings();
    }

    public void BrowseBookings()
    {
        var bookings = _bookingService.Browse().Select(x => new BookingModel { Room = x.Room.RoomId, NumOfGuests = x.NumOfGuests, From = x.From, To = x.To });
        Bookings = new ObservableCollection<BookingModel>(bookings);

    }

    private void SaveBooking()
    {
        // send CurrentBooking to service
    }
}

RelayCommand:

public class RelayCommand : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors
    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion // Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameters)
    {
        return _canExecute == null ? true : _canExecute(parameters);
    }

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

    public void Execute(object parameters)
    {
        _execute(parameters);
    }

    #endregion // ICommand Members
}

Ответы [ 2 ]

0 голосов
/ 06 августа 2020

Вы упустили много кода, поэтому я не знаю, какой пакет nuget вы использовали для своего ObservableObject. Anywho, я подделал ObservableObject и получил привязку. Основная проблема заключалась в том, что вы пытались привязать SaveBookingCommand на уровне BookingModel, когда в вашем коде она написана на уровне MainWindowVM. ваша привязка должна быть Command={Binding Parent.SaveBookingCommand}.

Вот несколько указателей на внесенные мной изменения:

MainWindow.xaml.cs :

    <DataTemplate>
        <Button Content="Save" Command="{Binding Parent.SaveBookingCommand}" />
    </DataTemplate>

BookingModel.cs :

public class BookingModel : ObservableObject
{
    public MainWindowVM Parent { get; private set; }

    public BookingModel()
    {
        this.Parent = null;
    }

    public BookingModel(MainWindowVM parent)
    {
        this.Parent = parent;
    }

    // ... you know the rest

MainWindowVM.cs :

public MainWindowVM : ObservableObject
{
    public void BrowseBookings()
    {
        // NOTICE that I added 'this' as the parameter argument to connect MainWindowVM to the BookingModel.
        var bookings = _bookingService.Browse().Select(x => new BookingModel(this) { Room = x.Room, NumOfGuests = x.NumOfGuests, From = x.From, To = x.To });
        Bookings = new ObservableCollection<BookingModel>(bookings);
        CurrentBooking = Bookings.First();
    }

    // ... you know the rest
0 голосов
/ 06 августа 2020

Ваша команда находится в контексте данных всего массива данных MainWindowVM.

Контекст данных вашей кнопки - это контекст строки - BookingModel.

Вам нужен некоторый родственный источник для этой привязки.

В принципе это выглядит так:

{Binding DataContext.ParentVMProperty,
RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}

И вашим типом в этом случае будет DataGrid.

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

или

У вас может быть параметр команды

 CommandParameter="{Binding .}"

Команда Relay обычно бывает двух видов один из них RelayCommand

Возможно, я пропустил это, но я не вижу этого в вашей реализации. Я предлагаю вам go взять исходный код для MVVM Light и вставить его в свое решение для более полной реализации. Или просто добавьте пакет nuget, если вы не используете ядро. net. Вам нужна версия relaycommand в пространстве имен commandwpf.

...