简体   繁体   中英

MVVM WPF Add new item to the DataGrid from Textboxes which are bound to the nested properties of the Model class

I am new to WPF and also the MVVM pattern, I tried to create a simple WPF project to query a database of I-Shape Steel Profiles and show it to the user through the DataGrid. After I have successfully done it, I am now moving to the next part: Letting the user to add new steel profiles to the DataGrid by filling out a set of TextBoxes.

Model

namespace SectionGuardWPF.MVVM.Model
{
    public class IProfileModel : ObservableObject
    {
        #region Private Fields
        public short _recno;
        public string _name;
        public string _staadName;
        public double _ax;
        public double _d;
        public double _bf;
        public double _tf;
        public double _tw;
        public double _iz;
        public double _ix;
        public double _ct;
        public double _iy;
        public double _zx;
        public double _zy;
        #endregion

        #region Public Properties
        /// <summary>
        /// Properties are given NotifyPropertyChanged method to allow for a change in property to be notified
        /// </summary>

        public short RECNO
        {
            get { return _recno; }
            set
            {
                _recno = value;
                OnPropertyChanged();
            }
        }
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged();
            }
        }
...
// Repeated for the other fields

ObservableObject: Base class that implements INotifyPropertyChanged

namespace SectionGuardWPF.Core
{
    public class ObservableObject : INotifyPropertyChanged
    {
        /// <summary>
        /// Raised when a property on this object has a new value.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises this object's PropertyChanged event.
        /// </summary>
        protected void OnPropertyChanged([CallerMemberName] string name = null) // If you use the CallerMemberName attribute, calls to the NotifyPropertyChanged method don't have to specify the property name as a string argument
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }
}

RelayCommand

namespace SectionGuardWPF.Core
{
    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)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            return _canExecute == null || _canExecute(parameter);
        }
        
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
    }
}

View Model

namespace SectionGuardWPF.MVVM.ViewModel
{
    public class IProfilesViewModel:ObservableObject
    {
        #region Private Fields
        private readonly string _connectionString = //Hidden
        private ObservableCollection<IProfileModel> _iProfiles;
        private ICommand _getIProfilesCommand;
        private ICommand _addNewIProfileCommand;
        private IProfileModel _iProfile = new IProfileModel();
        #endregion

        #region Constructor
        public IProfilesViewModel()
        {
            // Set default Profile Database for the DataGrid upon View instantiation
            IProfiles = GetIProfiles();
        }
        #endregion

        #region Public Properties and Commands
        /// <summary>
        /// Public properties of the IProfilesViewModel Class (used for view binding with IProfilesSubView)
        /// </summary>
        public ObservableCollection<IProfileModel> IProfiles
        {
            get { return _iProfiles; }
            set
            {
                _iProfiles = value;
                OnPropertyChanged();
            }
        }
        public IProfileModel IProfile
        {
            get { return _iProfile; }
            set
            {
                _iProfile = value;
                OnPropertyChanged();
            }
        }
        public ICommand GetIProfilesCommand
        {
            get
            {
                if (_getIProfilesCommand == null)
                {
                    _getIProfilesCommand = new RelayCommand(
                        param => GetIProfiles()
                    );
                }
                return _getIProfilesCommand;
            }
        }
        public ICommand AddNewIProfileCommand
        {
            get
            {
                if (_addNewIProfileCommand == null)
                {
                    _addNewIProfileCommand = new RelayCommand(
                        param => AddNewIProfile()
                    );
                }
                return _addNewIProfileCommand;
            }
        }
        #endregion

        #region Private Methods
        private ObservableCollection<IProfileModel> GetIProfiles()
        {
            TableDataProvider tableDataProvider = new TableDataProvider(_connectionString);
            tableDataProvider.QueryString = "SELECT * FROM [I Shape]";

            IEnumerable<IProfileModel> iProfiles = tableDataProvider.Query<IProfileModel>();
            ObservableCollection<IProfileModel> iProfileObsvCol = new ObservableCollection<IProfileModel>(iProfiles);
            return iProfileObsvCol;
        }

        private void AddNewIProfile()
        {
            IProfiles.Add(
                new IProfileModel
                {
                    RECNO = IProfile.RECNO,
                    Name = IProfile.Name,
                    StaadName = IProfile.StaadName,
                    AX = IProfile.AX,
                    D = IProfile.D,
                    Bf = IProfile.Bf,
                    Tf = IProfile.Tf,
                    Tw = IProfile.Tw,
                    Ix = IProfile.Ix,
                    Iy = IProfile.Iy,
                    Iz = IProfile.Iz,
                    Ct = IProfile.Ct,
                    Zx = IProfile.Zx,
                    Zy = IProfile.Zy
                }
                );
        }

        #endregion
    }
}

View

<UserControl x:Class="SectionGuardWPF.MVVM.View.IProfilesSubView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:SectionGuardWPF.MVVM.View"
             xmlns:viewModel= "clr-namespace:SectionGuardWPF.MVVM.ViewModel"
             xmlns:view="clr-namespace:SectionGuardWPF.MVVM.View"
             mc:Ignorable="d" 
             d:DesignHeight="580" d:DesignWidth="1300">

    <UserControl.DataContext>
        <viewModel:IProfilesViewModel/>
    </UserControl.DataContext>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="620"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <DataGrid x:Name="ProfilesDataGrid"
                      x:FieldModifier="public"
                      ItemsSource="{Binding IProfiles, Mode=TwoWay}">

        </DataGrid>

        <Border Grid.Column="1"
                CornerRadius="20"
                Background ="White"
                Margin="10">
            <Grid Margin="10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="50"/>
                    <RowDefinition/>
                    <RowDefinition Height="150"/>
                </Grid.RowDefinitions>

                <TextBlock Text="I-Profiles Data Viewer"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Left"
                   Margin="20,0,0,0"
                   Foreground="Black"
                   FontSize="22"/>

                <Grid Grid.Row="1">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>

                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>

                    <TextBlock Text="RECNO"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="0"
                               Grid.Row="0"
                               FontSize="14"/>

                    <TextBox x:Name="RECNOTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="0"
                             Grid.Row="0"
                             Text="{Binding IProfile.RECNO, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="Name"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="0"
                               Grid.Row="1"
                               FontSize="14"/>

                    <TextBox x:Name="NameTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="0"
                             Grid.Row="1"
                             Text="{Binding IProfile.Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="StaadName"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="0"
                               Grid.Row="2"
                               FontSize="14"/>

                    <TextBox x:Name="StaadNameTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="0"
                             Grid.Row="2"
                             Text="{Binding IProfile.StaadName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="AX"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="0"
                               Grid.Row="3"
                               FontSize="18"/>

                    <TextBox x:Name="AXTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="0"
                             Grid.Row="3"
                             Text="{Binding IProfile.AX, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="D"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="0"
                               Grid.Row="4"
                               FontSize="18"/>

                    <TextBox x:Name="DTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="0"
                             Grid.Row="4"
                             Text="{Binding IProfile.D, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="Bf"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="0"
                               Grid.Row="5"
                               FontSize="18"/>

                    <TextBox x:Name="BfTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="0"
                             Grid.Row="5"
                             Text="{Binding IProfile.Bf, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="Tf"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="0"
                               Grid.Row="6"
                               FontSize="18"/>

                    <TextBox x:Name="TfTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="0"
                             Grid.Row="6"
                             Text="{Binding IProfile.Tf, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="Tw"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="1"
                               Grid.Row="0"
                               FontSize="18"/>

                    <TextBox x:Name="TwTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="1"
                             Grid.Row="0"
                             Text="{Binding IProfile.Tw, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="Ix"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="1"
                               Grid.Row="1"
                               FontSize="18"/>

                    <TextBox x:Name="IxTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="1"
                             Grid.Row="1"
                             Text="{Binding IProfile.Ix, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="Iy"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="1"
                               Grid.Row="2"
                               FontSize="18"/>

                    <TextBox x:Name="IyTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="1"
                             Grid.Row="2"
                             Text="{Binding IProfile.Iy, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="Iz"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="1"
                               Grid.Row="3"
                               FontSize="18"/>

                    <TextBox x:Name="IzTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="1"
                             Grid.Row="3"
                             Text="{Binding IProfile.Iz, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="Ct"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="1"
                               Grid.Row="4"
                               FontSize="18"/>

                    <TextBox x:Name="CtTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="1"
                             Grid.Row="4"
                             Text="{Binding IProfile.Ct, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="Zx"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="1"
                               Grid.Row="5"
                               FontSize="18"/>

                    <TextBox x:Name="ZxTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="1"
                             Grid.Row="5"
                             Text="{Binding IProfile.Zx, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

                    <TextBlock Text="Zy"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Margin="20,0,0,0"
                               Foreground="#1A80FA"
                               Grid.Column="1"
                               Grid.Row="6"
                               FontSize="18"/>

                    <TextBox x:Name="ZyTextBox"
                             Margin="0,0,20,0"
                             VerticalAlignment="Center"
                             HorizontalAlignment="Right"
                             Style="{StaticResource ParameterTextbox}"
                             Grid.Column="1"
                             Grid.Row="6"
                             Text="{Binding IProfile.Zy, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
                </Grid>

                <StackPanel Grid.Row="2"
                            Orientation="Horizontal">

                    <Button x:Name="AddButton"
                            Style="{StaticResource AddButton}"
                            Content="Add"
                            Background="#28C941"
                            Height="50"
                            Width="150"
                            Margin="85,0,0,0"
                            Command="{Binding AddNewIProfileCommand}"/>
                    <Button x:Name="EditButton"
                            Style="{StaticResource AddButton}"
                            Content="Edit"
                            Background="#FFBD2E"
                            Height="50"
                            Width="150"
                            Margin="20,0,0,0"/>
                    <Button x:Name="DeleteButton"
                            Style="{StaticResource AddButton}"
                            Content="Delete"
                            Background="#FF6059"
                            Height="50"
                            Width="150"
                            Margin="20,0,0,0"/>

                </StackPanel>
                
            </Grid>
        </Border>
    </Grid>
</UserControl>

My Model class is a class called the IProfileModel that represents the I-Shape Profiles, I have made so that it inherits from the ObservableObject class so that it can use the NotifyPropertyChanged methods and I have made so that the properties of the Model class has implemented the OnPropertyChanged() Method.

In my View, I have bound the source of the DataGrid to the IProfiles which is an ObservableCollection that stores instances of the IProfileModel . Each TextBox corresponds to one property of the IProfileModel class, thus the Text property of each TextBox is bound to its respective nested property of the IProfile property of the ViewModel. The "Add" Button is bound to the respective ICommand in the ViewModel called the AddNewIProfileCommand

In my ViewModel, I have added the IProfileModel as one of its properties using the name IProfile , the TextBox.Text properties are bound to the nested properties of the IProfile as shown in the XAML of the View. As for the AddNewIProfileCommand , I have made so that it corresponds to a private method called the AddNewIProfile() which will add a new IProfileModel object whose properties will refer to the nested properties of the IProfile property that is bound to the TextBoxes.

Edit : I want to add that the AddNewIProfile() will add the new IProfileModel object to the IProfiles which is bound to the DataGrid

My main issue is that whenever I modified the TextBoxes and then clicked on the Button to add the profiles to the DataGrid, the newly added entry to the DataGrid is always a IProfileModel with empty property values (all string properties of the IProfileModel are null and all numerical type properties are 0). It seems like modifying the TextBoxes does not change the nested properties of the IProfile even though I have set the UpdateSourceTrigger to PropertyChanged and the Mode to Two Way and the IProfile itself is of the IProfileModel class which has inherited from the ObservableObject class.

Lastly, I know that my question is quite similar to this one but I have followed the suggestions there and tried to match their solution but I somehow can't get it to work. Thanks!

Edit : Added some before - after picture of the DataGrid after the "Add" Button is pressed: 在此处输入图像描述 在此处输入图像描述

I tried to replicate your problem but I had to remove the styles since you did not provide them. It works at my side, so perhaps the problem lies within the style?

在此处输入图像描述

Please delete your styles for the textboxes (ParameterTextbox) and see if it works then. If you can provide me the source code for the style, I can take a look at it. Maybe you are overriding the text property.

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