具有ObservableCollection和INotifyPropertyChanged的WPF MVVM问题

时间:2019-05-12 16:19:58

标签: c# wpf mvvm

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

经过许多小时的调试和网络搜索,我尝试更改按钮命令参数:ElementNamePath,使用RelativeSource:AncestorType,在ViewModel构造函数中设置Command值,设置Command设置器中的值,在OnPropertyChanged中提高ObservableCollection,使用不同的绑定模式,从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>

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));
    }

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);
    }
   }

ViewModelBase.cs

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

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);
                    }
                }
            }
        }
    }

1 个答案:

答案 0 :(得分:5)

当您这样做:

          <Button.DataContext>
                <VM:WorkerInfoViewModel/>
            </Button.DataContext>

这将实例化workerinfo视图模型的另一个实例。

删除。

您想在同一viewmodel实例中的同一observablecollection中添加和删除其数据网格的itemsource。

实际上,您至少有3个该视图模型的实例。

确保您只有一个ViewModel实例,那是mainwindow的数据上下文,因此是按钮和datagrid。