I have a property Grid as follows:
I want to copy the complete content of the property grid to a data grid view(dataGeriView1) when submit button is clicked.
How to do this?
Please help.
private void Submit_Click(object sender, EventArgs e)
{
//propertyGrid1.SelectedObject = this;
dataGridView1.Columns.Add("Property", "Property");
dataGridView1.Columns.Add("Value", "Value");
GridItem gi = propertyGrid1.SelectedGridItem;
while (gi.Parent != null)
gi = gi.Parent;
foreach (GridItem item in gi.GridItems)
ParseGridItems(item); //recursive
dataGridView1.Sort(dataGridView1.Columns["Property"], ListSortDirection.Ascending);
}
private void ParseGridItems(GridItem gi)
{
if (gi.GridItemType == GridItemType.Category)
foreach (GridItem item in gi.GridItems)
ParseGridItems(item);
dataGridView1.Rows.Add(gi.Label, gi.Value);
}
Adapted from https://stackoverflow.com/a/12109186/1163434
Below is a sample snippet i have created to solve the above issue. Create a DataGridview by adding Columns Name,Age,Email,Phone.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Student std = new Student {Name = "Vimal" , Phone = "PhoneValue", Email="mymail",Age=24};
propertyGrid1.SelectedObject= std;
}
private void button1_Click(object sender, EventArgs e)
{
int index = dataGridView1.Rows.Count - 1;
Student std = (Student)propertyGrid1.SelectedObject;
dataGridView1.Rows[index].Cells["Name"].Value = std.Name;
dataGridView1.Rows[index].Cells["Age"].Value = std.Age;
dataGridView1.Rows[index].Cells["Email"].Value = std.Email;
dataGridView1.Rows[index].Cells["Phone"].Value = std.Phone;
}
}
public class Student
{
public int Age { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
}
Related
The ListBox's DataSource is bound to Detail.Tags.
When I select the first row, ListBox populates as expected.
When I select the second row, the expected (and desired) result is that the ListBox simply displays nothing, because ItemB's Detail property is purposely null for demonstration purposes, so ItemB's Detail.Tags doesn't exist.
Actual result is that program crashes to desktop with System.ArgumentException: 'Complex DataBinding accepts as a data source either an IList or an IListSource.'
Minimal reproducible example:
public partial class Form1 : Form
{
private readonly IList<Item> _items;
private BindingSource _bs = new BindingSource(){ DataSource = typeof(Item) };
public Form1()
{
InitializeComponent();
_items = GenerateSampleItems();
}
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.DataSource = _items;
listBox1.DataBindings.Add(new Binding("DataSource", _bs, "Detail.Tags", false, DataSourceUpdateMode.Never));
}
private void DataGridView1_SelectionChanged(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count == 1)
{
_bs.DataSource = dataGridView1.SelectedRows[0].DataBoundItem;
}
else
{
_bs.DataSource = typeof(Item);
}
}
private IList<Item> GenerateSampleItems()
{
return new List<Item>()
{
new Item()
{
Name = "ItemA"
,Detail = new Detail()
{
Expiration = new DateTime(2024,1,1)
,Tags = new BindingList<Tag>(new List<Tag>()
{
new Tag()
{
TagName = "FirstT"
,TagValue = "FirstV"
}
,new Tag()
{
TagName = "SecondT"
,TagValue = "SecondV"
}
})
}
}
,new Item()
{
Name = "ItemB"
// Detail purposely omitted
}
,new Item()
{
Name = "ItemC"
// Detail purposely omitted
}
};
}
}
class Item
{
public string Name { get; set; }
public Detail Detail { get; set; }
}
public class Detail
{
public DateTime Expiration { get; set; }
public BindingList<Tag> Tags { get; set; }
}
public class Tag
{
public string TagName { get; set; }
public string TagValue { get; set; }
}
You can solve this problem by Creating a BindingSource for each model:
Main BindingSource where its DataSource property is set to a list of Item. This one is the DataGridView.DataSource.
Second BindingSource to navigate the Detail data members of the main BindingSource.
Third one to navigate and display the Tags data members of the detail's BindingSource. This one is the ListBox.DataSource.
public partial class Form1 : Form
{
private IList<Item> _items;
private BindingSource _bsItems, _bsDetail, _bsTags;
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_items = GenerateSampleItems();
_bsItems = new BindingSource(_items, null);
_bsDetail = new BindingSource(_bsItems, "Detail");
_bsTags = new BindingSource(_bsDetail, "Tags");
dataGridView1.DataSource = _bsItems;
listBox1.DataSource = _bsTags;
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
_bsItems.Dispose();
_bsDetail.Dispose();
_bsTags.Dispose();
}
private IList<Item> GenerateSampleItems()
{
return new List<Item>()
{
new Item()
{
Name = "ItemA",
Detail = new Detail
{
Expiration = new DateTime(2024,1,1),
Tags = new BindingList<Tag>(new List<Tag>()
{
new Tag
{
TagName = "FirstT",
TagValue = "FirstV"
},
new Tag
{
TagName = "SecondT",
TagValue = "SecondV"
}
})
}
},
new Item()
{
Name = "ItemB"
// Detail purposely omitted
},
new Item()
{
Name = "ItemC"
// Detail purposely omitted
}
};
}
}
// Elsewhere within the project's namespace
public class Item
{
public string Name { get; set; }
public Detail Detail { get; set; }
// Optional: Change, remove as needed...
public override string ToString()
{
return $"Name: {Name} - Detail: {Detail}";
}
}
public class Detail
{
public DateTime Expiration { get; set; }
public BindingList<Tag> Tags { get; set; }
// Optional: Change, remove as needed...
public override string ToString()
{
var tags = $"[{string.Join(", ", Tags)}]";
return $"Expiration: {Expiration} - Tags: {tags}";
}
}
public class Tag
{
public string TagName { get; set; }
public string TagValue { get; set; }
// Optional: Change, remove as needed...
public override string ToString()
{
return $"{TagName}: {TagValue}";
}
}
That's all. No need to add DataBindings nor to handle the grid's SelectionChanged event as shown in your code snippet.
On the other hand, if you need to display the selected Item.Detail.Tags, then you need to flatten them in a list whenever the grid's selection changes and bind the result to the ListBox.
// +
using System.Linq;
public partial class Form1 : Form
{
private BindingSource _bsItems;
public Form1() => InitializeComponent();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_bsItems = new BindingSource(GenerateSampleItems(), null);
dataGridView1.DataSource = _bsItems;
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
_bsItems.Dispose();
}
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
listBox1.DataSource = dataGridView1.SelectedCells
.Cast<DataGridViewCell>()
.Select(cell => cell.OwningRow).Distinct()
.Where(row => (row.DataBoundItem as Item)?.Detail != null)
.SelectMany(row => (row.DataBoundItem as Item).Detail.Tags)
.ToList();
}
}
I trying add a delete row but when im editing a some row(delete the word or symbol), the row will remove. i want delete row. if the row in editing mode and if i press a delete then the row will be remove.
public partial class MainWindow : Window
{
ObservableCollection<Employee> johnSmith = new ObservableCollection<Employee>();[enter image description here][1]
public MainWindow()
{
InitializeComponent();
dataGrid.ItemsSource = johnSmith;
}
public class Employee
{
public string employeeID { get; set; }
public string employeeName { get; set; }
public string employeeAddress { get; set; }
public string employeeCity { get; set; }
public string employeeState { get; set; }
}
private void AddNewEmployeeClicked(object sender, RoutedEventArgs e)
{
johnSmith.Add(new Employee()
{
employeeID = IDTB.Text,
employeeAddress = AdressTB.Text,
employeeCity = CityTB.Text,
employeeName = NameTB.Text,
employeeState = StateTB.Text
});
}
private void datagrid_loadingrow(object sender, DataGridRowEventArgs e)
{
e.Row.Header = (e.Row.GetIndex() + 1).ToString();
}
private void deletegrid_keydown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
if (dataGrid.SelectedItem != null)
{
johnSmith.Remove(item);
}
}
}
If you want to prevent row deletion, you can use CanUserDeleteRows="False".
If you want to perform some custom handling related to keyboard input, you can use PreviewKeyDown event and subscribe your handling method.
PreviewKeyDown="DataGrid_PreviewKeyDown"
private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete || e.Key == Key.Back)
{
e.Handled = true;
//put some additional handling if you need
}
}
I created a custom picker with the help of Lucas Zhang which you can check in the link
xamarin custom multiple picker
Now I have another question with this problem. When user select a group or groups, I need access to these selected parameters.
public class Grup
{
public int ID { get; set; }
public string GroupName { get; set; }
public Nullable<int> SubsID { get; set; }
}
This is the model I use. Picker reads Groupnames through ViewModel which is shown below.
public class NewUserViewModel
{
public List<Grup> GroupList { get; set; }
public List<Grup> SelectedGroup { get; set; }
}
And I want save these parameters which came from every pickers in the view to here and furthermore I will send them to database through API.Question is how can I access these IDs when user select them and click save button.
An easy way to do this is to listen for picker's selection events and then get the result of each selection by iterating over the pickerStack
like this(base on Lucas Zhang's sample):
in PickerView :
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class PickerView : ContentView
{
public Group SelectGroup; // add the SelectGroup property which save the result after you select
public ObservableCollection<Group> pickerSource { get; set; }
public PickerView(ObservableCollection<Group> source) //here i change the source to your Group model
{
InitializeComponent();
picker.ItemsSource = source;
}
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
var stack = this.Parent as StackLayout;
stack.Children.Remove(this);
}
private void picker_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = sender as Picker;
SelectGroup = (Group)picker.SelectedItem;
}
}
in PickerView.xaml just add the SelectedIndexChanged event:
<Picker Grid.Column="0" x:Name="picker" Title="{Binding GroupName}" ItemDisplayBinding="{Binding ID}" TitleColor="Red" SelectedIndexChanged="picker_SelectedIndexChanged" />
in your page :
public partial class MutiPicker : ContentPage
{
public MutiPicker()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
var source = new ObservableCollection<Group>() { new Group() { ID=111,GroupName="AAA",SubsID=1}, new Group() { ID = 222, GroupName = "BBB", SubsID = 2 }, new Group() { ID = 333, GroupName = "CCC", SubsID = 3 } };
pickerStack.Children.Add(new PickerView(source));
}
//iterate over your pickerviews
private void Update_Clicker(object sender, EventArgs e)
{
foreach (var pickerview in pickerStack.Children)
{
if (pickerview is PickerView && ((PickerView)pickerview).SelectGroup != null)
{
var selectgroup = ((PickerView)pickerview).SelectGroup;//here you will get your select group,then you could get its ID ,GroupName or SubsID
}
}
}
}
I have asked This Question a few days ago and the answer has solved my problem perfectly. I have another related question to my own post.
The CheckState of second (Doctors') Checkedlistbox items will be saved when I change data source by checking the first (Specialty) Checkedlistbox items. Here's my code:
CheckedListBoxItem class:
/// <summary>
/// A class to svae status of checkboxes when datasource changes.
/// </summary>
/// <typeparam name="T"></typeparam>
public class CheckedListBoxItem<T>
{
public CheckedListBoxItem(T item)
{
DataBoundItem = item;
}
public T DataBoundItem { get; set; }
public CheckState CheckState { get; set; }
public override string ToString() { return DataBoundItem.ToString(); }
}
SpecialityCheckList and DoctorCheckList related to first and second Checkedlistbox controls:
public class SpecialityCheckList
{
public int SpecialtyID { get; set; }
public string Title { get; set; }
public override string ToString() { return Title; }
}
public class DoctorCheckList
{
public int DoctorID { get; set; }
public string Name { get; set; }
public int? SpecialityId { get; set; }
public override string ToString() { return Name; }
}
Form codes:
BindingList<CheckedListBoxItem<DoctorCheckList>> doctors = new BindingList<CheckedListBoxItem<DoctorCheckList>>();
private void ReportVisitSocialInsurance_Load(object sender, EventArgs e)
{
SpecialtyTypeCheckbox.DataSource = new BindingList<SpecialityCheckList>(_Specialty.SelectTbl_SpecialityCheckListBox());
DoctorsIDCheckedlistbox.DataSource = doctors;
doctors.ListChanged += Doctors_ListChanged;
}
private void Doctors_ListChanged(object sender, ListChangedEventArgs e)
{
for (var i = 0; i < DoctorsIDCheckedlistbox.Items.Count; i++)
{
DoctorsIDCheckedlistbox.SetItemCheckState(i, ((CheckedListBoxItem<DoctorCheckList>)DoctorsIDCheckedlistbox.Items[i]).CheckState);
}
}
private void SpecialtyTypeID_ItemCheck(object sender, ItemCheckEventArgs e)
{
var item = (SpecialityCheckList)SpecialtyTypeCheckbox.Items[e.Index];
if (e.NewValue == CheckState.Checked)
{
_Doctors.SelectTbl_DoctorsCheckListBox(item.SpecialtyID)
.Select(s => new CheckedListBoxItem<DoctorCheckList>(s)).ToList()
.ForEach(f => doctors.Add(f));
} else {
doctors
.Where(w => w.DataBoundItem.SpecialityId == item.SpecialtyID)
.ToList()
.ForEach(f => doctors.Remove(f));
}
}
private void DoctorsID_ItemCheck(object sender, ItemCheckEventArgs e)
{
((CheckedListBoxItem<DoctorCheckList>)DoctorsIDCheckedlistbox.Items[e.Index]).CheckState = e.NewValue;
}
How ever if I search through Doctors' Checkedlistbox items and check a item, when I clear search textbox to see all doctors' name the checked item is different from what I have checked while searching. This is because it uses index I think. Here is my code when searching:
private void DoctorsNameTextbox_TextChanged(object sender, EventArgs e)
{
BindingList<CheckedListBoxItem<DoctorCheckList>> doctorsSerach =
ObjectCloning.CloneJson<BindingList<CheckedListBoxItem<DoctorCheckList>>>(doctors);
doctorsSerach
.Where(w => !w.DataBoundItem.Name.Contains(DoctorsNameTextbox.Text))
.ToList()
.ForEach(f => doctorsSerach.Remove(f));
DoctorsIDCheckedlistbox.DataSource = doctorsSerach;
}
The problem is that for example if I search for name Ali, it shows me 3 items. When I check item number 2nd and clear search textbox, item at index 1 (zero-based) has been checked.
When handling TexctChanged event of the searchTextBox, you can check if the text is empty, set the data source to list of doctors, otherwise set the data source to a list of filtered doctors. In both case, after setting the data source, sync the check marks with data source:
private void searchTextBox_TextChanged(object sender, EventArgs e)
{
if(string.IsNullOrEmpty(searchTextBox.Text))
{
doctorsCheckedListBox.DataSource = doctors;
}
else
{
var filteredDoctors =
new BindingList<CheckedListBoxItem<Doctor>>
(doctors.Where(x => x.DataBoundItem.Name.StartsWith(searchTextBox.Text))
.ToList());
doctorsCheckedListBox.DataSource = filteredDoctors;
}
for (var i = 0; i < doctorsCheckedListBox.Items.Count; i++)
{
doctorsCheckedListBox.SetItemCheckState(i,
((CheckedListBoxItem<Doctor>)doctorsCheckedListBox.Items[i]).CheckState);
}
}
I want to remove selected rows from a datagridview and my grid was bound to a List<T>. I wrote the code which throwing error called
Rows cannot be programmatically removed unless the DataGridView is data-bound to an IBindingList that supports change notification and allows deletion
This is my full sample code which I have tried but it is not working.
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
}
List<person> _person = null;
private void Form3_Load(object sender, EventArgs e)
{
_person =new List<person>();
_person.Add(new person { ID = 1, Name = "Tridip" });
_person.Add(new person { ID = 2, Name = "Sujit" });
_person.Add(new person { ID = 3, Name = "Arijit" });
dgLogList.DataSource = _person;
}
private void button1_Click(object sender, EventArgs e)
{
foreach (DataGridViewRow dvr in dgLogList.SelectedRows)
{
if (dvr != null)
{
_person.RemoveAt(dvr.Index);
dgLogList.DataSource = _person.ToList();
}
}
}
}
public class person
{
public int ID { get; set; }
public string Name { get; set; }
}
So please tell me how could I remove selected rows from a datagridview when it is bound to a List<T>.
Instead of binding the DataGridViewRow to your List, bind it to a BindingSource instance. The BindingSource acts as a intermediary between your Grid and List. Also make sure to call the Refresh() method following your row(s) deletion. Here is your code including the updates.
public partial class Form1 : Form
{
private BindingSource _source = new BindingSource();
List<person> _person = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_person = new List<person>();
_person.Add(new person {ID = 1, Name = "Tridip"});
_person.Add(new person {ID = 2, Name = "Sujit"});
_person.Add(new person {ID = 3, Name = "Arijit"});
_source.DataSource = _person;
dgLogList.DataSource = _source;
}
private void Button1_Click(object sender, EventArgs e)
{
foreach (DataGridViewRow dvr in dgLogList.SelectedRows)
{
if (dvr != null)
{
dgLogList.Rows.Remove(dvr);
dgLogList.Refresh();
}
}
}
}
public class person
{
public int ID { get; set; }
public string Name { get; set; }
}