How can I set a selection in a DataGridComboBoxColumn? - c#

How can I set a DataGridComboBoxColumn item selection programmatically?
I want to populate my list with data, and the comboboxitem should set the selected item from the list.
But I dont find any way to achieve that.
In this example the selection should be "Forward"
XAML:
<DataGrid ItemsSource="{Binding }" x:Name="dgSFD" AlternatingRowBackground="BlanchedAlmond" SelectionChanged="dgSFD_SelectionChanged" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=sID}" Header="Step ID"/>
<DataGridComboBoxColumn x:Name="cbServo" Header="Servo" SelectedItemBinding="{Binding Servo, Mode =TwoWay}" SelectedValuePath="sFunctionName ,Mode=TwoWay" DisplayMemberPath="sFunctionName"/>
</DataGrid>
Class
public class Step
{
public string sID { get; set; }
public Servo serServo { get; set; }
}
public class Servo
{
public string sFunction { get; set; }
public string sServo { get; set; }
}
C#
public static List<Step> listStep { get; set; }
public static List<Servo> listServo { get; set; }
public MainWindow()
{
InitializeComponent();
}
public void loadList()
{
dgSFD.ItemsSource = listStep;
cbServo.ItemsSource = listServo;
}
public void testfill()
{
Servo newServo = new Servo();
newServo.sFunctionName = "Forward";
newServo.sServo = "Left";
listServo.Add(newServo);
Step newStep = new Step();
newStep.serServo = newServo;
newStep.sID = "1";
listStep.Add(newStep);
}

Bind the SelectedItemBinding property to the serServo property of the Step object:
<DataGridComboBoxColumn x:Name="cbServo" Header="Servo" SelectedItemBinding="{Binding serServo, Mode=TwoWay}" DisplayMemberPath="sFunction"/>

Related

Displaying Empty Combobox content in DataGridView?

I created a User Control page (SubPODetailView.xaml) that contains some entities "that entities defined from SubPO.cs" and a datagridview "that gridview is a separated class called SubInvoice.cs", and everything went good but in the datagridview, one of entities is a combobox display, the problem here is the collection list of this combobox is not displayed when running a program.
SubPO.cs
public class SubInvoice
{
public int Id { get; set; }
[Required]
public string InvoiceName { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
public List<string> Status{ get; set; }
public int SubPOId { get; set; }
public SubPO SubPO { get; set; }
}
Configuration.cs (In Migration Folder)
context.SubInvoices.AddOrUpdate(i => i.InvoiceName,
new SubInvoice
{
InvoiceName = "Invoice1",
Date = new DateTime(2020, 5, 26),
Amount = 1200,
SubPOId=context.SubPOs.First().Id,
Status = new List<string>() { "Open", "Closed", "Pending" }
});
SubPODetailViewModel.cs
<DockPanel Grid.Row="12" Margin="10">
<StackPanel DockPanel.Dock="Right" Width="86">
<Button Content="Add" Margin="10"
Command="{Binding AddInvoiceCommand}"/>
<Button Content="Remove" Margin="10"
Command="{Binding RemoveInvoiceCommand}"/>
</StackPanel>
<DataGrid ItemsSource="{Binding Invoices}"
SelectedItem="{Binding SelectedInvoice,Mode=TwoWay}"
AutoGenerateColumns="False" RowHeaderWidth="0" >
<DataGrid.Columns>
<DataGridTextColumn Header="Invoices" Width="*"
Binding="{Binding InvoiceName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTemplateColumn Header="Status" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding Date,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Amount" Width="*" Binding="{Binding Amount,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTemplateColumn Header="Status" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Status,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
SubPODetailViewModel.cs(Model)
namespace SubBV.UI.ViewModel
{
public class SubPODetailViewModel : DetailViewModelBase, ISubPODetailViewModel
{
private ISubPORepository _subPORepository;
private IMessageDialogService _messageDialogService;
private SubPOWrapper _subPO;
private SubInvoiceWrapper _selectedInvoice;
public SubPODetailViewModel(IEventAggregator eventAggregator,
IMessageDialogService messageDialogService,
ISubPORepository subPORepository) : base(eventAggregator)
{
_subPORepository = subPORepository;
_messageDialogService = messageDialogService;
AddInvoiceCommand = new DelegateCommand(OnAddInvoiceExecute);
RemoveInvoiceCommand = new DelegateCommand(OnRemoveInvoiceExecute, OnRemoveInvoiceCanExecute);
Invoices = new ObservableCollection<SubInvoiceWrapper>();
}
public SubPOWrapper SubPO
{
get { return _subPO; }
private set
{
_subPO = value;
OnPropertyChanged();
}
}
public SubInvoiceWrapper SelectedInvoice
{
get { return _selectedInvoice; }
set
{
_selectedInvoice = value;
OnPropertyChanged();
((DelegateCommand)RemoveInvoiceCommand).RaiseCanExecuteChanged();
}
}
public ICommand AddInvoiceCommand { get; }
public ICommand RemoveInvoiceCommand { get; }
public ObservableCollection<SubInvoiceWrapper> Invoices { get; }
public override async Task LoadAsync(int? subPOId)
{
var subPO = subPOId.HasValue
? await _subPORepository.GetByIdAsync(subPOId.Value)
: CreateNewSubPO();
InitializeSubInvoice(subPO.Invoices);
}
private void InitializeSubInvoice(ICollection<SubInvoice> invoices)
{
foreach (var wrapper in Invoices)
{
wrapper.PropertyChanged -= SubInvoiceWrapper_PropertyChanged;
}
Invoices.Clear();
foreach (var subInvoice in invoices)
{
var wrapper = new SubInvoiceWrapper(subInvoice);
Invoices.Add(wrapper);
wrapper.PropertyChanged += SubInvoiceWrapper_PropertyChanged;
}
}
private void SubInvoiceWrapper_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!HasChanges)
{
HasChanges = _subPORepository.HasChanges();
}
if (e.PropertyName == nameof(SubInvoiceWrapper.HasErrors))
{
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
}
private void InitializeSubPO(SubPO subPO)
{
SubPO = new SubPOWrapper(subPO);
SubPO.PropertyChanged += (s, e) =>
{
if (!HasChanges)
{
HasChanges = _subPORepository.HasChanges();
}
if (e.PropertyName == nameof(SubPO.HasErrors))
{
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
};
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
if (SubPO.Id == 0)
{
// Little trick to trigger the validation
SubPO.Title = "";
}
}
}
SubInvoiceWrapper.cs (Wraps what is contained in the DataGridView)
public class SubInvoiceWrapper:ModelWrapper<SubInvoice>
{
public SubInvoiceWrapper(SubInvoice model) : base(model)
{
}
public string InvoiceName
{
get { return GetValue<string>(); }
set { SetValue(value); }
}
public DateTime Date
{
get { return GetValue<DateTime>(); }
set { SetValue(value); }
}
public decimal Amount
{
get { return GetValue<decimal>(); }
set { SetValue(value); }
}
public List<string> Status
{
get { return GetValue<List<string>>(); }
set { SetValue(value); }
}
}
ViewModelBase.cs (contains the propertychanged)
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Datagrid image TemplateColumn source binding wpf

i'm using a Datagrid to display users information, every thing is working fine for text columns ,except the column I want to display user image,
here is the Datagrid in xaml
<DataGrid Name="UserListDataGrid" Margin="10,50,10,10"
AutoGenerateColumns="False"
EnableRowVirtualization="False"
ItemsSource="{Binding convUsrList}"
CellStyle="{StaticResource Body_Content_DataGrid_Centering}"
RowDetailsVisibilityMode="VisibleWhenSelected"
CanUserSortColumns="False"
CanUserAddRows="False"
CanUserResizeRows="False"
CanUserReorderColumns="False"
IsReadOnly="True"
Width="900"
Opacity="0"
Foreground="Black"
GridLinesVisibility="None"
HeadersVisibility="All"
HorizontalContentAlignment="Center"
Background="Gray"
BorderBrush="Transparent"
ScrollViewer.HorizontalScrollBarVisibility="Auto" >
<DataGrid.Columns>
<DataGridTemplateColumn >
<DataGridTemplateColumn.CellTemplate >
<DataTemplate>
<Image Source="{Binding PhotoSource}" Width="60" Height="60" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext[10],RelativeSource={RelativeSource AncestorType=DataGrid}}" Width="60"/>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding FirstName}" >
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext[4],RelativeSource={RelativeSource AncestorType=DataGrid}}" Width="60"/>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>....
I get the user photo from my DB in base64 string format, convert to BitmapImage using this function
public static BitmapImage getImage(string image)
{
byte[] b = Convert.FromBase64String(image);
MemoryStream mst = new MemoryStream(b, 0, b.Length);
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.StreamSource = mst;
bmp.EndInit();
return bmp;
}
and finally create a list of user (which is a class presenting user information such as firstname (string) //lastname(string)// ...//PhotoSource (BitmapImage) ) and feed it to data grid as follows
UserListDataGrid.ItemsSource = convUsrList;
as I said everything is showing on the Datagrid except user image would you please help me ?
this is the user class:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool IsActive { get; set; }
public int UserTypeId { get; set; }
public int ShopId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string CellNumber { get; set; }
public string Address { get; set; }
public string Email { get; set; }
public bool Gender { get; set; }
public string Photo { get; set; }
public string SecurityAnswer { get; set; }
public int SecurityQuestionId { get; set; }
public long LastSecurityCheck { get; set; }
public DateTime? DeletedAt { get; set; }
public DateTime? ExpireDate { get; set; }
}
public class UserDatagrid:User
{
public BitmapImage PhotoSource;
}
and here is how convUsrList is declared:
public void loadUserTable()
{
UserManagement um = new UserManagement(db);
List<User> userlist = um.getUserlist(um.GetUsers());
if (db.IsRTL)
UserListDataGrid.FlowDirection = FlowDirection.RightToLeft;
else
UserListDataGrid.FlowDirection = FlowDirection.LeftToRight;
string s = "";
dataBase.AppNotifyDic.TryGetValue("userTbl", out s);
string[] contbl = s.Split('-');
UserListDataGrid.DataContext = new List<string>() { contbl[0], contbl[1], contbl[2], contbl[3], contbl[4], contbl[5], contbl[6], contbl[7], contbl[8], contbl[9], contbl[10], contbl[11], contbl[12], contbl[13], contbl[14], contbl[15], contbl[16] };
List<UserDatagrid> convUsrList = new List<UserDatagrid>();
for (int i=0;i<userlist.Count;i++)
{
convUsrList.Add(tools.convertUserForDataGrid(userlist[i]));
}
UserListDataGrid.ItemsSource = convUsrList;
}
and the convertUserForDataGrid is as follow:
public static UserDatagrid convertUserForDataGrid(User origUser)
{
UserDatagrid convUser = new UserDatagrid();
convUser.Id = origUser.Id;
convUser.UserName = origUser.UserName;
convUser.Password = origUser.Password;
convUser.IsActive = origUser.IsActive;
convUser.UserTypeId = origUser.UserTypeId;
convUser.ShopId = origUser.ShopId;
convUser.FirstName = origUser.FirstName;
convUser.LastName = origUser.LastName;
convUser.PhoneNumber = origUser.PhoneNumber;
convUser.CellNumber = origUser.CellNumber;
convUser.Address = origUser.Address;
convUser.Email = origUser.Email;
convUser.Gender = origUser.Gender;
convUser.Photo = origUser.Photo;
convUser.SecurityAnswer = origUser.SecurityAnswer;
convUser.SecurityQuestionId = origUser.SecurityQuestionId;
convUser.LastSecurityCheck = origUser.LastSecurityCheck;
convUser.DeletedAt = origUser.DeletedAt;
convUser.ExpireDate = origUser.ExpireDate;
convUser.PhotoSource = (string.IsNullOrEmpty(origUser.Photo)) ? (convUser.Gender)? setImagesource("male.png"): setImagesource("Female.png") : getImage(origUser.Photo);
return convUser;
}
Because binding system uses Reflection to find the
Property in DataContext(i.e your VM)
Hope this will help.
credits:link1

How to: Fill a DataGrid.DataContext with information from txt files (C#, WPF)

How to: Fill a DataGrid.DataContext with information from txt files (C#, WPF)
I am trying to fill my DataGrid with information I get from some txt files. There is a folder called "CONTACTS" and there are some (5) files in. These files are configurated this way:
Content of John Doe.txt (without list symbols):
John Doe Corp.
Mr.
Doe
John
XAML:
<DataGrid AutoGenerateColumns="False" CanUserResizeRows="False" DockPanel.Dock="Bottom" ItemsSource="{Binding CollectionofDetails}" Name="dataGrid_Content">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CorporationName}" Header="Firma" />
<DataGridTextColumn Binding="{Binding Prefix}" Header="Anrede" />
<DataGridTextColumn Binding="{Binding FirstName}" Header="Nachname" />
<DataGridTextColumn Binding="{Binding LastName}" Header="Vorname" />
</DataGrid.Columns>
</DataGrid>
C#:
public class Details
{
public string CorporationName { get; set; }
public string Prefix { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public ObservableCollection<Details> _CollectionofDetails = new ObservableCollection<Details>();
public ObservableCollection<Details> CollectionofDetails
{
get { return _CollectionofDetails; }
set { _CollectionofDetails = value; }
}
public void SetItemsToDataContext()
{
foreach (string Datei in Directory.GetFiles(#"C:\Users\schwarz\Desktop\Cusposes_2014-05-20_0827\ISAPO\ISAPO Cusposes\Business Contacts\Contacts", "*.txt"))
{
StreamReader reader = new StreamReader(Datei);
int i = 0;
string line = reader.ReadToEnd().Replace("\n", "");
string[] t = line.Split('\r');
Details d = new Details();
d.CorporationName = t[i];
d.Prefix = t[i + 1];
d.FirstName = t[i + 2];
d.LastName = t[i + 3];
CollectionofDetails.Add(d);
reader.Close();
}
}
Unfortunately, I do have the following problems:
I do not know how to load THE CONTENT of each file (for all files).
I do not know how to fill my DataGrid with this Information.
--> Solution by Dhaval Patel (see below).
You can use the below mentioned code
Your Xaml Code looks like
<DataGrid AutoGenerateColumns="False" CanUserResizeRows="False" DockPanel.Dock="Bottom" ItemsSource="{Binding CollectionofDetails}" Name="dataGrid_Content">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CorporationName}" Header="Firma" />
<DataGridTextColumn Binding="{Binding Prefix}" Header="Anrede" />
<DataGridTextColumn Binding="{Binding FirstName}" Header="Nachname" />
<DataGridTextColumn Binding="{Binding LastName}" Header="Vorname" />
</DataGrid.Columns>
</DataGrid>
<Button Command="{Binding MyCommand}" Width="100" Margin="346,230,346,-189">RunMyCommand</Button>
Your ViewModel code look like
private ObservableCollection<Details> _CollectionofDetails=new ObservableCollection<Details>();
public ObservableCollection<Details> CollectionofDetails
{
get { return _CollectionofDetails; }
set { _CollectionofDetails = value; RaisePropertyChanged("CollectionofDetails"); }
}
private RelayCommand _MyCommand;
public RelayCommand MyCommand
{
get { return _MyCommand??(_MyCommand=new RelayCommand(Methodcall)); }
set { _MyCommand = value; }
}
void Methodcall()
{
foreach (string Datei in Directory.GetFiles(#"C:\textfile", "*.txt"))
{
StreamReader reader = new StreamReader(Datei);
int i=0;
string line = reader.ReadToEnd().Replace("\n","");
string[] t = line.Split('\r');
Details d = new Details();
d.CorporationName = t[i];
d.Prefix = t[i + 1];
d.FirstName = t[i + 2];
d.LastName = t[i + 3];
CollectionofDetails.Add(d);
reader.Close();
}
}
public class Details
{
public string CorporationName { get; set; }
public string Prefix { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
output should look like
This is a problem that you have to break into pieces. I would recommend that you define a class suitable for your data, like
public class Person {
public string FullName {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public string Title {get; set;}
}
and in your viewmodel class you have a variable PersonList of type List<Person>.
Then your have to write a method, something like
private void PopulatePersonData(){
// Here you put all the logic reading the .txt files creating a person for each file
// and adding the person to the PersonList.
}
which you then call in the constructor of the viewmodel.
Finally you bind the ItemsSource of your DataGrid to the PersonList. That should do it.
Maybe you should really read what is in your txt files.
By using for example a Reader :
private void SetItemsToDataContext()
{
foreach (String Datei in Directory.GetFiles(#"C:\Contacts", "*.txt"))
{
String[] linesRead = File.ReadAllLines(Datei);
if (linesRead.Length != 4)
{
continue;
}
Contact contactRead = new Contact();
contactRead.Company = linesRead[0];
contactRead.Gender = linesRead[1];
contactRead.Name = linesRead[2];
contactRead.FirstName = linesRead[3];
dataGrid_Content.Items.Add(contactRead);
}
}
public class Contact
{
public String Company { get; set; }
public String Gender { get; set; }
public String Name { get; set; }
public String FirstName { get; set; }
}

How do I bind the foreground color of a label in a datagrid?

I have a wpf datagrid with a label in a datatemplate. I want to bind the color of the text to a property and it's not working.
Here is the xaml.
<DataGrid x:Name="ResultsDataGrid" CanUserSortColumns="True" MouseDown="ResultsDataGrid_OnMouseDown" SelectionChanged="ResultsDataGrid_OnSelectionChanged"
IsReadOnly="False" AlternatingRowBackground="WhiteSmoke" CanUserAddRows="False" Margin="10" AutoGenerateColumns="False" VerticalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Test ID" Width="150" IsReadOnly="True" SortMemberPath="TestDate">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Foreground="{Binding PassedColor}" Content="{Binding TestID}"></Label>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And here is the property.
public Brush PassedColor
{
get
{
return new SolidBrush(Color.Red);
}
}
I can't seem to figure out what I'm doing wrong.
If it remove the binding and set the foreground to red it works. So it's definitely something with the binding.
Edit:
Here is the entire object
public class LabelInfo : INotifyPropertyChanged
{
private bool _isSelected;
private double? _karat;
private bool _passed;
public string TestID { get; set; }
public string Label1 { get; set; }
public string Label2 { get; set; }
public string Value1 { get; set; }
public string Value2 { get; set; }
public string HasPassed { get { return Passed ? "Yes" : "No"; } }
public Brush PassColor
{
get
{
return Brushes.Red;
}
}
public bool Passed
{
get { return _passed; }
set
{
_passed = value;
NotifyPropertyChanged();
}
}
public bool Final { get; set; }
public DateTime? TestDate { get; set; }
public Double RealTime { get; set; }
public string JTVID { get; set; }
public int AnalysisID { get; set; }
public List<ElementResults> Elements { get; set; }
public double Karat
{
get
{
if (_karat == null)
_karat = CalculateKarat();
return _karat.Value;
}
set { _karat = value; }
}
public bool PlatingAlert
{
get
{
return Karat < 7.5;
}
}
public bool IsSelected
{
get { return _isSelected; }
set { _isSelected = value; NotifyPropertyChanged(); }
}
public bool PotentialCoating { get; set; }
private double CalculateKarat()
{
if (Elements == null || Elements.Count == 0) return 0;
return Elements.Where(ex => ex.Name.ToLower().Trim() == "au").Select(ex => ex.Level).FirstOrDefault();
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You are using System.Drawing.Brush, which is from WinForms, but you should use the WPF System.Windows.Media.Brush. Change your code to this:
using System.Windows.Media;
public Brush PassedColor
{
get { return new SolidColorBrush(Colors.Red); }
}
or
public Brush PassedColor
{
get { return Brushes.Red; }
}
Perhaps remove System.Drawing from the referenced assemblies. Then you'll immediately find all those flaws.

Different values for different comboboxes in Datagrid

Bear with me, as I'm new to WPF.I have a datagrid which contains comboboxes.
<DataGrid x:Name="dgData" AutoGenerateColumns="False" Margin="131,93,13,101" Grid.Column="1" SelectionChanged="dgData_SelectionChanged">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Subject}" Header="Subject" Width="100"/>
<DataGridComboBoxColumn Header="Subject" x:Name="comboboxColumn1"/>
</DataGrid.Columns>
</DataGrid>
And I use the following code to load different itemsources depending on which combobox is clicked on by the user:
private void dgData_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (dgData.SelectedIndex == 0)
{
comboboxColumn1.ItemsSource = resolution;
} else if (dgData.SelectedIndex == 1)
{
comboboxColumn1.ItemsSource = codec;
}
}
How I bind dgData:
List<TicketInfo> ticketsList = new List<TicketInfo>
{
new TicketInfo{ Subject="File", Status="Open"},
new TicketInfo{ Subject="Resolution", Status="Assigned"},
};
dgData.ItemsSource = ticketsList;
public class TicketInfo
{
public string Subject { get; set; }
public string Status { get; set; }
}
Now I also need to retrieve the value of the combobox once the user chooses the value. Is there a more elegant way to load different items in different comboboxes and retrieving the value?
Thanks!
You don't show how you bind dgData.
public class TicketInfo
{
public string Subject { get; set; }
public string Status { get; set; }
public List<string> SubjectSubList { get; set; }
}
Bind the second column to SubjectSubList
When you build List ticketsList just put resolution in the first SubjectSubList and codec in the second.

Categories

Resources