MVVM 绑定无法正常工作

本文关键字:工作 常工作 绑定 MVVM | 更新日期: 2023-09-27 18:19:25

更新 设法修复了选定的索引问题。我也忘记设置SelectedItem,自然而然地导致了一些问题。

所以今天早上9点,我们接到了24小时的任务,我碰到了一堵砖墙。我们应该创建一个程序,允许主管添加和删除员工,并添加工作会话、总工时和总收入。但是我在按照 MVVM 模式成功实现这一点时遇到了一些问题。由于某种原因,我的绑定根本不起作用,我能看到的唯一解决方案是有人查看我的项目并帮助我对其进行故障排除。

这是我的代码 - 我很抱歉不得不发布整个事情,但鉴于我不知道问题出在哪里,我没有看到任何其他选项。

员工模型

[Serializable]
public class WorkSessions : ObservableCollection<WorkSessionModel>
{
    public WorkSessions()
    {
    }
}
[Serializable]
public class WorkSessionModel : INotifyPropertyChanged
{
    private DateTime _dateTime;
    private string _id;
    private double _hours;
    public WorkSessionModel()
    {
    }
    public DateTime DateTime
    {
        get { return _dateTime; }
        set
        {
            _dateTime = value;
            NotifyPropertyChanged("DateTime");
        }
    }
    public string ID
    {
        get { return _id; }
        set
        {
            _id = value;
            NotifyPropertyChanged("ID");
        }
    }
    public double Hours
    {
        get { return _hours; }
        set
        {
            _hours = value;
            NotifyPropertyChanged("Hours");
            NotifyPropertyChanged("TotalHours");
        }
    }

    [field: NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

工作会话模型

    [Serializable]
public class WorkSessions : ObservableCollection<WorkSessionModel>
{
    public WorkSessions()
    {
    }
}
[Serializable]
public class WorkSessionModel : INotifyPropertyChanged
{
    private DateTime _dateTime;
    private string _id;
    private double _hours;
    public WorkSessionModel()
    {
    }
    public DateTime DateTime
    {
        get { return _dateTime; }
        set
        {
            _dateTime = value;
            NotifyPropertyChanged("DateTime");
        }
    }
    public string ID
    {
        get { return _id; }
        set
        {
            _id = value;
            NotifyPropertyChanged("ID");
        }
    }
    public double Hours
    {
        get { return _hours; }
        set
        {
            _hours = value;
            NotifyPropertyChanged("Hours");
            NotifyPropertyChanged("TotalHours");
        }
    }

    [field: NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

员工视图模型

public class EmployeeViewModel : ViewModelBase
{
    private Employees _employeesModel = new Employees();
    public Employees EmployeesView = new Employees();
    public ObservableCollection<WorkSessionModel> WorkSessions { get; set; }
    private string _id = "0";
    private string _name = "noname";
    private double _wage = 0;
    private int _totalhours = 0;
    public string ID
    {
        get { return _id; }
        set { _id = value; RaisePropertyChanged("ID"); }
    }
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged("Name");
        }
    }
    public double Wage
    {
        get { return _wage; }
        set
        {
            _wage = value;
            RaisePropertyChanged("Wage");
        }
    }
    public int TotalHours
    {
        get { return _totalhours; }
        set
        {
            _totalhours = value;
            RaisePropertyChanged("TotalHours");
        }
    }
    private EmployeeModel _selectedEmployee = new EmployeeModel();
    public EmployeeModel SelectedEmployee
    {
        get { return _selectedEmployee; }
        set
        {
            _selectedEmployee = value;
            RaisePropertyChanged("SelectedEmployee");
        }
    }
    private int _selectedEmployeeIndex;
    public int SelectedEmployeeIndex
    {
        get { return _selectedEmployeeIndex; }
        set
        {
            _selectedEmployeeIndex = value;
            RaisePropertyChanged("SelectedEmployeeIndex");
        }
    }
    #region RelayCommands
    // Employee Relay Commands
    public RelayCommand EmployeeAddNewCommand { set; get; }
    public RelayCommand EmployeeDeleteCommand { set; get; }
    public RelayCommand EmployeeNextCommand { set; get; }
    public RelayCommand EmployeePrevCommand { set; get; }
    public RelayCommand EmployeeTotalHoursCommand { get; set; }
    #endregion
    public EmployeeViewModel()
    {
        InitCommands();
    }
    private void InitCommands()
    {           
        EmployeeAddNewCommand = new RelayCommand(EmployeeAddNewExecute, EmployeeAddNewCanExecute);
        EmployeeDeleteCommand = new RelayCommand(EmployeeDeleteNewExecute, EmployeeDeleteCanExecute);
        EmployeeNextCommand = new RelayCommand(EmployeeNextExecute, EmployeeNextCanExecute);
        EmployeePrevCommand = new RelayCommand(EmployeePrevExecute, EmployeePrevCanExecute);
        //EmployeeTotalHoursCommand = new RelayCommand(EmployeeTotalHoursExecute, EmployeeTotalHoursCanExecute);
    }
    //private void EmployeeTotalHoursExecute()
    //{
    //    _selectedEmployee.TotalHours();
    //}
    //private bool EmployeeTotalHoursCanExecute()
    //{
    //    return true;
    //}
    private void EmployeeAddNewExecute()
    {
        EmployeeModel newEmployee = new EmployeeModel();
        EmployeesView.Add(newEmployee);
        _employeesModel.Add(newEmployee);
        SelectedEmployee = newEmployee;
    }
    private bool EmployeeAddNewCanExecute()
    {
        return true;
    }
    private void EmployeeDeleteNewExecute()
    {
        if (MessageBox.Show("You are about delete all submissions for     Employee," + SelectedEmployee.Name + "(" + SelectedEmployee.ID +")'r'nAre you sure?", "This is a Warning!", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
        {
            _employeesModel.Remove(SelectedEmployee);
            EmployeesView.Remove(SelectedEmployee);
        }
    }
    private bool EmployeeDeleteCanExecute()
    {
        if (SelectedEmployee != null)
            return true;
        else return false;
    }
    private void EmployeeNextExecute()
    {
        SelectedEmployeeIndex++;
    }
    private bool EmployeeNextCanExecute()
    {
        if (SelectedEmployeeIndex < EmployeesView.Count - 1)
            return true;
        return false;
    }
    private void EmployeePrevExecute()
    {
        SelectedEmployeeIndex--;
    }
    private bool EmployeePrevCanExecute()
    {
        if (SelectedEmployeeIndex > 0)
            return true;
        return false;
    }
}

视图

    public partial class MainWindow : Window
{
    public EmployeeViewModel EmployeeViewModel = new EmployeeViewModel();
    public MainWindow()
    {
        InitializeComponent();
        menu_employee.DataContext = EmployeeViewModel;
        sp_employees.DataContext = EmployeeViewModel;
        datagrid_employees.ItemsSource = EmployeeViewModel.EmployeesView;
        grid_selectedEmployee.DataContext =  EmployeeViewModel.SelectedEmployee;
    }
}

MVVM 绑定无法正常工作

我可以看到你的代码存在一些问题:

  • 当选定索引更新时,选定项保持不变,反之亦然。
  • 看起来数据绑定顺序不正常:

DataContext属性向下级联到某个依赖项对象的每个子对象。

MainWindow 构造函数中的代码可能应该替换为:

this.DataContext = EmployeeViewModel;

然后在 XAML 中使用数据绑定设置其余属性。您遇到的问题是所选员工的数据上下文仅设置一次。这意味着如果您选择其他员工,则它不会更新。

"选定员工"网格的示例:

<Grid Name="grid_selectedEmployee" DataContext="{Binding SelectedEmployee, 
UpdateSourceTrigger=PropertyChanged}">...</Grid>

我看到的最重要的事情之一是您正在设置属性,而不是绑定它们。

例如

datagrid_employees.ItemsSource = EmployeeViewModel.EmployeesView;

您正在告诉您的 DataGrid 它的 ItemsSource 应该是该特定对象。您需要将其绑定到该值,以便告诉它指向该属性。这将使您的 UI 正确反映您的视图模型中的内容

我看到的另一个巨大的危险信号是你的ViewModel引用了一些叫做和EmployeeView的东西,这让我相信你的View和ViewModel太紧密地联系在一起了。ViewModel应包含所有业务逻辑和代码,而视图通常是 XAML,只是以用户友好的方式反映 ViewModel。

视图和视图模型

永远不应该直接相互引用(我在我的视图在极少数情况下引用我的视图模型,但永远不会相反(

例如,EmployeesViewModel可能包含

  • ObservableCollection<Employee> Employees
  • Employee SelectedEmployee
  • ICommand AddEmployeeCommand
  • ICommand DeleteEmployeeCommand

而您的视图 (XAML( 可能如下所示:

<StackPanel>
    <StackPanel Orientation="Horizontal">
        <Button Content="Add" Command="{Binding AddEmployeeCommand}" />
        <Button Content="Delete" Command="{Binding DeleteEmployeeCommand}" />
    </StackPanel>
    <DataGrid ItemsSource="{Binding Employees}"
              SelectedItem="{Binding SelectedEmployee}">
        ... etc
    </DataGrid>
    <UniformGrid DataContext="{Binding SelectedEmployee}" Columns="2" Rows="4">
        <TextBlock Text="ID" />
        <TextBox Text="{Binding Id}" />
        ... etc
    </UniformGrid >
</StackPanel>

您唯一应该设置的是整个窗口的数据上下文。通常我会覆盖App.OnStartup()以启动我的应用程序:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var view = new MainWindow();
        var vm = new EmployeesViewModel;
        view.DataContext = vm;
        view.Show();
    }
}

虽然我想在你的情况下这也行得通:

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