具有ObservableCollection和INotifyPropertyChanged的WPF MVVM问题

Dem*_*ake 2 c# wpf mvvm

我在添加和删除项目时遇到问题ObservableCollection。单击绑定的按钮后,SQL Server数据库成功更新,但是调用方法WorkerInfoCollection.AddWorkerInfoCollection.RemoveUI后未更新。此外,WorkerInfoCollection.Remove(SelectedWorker)完全不删除项目。仅当我使用LINQ First或Default时,我才能删除此项目,但是-UI不变。

经过几个小时的调试和网络搜索的,我试图改变按钮命令参数:ElementNamePath使用RelativeSource:AncestorType,在视图模型构造设定指令值,设置在二传手命令值,提高OnPropertyChangedObservableCollection,使用从绑定,添加和删除项目的不同模式ObservableCollection,甚至尝试ObservableCollection在运行时直接从数据库重新初始化并填充它(有点愚蠢)。没有任何帮助。在网络中搜索也无济于事。

MainWindow.xaml

<Grid>
    <DataGrid Name="WorkerInfoData" 
              HorizontalAlignment="Stretch" 
              Margin="10,10,50,10" 
              VerticalAlignment="Stretch" 
              ItemsSource="{Binding WorkerInfoCollection}"
              SelectedItem="{Binding SelectedWorker,
                                     Mode=TwoWay}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID ?????????" 
                                Binding="{Binding WorkerId}" />
            <DataGridTemplateColumn Header="????"
                                    Width="SizeToCells" 
                                    IsReadOnly="True">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Image Source="{Binding WorkerPhoto}"
                               Height="75"
                               Width="75"
                               Stretch="UniformToFill"
                               RenderOptions.BitmapScalingMode="Fant"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTextColumn Header="???" 
                                Binding="{Binding WorkerFullName}" />

            <...>

            <DataGridTextColumn Header="?????" 
                                Binding="{Binding WorkerHobby}" />
        </DataGrid.Columns>
    </DataGrid>
    <StackPanel Panel.ZIndex="3" 
                Name="addRecordPanel" 
                Orientation="Horizontal" 
                HorizontalAlignment="Right" 
                Margin="0,0,-369,0" 
                Width="417"
                DataContext="{Binding NewWorker,
                                      Mode=TwoWay}">
        <Grid>
            <Button Name="buttonHideAddRecordPanel" 
                    Width="48" 
                    Height="48" 
                    Click="ButtonHideAddRecordPanel_Click" 
                    Visibility="Hidden"
                    Margin="0 0 0 100">
                <Button.Background>
                    <SolidColorBrush Color="White"/>
                </Button.Background>
                <Image Source="Images/ClosePanel.png" 
                       RenderOptions.BitmapScalingMode="Fant"/>
            </Button>
            <Button Name="buttonShowAddRecordPanel"
                    Height="48"
                    Width="48" 
                    Click="ButtonShowAddRecordPanel_Click"
                    Margin="0 0 0 100">
                <Button.Background>
                    <SolidColorBrush Color="White"/>
                </Button.Background>
                <Image Source="Images/AddRecord.ico" 
                       RenderOptions.BitmapScalingMode="Fant"/>
            </Button>
        </Grid>
        <Border BorderBrush="DarkGray" 
                BorderThickness="1.5" 
                Width="369" 
                Background="WhiteSmoke" 
                ScrollViewer.VerticalScrollBarVisibility="Disabled">
            <ScrollViewer VerticalScrollBarVisibility="Auto">
                <StackPanel Margin="0 0 0 10"
                            SnapsToDevicePixels="True">
                    <TextBlock Text="???? ?????? ?????????" 
                               FontSize="16"
                               Background="Gainsboro"
                               TextAlignment="Center"
                               Margin="0"
                               Padding="0 0 0 3"/>
                    <TextBlock Text="ID ?????????"
                               Margin="0 10 0 0"/>
                    <TextBox   Text="{Binding WorkerId,

       UpdateSourceTrigger=PropertyChanged}" 
                               Margin="0 5"
                               Width="300"
                               Background="White"/>
                    <TextBlock Text="???? ?????????" 
                               Margin="0 5 0 0"/>
                    <Border BorderBrush="DarkGray"
                            BorderThickness="1"
                            Width="300"
                            Margin="0 5 0 0">
                        <Image Source="{Binding WorkerPhoto, 

         NotifyOnTargetUpdated=True,

         UpdateSourceTrigger=PropertyChanged, 
                                                Mode=TwoWay,
                                                Converter= 
         {conv:ByteToImage}}" 
                               Height="300"
                               Stretch="UniformToFill"/>
                    </Border>
                    <Button Name="AddNewWorkerPhoto" 
                            Width="150" 
                            FontSize="14" 
                            Content="??????????? ????" 
                            Command="{Binding AddRecordImage}"
                            CommandParameter="{Binding 
         ElementName=addRecordPanel, 

         Path=DataContext}">
                        <Button.DataContext>
                            <VM:WorkerInfoViewModel/>
                        </Button.DataContext>
                    </Button>

                   <...>

                    <Button Name="AddRecord"
                            Content="?????? ?????"
                            FontSize="14"
                            Width="150" 
                            Margin="0 10"
                            Command="{Binding AddRecord}"
                            CommandParameter="{Binding 
            ElementName=addRecordPanel, 

            Path=DataContext}">
                        <Button.DataContext>
                            <VM:WorkerInfoViewModel/>
                        </Button.DataContext>
                    </Button>
                </StackPanel>
            </ScrollViewer>
        </Border>
    </StackPanel>
    <StackPanel Panel.ZIndex="3"
                Name="deleteRecordPanel"
                Orientation="Horizontal" 
                HorizontalAlignment="Right" 
                Margin="0,0,-369,0" 
                Width="417">
        <Grid>
            <Button Name="DeleteRecord"
                    Height="48"
                    Width="48" 
                    Margin="0 100 0 0"
                    Command="{Binding DeleteRecord}"
                    CommandParameter="{Binding 
                ElementName=WorkerInfoData, 
                                               Path=SelectedItem}">
                <Button.DataContext>
                    <VM:WorkerInfoViewModel/>
                </Button.DataContext>
                <Button.Background>
                    <SolidColorBrush Color="White"/>
                </Button.Background>
                <Image Source="Images/DeleteRecord.png" 
                       RenderOptions.BitmapScalingMode="Fant"/>
            </Button>
        </Grid>
    </StackPanel>
</Grid>
Run Code Online (Sandbox Code Playgroud)

WorkerInfo.cs

      public class WorkerInfo : INotifyPropertyChanged
      {
             private int workerId;
      <...>
      public int WorkerId
      {
        get { return workerId; }
        set
        {
            workerId = value;
            OnPropertyChanged(nameof(WorkerId));
        }

      <...>

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName]string prop = "")
    {
        PropertyChanged?.Invoke(this, new 
        PropertyChangedEventArgs(prop));
    }
Run Code Online (Sandbox Code Playgroud)

RelayCommand.cs

public class RelayCommand : ICommand
{
    private Action<object> execute;
    private 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)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return canExecute == null || canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        execute(parameter);
    }
   }
Run Code Online (Sandbox Code Playgroud)

ViewModelBase.cs

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName]string prop = "")
    {
        PropertyChanged?.Invoke(this, new 
  PropertyChangedEventArgs(prop));
    }
}
Run Code Online (Sandbox Code Playgroud)

WorkerInfoViewModel.cs

 public class WorkerInfoViewModel : ViewModelBase
 {
    private SqlDataAdapter dataAdapter;
    private DataTable dataTable;
    private WorkerInfo newWorker;
    private WorkerInfo selectedWorker;

    public WorkerInfo NewWorker
    {
        get
        {
            if (newWorker == null)
            {
                return newWorker = new WorkerInfo();
            }
            else return newWorker;
        }
        set
        {
            newWorker = value;
        }
    }
    public WorkerInfo SelectedWorker
    {
        get { return selectedWorker; }
        set
        {
            selectedWorker = value;
            OnPropertyChanged("SelectedWorker");
        }
    }

    private ObservableCollection<WorkerInfo> workerInfoCollection;
    public ObservableCollection<WorkerInfo> WorkerInfoCollection
    {
        get { return workerInfoCollection; }
        set
        {
            workerInfoCollection = value;
            OnPropertyChanged("WorkerInfoCollection");
        }

    }

    public ICommand AddRecord { get; }
    public ICommand AddRecordImage { get; }
    public ICommand UpdateRecord { get; }
    public ICommand UpdateRecordImage { get; }

    private ICommand deleteCommand;
    public ICommand DeleteRecord
    {
        get
        {
            if (deleteCommand == null)
            {
                deleteCommand = new RelayCommand(parameter => 
        DeleteRecord_Click(parameter));
            }
            return deleteCommand;
        }
    }


    public WorkerInfoViewModel()
    {
        string selectQuery = "SELECT * FROM [WorkerInfo]";

        using (SqlConnection connection = new 
             SqlConnection(builder.ConnectionString))
        {
            using (SqlCommand command = new SqlCommand(selectQuery, 
            connection))
            {
                connection.Open();

                using (dataAdapter = new SqlDataAdapter(command))
                {
                    dataTable = new DataTable();
                    dataAdapter.Fill(dataTable);

                    WorkerInfoCollection = new 
              ObservableCollection<WorkerInfo>();
                    foreach (DataRow dataRow in dataTable.Rows)
                    {
                        WorkerInfoCollection.Add
                            (
                               new WorkerInfo()
                               {
                                   WorkerId = 
                 Convert.ToInt32(dataRow["WorkerId"]),

                 <...>
                                   WorkerHobby = 
                dataRow["WorkerHobby"].ToString()
                               }
                            );
                    }
                }

                connection.Close();
            }
        }

        AddRecord = new RelayCommand(parameter => 
             AddRecord_Click(parameter));
        AddRecordImage = new RelayCommand(parameter => 
                  AddWorkerPhoto_Click(parameter));

        UpdateRecord = new RelayCommand(parameter => 
                   UpdateRecord_Click(parameter));
        UpdateRecordImage = new RelayCommand(parameter => 
             UpdateWorkerPhoto_Click(parameter));

        //DeleteRecord = new RelayCommand(parameter => 
                DeleteRecord_Click(parameter));
    }


    private void AddRecord_Click(object parameter)
    {
        NewWorker = (WorkerInfo)parameter;

        WorkerInfoCollection.Add
            (
                new WorkerInfo()
                {
                    WorkerId = NewWorker.WorkerId,
                    <...>
                    WorkerHobby = NewWorker.WorkerHobby
                }
            );


        string insertQuery = "INSERT INTO [WorkerInfo] (" +
                "WorkerId, " +
                    <...>
                "@WorkerHobby)";

        using (SqlConnection connection = new 
            SqlConnection(builder.ConnectionString))
        {
            connection.Open();

            using (SqlCommand command = new SqlCommand(insertQuery, 
               connection))
            {
                try
                {

                    command.Parameters.AddWithValue("@WorkerId", 
                NewWorker.WorkerId);

                    <...>

                    command.Parameters.AddWithValue("@WorkerHobby", 
              NewWorker.WorkerHobby);

                    command.ExecuteNonQuery();

                    connection.Close();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("error");

                    log.Error(ex);
                }
            }
        }
    }

    private void DeleteRecord_Click(object parameter)
    {
        SelectedWorker = (WorkerInfo)parameter;

        string deleteRecord = "DELETE FROM [WorkerInfo] " +
                              "WHERE WorkerId = @WorkerId";

        if (SelectedWorker != null)
        {
            using (SqlConnection connection = new 
           SqlConnection(builder.ConnectionString))
            {
                connection.Open();

                using (SqlCommand command = new SqlCommand(deleteRecord, 
               connection))
                {
                    try
                    {
                        command.Parameters.AddWithValue("@WorkerId", 
              SelectedWorker.WorkerId);

                        command.ExecuteNonQuery();

                        connection.Close();

                        WorkerInfoCollection.Remove(SelectedWorker);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("error");

                        log.Error(ex);
                    }
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

And*_*ndy 5

When you do:

          <Button.DataContext>
                <VM:WorkerInfoViewModel/>
            </Button.DataContext>
Run Code Online (Sandbox Code Playgroud)

That instantiates another instance of workerinfo viewmodel.

Remove that.

You want to be adding and removing from the same observablecollection in the same viewmodel instance the itemssource of your datagrid is bound to.

As it is, you have at least 3 instances of that viewmodel.

Make sure you just have the one instance of your viewmodel and that is the datacontext of mainwindow and hence the buttons and datagrid.