RelayCommand Params & Binding - wpf

View:
Playing with a basic calculator using WPF(MVVM).
I've 1 TextBox for the first num, 1 TextBox for the second num, 1 TextBlock for the results and 1 Button to execute the AddCommand and return the result.
What's the right XAML syntax to bind these controls to the right Data.
Model:
public class Operation : INotifyPropertyChanged
{
private double _result;
public Operation()
{
_result = 0;
}
public double Result
{
get { return _result; }
set
{
if (value != _result)
{
_result = value;
RaisePropertyChanged("Result");
}
}
}
public double DoAdd(double first, double second)
{
_result = first + second;
return _result;
}
}
ViewModel:
public class CalcViewModel
{
private Operation _operation;
public RelayCommand AddCommand { get; set; }
public CalcViewModel()
{
_operation = new Operation();
// This is not correct, how to define the AddCommand here so it takes two params
// The first and second nums to work with.
AddCommand = new RelayCommand(first, second => ExecuteAddCommand(first, second));
}
private void ExecuteAddCommand(double first, double second)
{
// How to bind this returned double to the TextBlock in View
_oepration.DoAdd(first, second);
}
}
EDIT new version of code on request of Vlad
Model:
public class Operation
{
private double _result;
public Operation()
{
_result = 0;
}
public double Result
{
get { return _result; }
}
public void PerformAdd(double leftNum, double rightNum)
{
_result = leftNum + rightNum;
}
}
ViewModel:
public class CalcViewModel
{
private Operation _operation;
public double LeftNumber { get; set; }
public double RightNumber { get; set; }
public double Result { get; set; }
public RelayCommand AddCommand { get; set; }
public CalcViewModel()
{
AddCommand = new RelayCommand(a => ExecuteAddCommand());
_operation = new Operation();
}
private void ExecuteAddCommand()
{
_operation.PerformAdd(LeftNumber, RightNumber);
Result = _operation.Result;
}
View XAML:
<TextBox Text="{Binding LeftNumber}" />
<TextBox Text="{Binding RightNumber}" />
<TextBox Text="{Binding Result}" />
<Button Content="Add" Command="{Binding AddCommand}" />
View Code behind:
public partial class CalcUserControl : UserControl
{
CalcViewModel vm;
public CalcUserControl()
{
InitializeComponent();
vm = new CalcViewModel();
this.DataContext = vm;
}
}
I tried all modes of binding without any result. I have here an additional question, what's the default binding mode in such a situation?
I even thought that it has to do with the datatype of the calculation, so I swiched from double to int, but still not working.

Well, let's see what can be done.
1) Model. The model doesn't need anything fancy. I would keep it simple and make it just return the value and not use NotifyPropertyChanged. After all, it's a model.
public class BinaryOperation
{
double _l, _r, _result = 0.0;
public Operation(double l, double r)
{
_l = l; _r = r;
}
public double Result
{
get { return _result; }
}
public PerformAdd()
{
_result = _l + _r;
}
}
2) ViewModel. Here, your RelayCommand doesn't really need any arguments. But you need to store the values of operands in your VM, so that your view can bind to them, instead of sending them in a command. Remember, business logic doesn't belong to view, view just blindly binds to the VM! So you need 3 DPs (left addend, right addend, result) in your VM.
3) When the command arrives, you just take the addends from VM, ask the model to perform the operation, retrieve the result and assign it to your VM's result DP. (Right now, your model operations are fast, so you don't need to do it in asynchronous way. But maybe in the future...)
4) View. You need for your Window/UserControl just to bind to the VM's properties.
Its going to be something as simple as:
<TextBox Text="{Binding LeftAddend}"/>
<TextBox Text="{Binding RightAddend}"/>
<TextBox Text="{Binding Result}"/>
<Button Command="{Binding AddCommand}">Add</Button>
(Don't forget to set the DataContext right.)
Edit:
the VM class has to be a dependency object! And the properties should b defined as dependency properties. Something like this:
public class CalcViewModel : DependencyObject
{
private Operation _operation;
public double LeftNumber
{
get { return (double)GetValue(LeftNumberProperty); }
set { SetValue(LeftNumberProperty, value); }
}
public static readonly DependencyProperty LeftNumberProperty =
DependencyProperty.Register("LeftNumber", typeof(double), typeof(CalcViewModel));
public double RightNumber
{
get { return (double)GetValue(RightNumberProperty); }
set { SetValue(RightNumberProperty, value); }
}
public static readonly DependencyProperty RightNumberProperty =
DependencyProperty.Register("RightNumber", typeof(double), typeof(CalcViewModel));
public double Result
{
get { return (double)GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
public static readonly DependencyProperty ResultProperty =
DependencyProperty.Register("Result", typeof(double), typeof(CalcViewModel));
public RelayCommand AddCommand { get; set; }
public CalcViewModel()
{
AddCommand = new RelayCommand(a => ExecuteAddCommand());
_operation = new Operation();
}
private void ExecuteAddCommand()
{
_operation.PerformAdd(LeftNumber, RightNumber);
Result = _operation.Result;
}
}
Or, if you want to do it with INotifyPropertyChanged, and you are working with .NET 4.5
public class CalcViewModel : INotifyPropertyChanged
{
private Operation _operation;
public event PropertyChangedEventHandler PropertyChanged;
void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
double _leftNumber;
public double LeftNumber
{
get { return _leftNumber; }
set
{
if (value == _leftNumber) return;
_leftNumber = value;
NotifyPropertyChanged();
}
}
double _rightNumber;
public double RightNumber
{
get { return _rightNumber; }
set
{
if (value == _rightNumber) return;
_rightNumber = value;
NotifyPropertyChanged();
}
}
double _result;
public double Result
{
get { return _result; }
set
{
if (value == _result) return;
_result = value;
NotifyPropertyChanged();
}
}
public RelayCommand AddCommand { get; set; }
public CalcViewModel()
{
AddCommand = new RelayCommand(a => ExecuteAddCommand());
_operation = new Operation();
}
private void ExecuteAddCommand()
{
_operation.PerformAdd(LeftNumber, RightNumber);
Result = _operation.Result;
}
}
The same with older .NET versions:
void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
double _leftNumber;
public double LeftNumber
{
get { return _leftNumber; }
set
{
if (value == _leftNumber) return;
_leftNumber = value;
NotifyPropertyChanged("LeftNumber");
}
}
etc.

Thank you all and especially #Vlad. Just one tiny fault, y've declared the property Result twice on class CalcViewModel : DependencyObject.
It works now fine :)

Related

I need your suggestion regarding loop in Model to ViewModel MVVM

I'am new to WPF and MVVM and I was given the task to continue working on one of the unfinished project that is made using the said technology. I've written a sample code below that is similar to the structure of the project.
My concern is, the loop used in GetBookPages() to display the details on the grid might take some time to finish.
public class BookModel
{
public string BookTitle { get; set; }
public List<BookDetailModel> BookDetails { get; set; }
}
public class BookDetailModel
{
public int Pages { get; set; }
public string Others { get; set; }
// ....
}
public class BookViewModel : INotifyPropertyChanged
{
private BookModel _model;
private ObservableCollection<BookDetailViewModel> _bookDetailSource;
private BookService _service;
public BookViewModel()
{
_model = new BookModel();
_service = new BookService();
GetBookPages();
}
/// <summary>
/// This is the item source of datagrid that is located in view
/// </summary>
public ObservableCollection<BookDetailViewModel> BookDetailSource
{
get { return _bookDetailSource; }
set
{
if (value == _bookDetailSource)
return;
_bookDetailSource = value;
OnPropertyChanged();
}
}
private void GetBookPages()
{
BookModel bookModel = _service.GetBookData();
var listOf = new List<BookDetailViewModel>();
bookModel.BookDetails.ForEach(e =>
{
// This is were the system's bottle neck is.
// can someone please suggests me a good work around.
listOf.Add(
new BookDetailViewModel
{
Others = e.Others,
// ....
});
});
BookDetailSource = new ObservableCollection<BookDetailViewModel>(listOf);
}
public string BookTitle
{
get { return _model.BookTitle; }
set
{
if (value == _model.BookTitle)
return;
_model.BookTitle = value;
OnPropertyChanged();
}
}
#region Property Change
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public class BookDetailViewModel : INotifyPropertyChanged
{
private BookDetailModel _model;
#region Constructor
public BookDetailViewModel()
{
_model = new BookDetailModel();
ViewPageDataCommand = new RelayCommand(x => ViewPageData());
RemovePageCommdand = new RelayCommand(x => RemovePage());
}
#endregion
#region Properties
public int Page
{
get { return _model.Pages; }
set
{
if (value == _model.Pages)
return;
_model.Pages = value;
OnPropertyChanged();
}
}
public string Others
{
get { return _model.Others; }
set
{
if (value == _model.Others)
return;
_model.Others = value;
OnPropertyChanged();
}
}
#endregion
// These are the button command inside the grid's row
public ICommand ViewPageDataCommand { get; private set; }
public ICommand RemovePageCommdand { get; private set; }
private void ViewPageData()
{
// view the page data by clicking the row button inside the grid
}
private void RemovePage()
{
// will remove the currently selected row inside the grid
}
#region Property Change
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public class BookService
{
public BookModel GetBookData()
{
var data = GetBookData(99);
data.BookDetails = GetBookDetail(99);
return data;
}
private BookModel GetBookData(int bookId)
{
// return 1 row only
}
private List<BookDetailModel> GetBookDetail(int bookId)
{
// return List<BookDetailModel> that might consists of more than 100 index's
}
}
I hope you understand what I mean. Your suggestion will be much appreciated. Thanks in advance!

Caliburn CanExecute not working even though firing

Trying to implement simple validation with caliburn. All I want is to enable/disable save button based on certain conditions.
View:
`<xctk:MaskedTextBox x:Name="pm_personId" cal:Message.Attach="[Event LostFocus] = [Action CanSave()]" Mask="00-000-000?"/>
<Button Content="Save" x:Name="Save" />`
Model:
public class PersonModel
{
public String personId { get; set; }
public PersonModel() {}
public PersonModel(String id)
{
this.id = personId;
}
}
ViewModel:
[ImplementPropertyChanged]
public class PersonViewModel : Screen
{
public PersonModel pm { get; set; }
public PersonViewModel()
{
pm = new PersonModel();
}
public bool CanSave()
{
MessageBox.Show(pm.personId);
if (pm.personId != null)
return true;
else return false;
}
}
The MessageBox is fired with the right value but button is not enable. Am I missing anything. Either am missing something with caliburn or it's doing too much magic. Am beginning to suspect that the time it may save you initially will be lost in debugging, just my exeprience.
Thanks #CCamilo but your answer was incomplete. For other people who encounter a similar problem, below is my final working code:
[ImplementPropertyChanged]
public class PersonModel
{
public String personId { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public PersonModel() {}
public PersonModel(String id)
{
this.id = personId;
}
}
[ImplementPropertyChanged]
public class PersonViewModel : Screen
{
public PersonModel pm { get; set; }
public PersonViewModel()
{
pm = new PersonModel();
this.pm.PropertyChanged += pm_PropertyChanged;
}
void pm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyOfPropertyChange(() => CanSave);
}
public bool CanSave
{
get { return pm.personId != null; }
}
}
The error you have is with CanSave() method.. It should be a property instead:
public bool CanSave
{
get
{
if (pm.personId != null)
return true;
else return false;
}
}

Again, ObservableCollection doesnt Update item

Thats my first project using MVVM , MVVM light.
I have a listbox, that gets refreshed from the PersonList Observable collection, adding and removing refresh it normal. the problem is when editing an item.
I looked for all the solutions for this problem, nothing worked, which make me think that I missed something.
so here is the code :
public class AdminViewModel : ApplicationPartBaseViewModel
{
private ObservableCollection<Person> personList;
public AdminViewModel()
{
this.context = new Entities();
this.SavePersonCommand = new RelayCommand(() => this.SavePerson ());
this.PersonList = new ObservableCollection<Peson>(context.Person.OrderBy(o => o.PersonName).ToList());
}
public ObservableCollection<Person> PersonList
{
get
{
return personList;
}
set
{
this.personList = value;
RaisePropertyChanged("PersonList");
}
}
private void SavePerson()
{
//Add and update code here
this.context.SaveChanges();
RaisePropertyChanged("PersonList");
}
}
Person Class is Autogenerated template from the DataModel edmx
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
public partial class Person
{
#region Primitive Properties
public virtual int PersonId
{
get;
set;
}
public virtual string PersonName
{
get;
set;
}
public virtual Nullable<int> PersonAge
{
get;
set;
}
#endregion
#region Navigation Properties
public virtual ICollection<Humans> Humans
{
get
{
if (_human == null)
{
var newCollection = new FixupCollection<Human>();
newCollection.CollectionChanged += FixupHuman;
_human = newCollection;
}
return _human;
}
set
{
if (!ReferenceEquals(_human, value))
{
var previousValue = _human as FixupCollection<Human>;
if (previousValue != null)
{
previousValue.CollectionChanged -= FixupHuman;
}
_human = value;
var newValue = value as FixupCollection<Human>;
if (newValue != null)
{
newValue.CollectionChanged += FixupAssets;
}
}
}
}
private ICollection<Human> _human;
#endregion
#region Association Fixup
private void FixupHuman(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Human item in e.NewItems)
{
if (!item.Person.Contains(this))
{
item.Person.Add(this);
}
}
}
if (e.OldItems != null)
{
foreach (Human item in e.OldItems)
{
if (item.Person.Contains(this))
{
item.Person.Remove(this);
}
}
}
}
#endregion
}
I thought that MVVM light update the item when I call RaisePropertyChanged.
I am so confused.
Thanks in advance.
First option is try to get your auto-generated class to implement INPC if you can. Have a look at Fody.PropertyChanged
If that's not possible, since it does have it's properties as "virtual", we can over-ride them in a derived class such as
public class ObservablePerson : Person, INotifyPropertyChanged {
public override int PersonId {
get {
return base.PersonId;
}
set {
base.PersonId = value;
OnPropertyChanged();
}
}
public override string PersonName {
get {
return base.PersonName;
}
set {
base.PersonName = value;
OnPropertyChanged();
}
}
public override int? PersonAge {
get {
return base.PersonAge;
}
set {
base.PersonAge = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Now in your AdminViewModel work with objects of type ObservablePerson than Person

How to create calculated Fields in Partial Classes - WPF

I am trying to use the calculated columns to display in my grid.
I have a partial class automatically generated by EF code generator with three properties: and i am trying to creating another partial class and add calculated field there for e.g.
Public partial class Employee
{
public decimal? totalSalary
{
get
{
return salary*wagerate+bonus;
}
}
}
It works fine for the first time but does not work when the salary/bonus/hours are changed. I am using these fields inside a grid
Here is my code generated by EF entity generator
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.Serialization;
using System.ComponentModel.DataAnnotations;
namespace Employees.Contract
{
[DataContract(IsReference = true)]
[KnownType(typeof(Department))]
[KnownType(typeof(PropertyType))]
public partial class Employee: IObjectWithChangeTracker, INotifyPropertyChanged,IDataErrorInfo
{
[NonSerialized]
private CLOS.Contract.Validation.DataErrorInfoSupport dataErrorInfoSupport;
public Employee()
{
dataErrorInfoSupport = new CLOS.Contract.Validation.DataErrorInfoSupport(this);
Init();
}
partial void Init();
string IDataErrorInfo.Error { get { return dataErrorInfoSupport.Error; } }
string IDataErrorInfo.this[string memberName] { get { return dataErrorInfoSupport[memberName]; } }
#region Primitive Properties
[DataMember]
public Nullable<decimal> Salary
{
get { return _salary; }
set
{
if (_salary != value)
{
_salary = value;
OnPropertyChanged("Salary");
}
}
}
private Nullable<decimal> _salary;
[DataMember]
public Nullable<decimal> WageRate
{
get { return _wageRate; }
set
{
if (_wageRate != value)
{
_wageRate = value;
OnPropertyChanged("WageRate");
}
}
}
private Nullable<decimal> _wageRate;
[DataMember]
public Nullable<decimal> Bonus
{
get { return _bonus; }
set
{
if (_bonus != value)
{
_bonus = value;
OnPropertyChanged("Bonus");
}
}
}
private Nullable<decimal> _bonus;
#endregion
#region Navigation Properties
[DataMember]
public Department Department
{
get { return _department; }
set
{
if (!ReferenceEquals(_department, value))
{
var previousValue = _department;
_department = value;
OnNavigationPropertyChanged("Department");
}
}
}
private Borrower _department;
[DataMember]
public PropertyType PropertyType
{
get { return _propertyType; }
set
{
if (!ReferenceEquals(_propertyType, value))
{
var previousValue = _propertyType;
_propertyType = value;
OnNavigationPropertyChanged("PropertyType");
}
}
}
private PropertyType _propertyType;
#endregion
#region ChangeTracking
protected virtual void OnPropertyChanged(String propertyName)
{
if (ChangeTracker.State != ObjectState.Added && ChangeTracker.State != ObjectState.Deleted)
{
ChangeTracker.State = ObjectState.Modified;
}
if (_propertyChanged != null)
{
_propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected virtual void OnNavigationPropertyChanged(String propertyName)
{
if (_propertyChanged != null)
{
_propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged{ add { _propertyChanged += value; } remove { _propertyChanged -= value; } }
private event PropertyChangedEventHandler _propertyChanged;
private ObjectChangeTracker _changeTracker;
[DataMember]
public ObjectChangeTracker ChangeTracker
{
get
{
if (_changeTracker == null)
{
_changeTracker = new ObjectChangeTracker();
_changeTracker.ObjectStateChanging += HandleObjectStateChanging;
}
return _changeTracker;
}
set
{
if(_changeTracker != null)
{
_changeTracker.ObjectStateChanging -= HandleObjectStateChanging;
}
_changeTracker = value;
if(_changeTracker != null)
{
_changeTracker.ObjectStateChanging += HandleObjectStateChanging;
}
}
}
private void HandleObjectStateChanging(object sender, ObjectStateChangingEventArgs e)
{
if (e.NewState == ObjectState.Deleted)
{
ClearNavigationProperties();
}
}
protected bool IsDeserializing { get; private set; }
[OnDeserializing]
public void OnDeserializingMethod(StreamingContext context)
{
IsDeserializing = true;
}
[OnDeserialized]
public void OnDeserializedMethod(StreamingContext context)
{
dataErrorInfoSupport = new CLOS.Contract.Validation.DataErrorInfoSupport(this);
IsDeserializing = false;
ChangeTracker.ChangeTrackingEnabled = true;
}
protected virtual void ClearNavigationProperties()
{
Department = null;
PropertyType = null;
}
#endregion
}
}
It also works if i put OnPropertyChanged("Salary") in Hours,Wage,Overtime Property in EF Generated class (which is not a good idea) because if the class gets regenerated , my code will be wiped out
Any help is appreciated. (Sorry for the formatting , this is my first question)
Thanks
In the partial class use the partial Init method to subscribe to the PropertyChanged event and when either the salary, wagerate or bonus property changes notify clients of the change of the totalSalary.
This way you do not need to modify the generated code. (that is why the Init method is partial).
public partial class Employee
{
partial void Init()
{
_propertyChanged += PropertyChangedHandler;
}
void PropertyChangedHandler(object sender, PropertyChangedEventArgs args)
{
if(args.PropertyName == "salary" ||
args.PropertyName == "wagerate" ||
args.PropertyName == "bonus")
{
OnPropertyChanged("totalSalary");
}
}
public decimal? totalSalary
{
get
{
return salary * wagerate + bonus;
}
}
}
this is why MVVM is a popular design pattern, if you wrap your Employee (a Model) in a new class (a ViewModel), it will be easier to customise before you hand it to the grid (the View).
However, a hacky way to get your current code working would be to attach to the current PropertyChanged event in your partial class and call OnPropertyChanged("Salary") if the current property name matches one of the dependent properties (watch out for recursion!)

How to pass CommandParameters to the ViewModel?

I have a command that should switch the current view when it's executed. I binded this command to my buttons like this:
<Button Style="{StaticResource TextButton}" Command="{Binding ViewModel:MainViewModel.OpenItemCommand}" CommandParameter="{Binding Link}"/>
I want to pass Link (the link of the currently selected article) to my command. My command is defined like this:
public class Command : ICommand
{
public event EventHandler CanExecuteChanged;
readonly Predicate<Object> _canExecute;
readonly Action<Object> _executeAction;
public Command(Predicate<Object> canExecute, Action<object> executeAction)
{
_canExecute = canExecute;
_executeAction = executeAction;
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
return _canExecute(parameter);
return true;
}
public void UpdateCanExecuteState()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
public void Execute(object parameter)
{
if (_executeAction != null)
_executeAction(parameter);
UpdateCanExecuteState();
}
}
In my ViewModel I have this:
public ICommand OpenItemCommand
{
get
{
if (_openItemCommand == null)
{
_openItemCommand = new Command.Command(
p => true,
p => OpenItem(_HOW_DO_I_GET_THE_PARAMETER?_)
);
}
return _openItemCommand;
}
set
{
if (_openItemCommand != value)
{
_openItemCommand = value;
RaisePropertyChanged("OpenItemCommand");
}
}
}
private void OpenItem(Uri link)
{
throw new NotImplementedException();
}
When I create the command I need to pass the command parameter (the link) to the Execute method. But how do I get the value of this? I defined the CommandParameter in XAML, but I don't know how to access it.
I really searched through a huge amount of websites but I can't really find the answer.
You should look at the implementation of Prism's DelegateCommand or MVVM light's RelayCommand. With these you would write code like this:
public class ViewModel
{
public ViewModel()
{
OpenItemCommand = new RelayCommand<string>(OpenItem);
}
public RelayCommand<string> OpenItemCommand { get; private set; }
private void OpenItem(string link)
{
Debug.WriteLine(link);
}
}
where string in this case is the type of the parameter.
I'm not sure where the link parameter is coming from but if it's from a control, the value of the control could be bound to a property of your view model, then you don't need a parameter, for example:
public class ViewModel
{
public ViewModel()
{
OpenItemCommand = new RelayCommand(OpenItem);
}
public RelayCommand OpenItemCommand { get; private set; }
public string Link { get; set; }
private void OpenItem()
{
Debug.WriteLine(Link);
}
}
replace
p => OpenItem(_HOW_DO_I_GET_THE_PARAMETER?_)
with
p => OpenItem(p)
that is what the p stands for: parameter

Resources