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
}
}
}
}
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 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; }
}
I'm trying to make a chart (UWP-c#) which changes dynamically when It's source data changes.
For example:
xaml file:
<StackPanel>
<Button Name="scatterButton" Content="click" Click="ScatterButton_Click" />
<Charting:Chart x:Name="test_chart">
<Charting:ScatterSeries IndependentValuePath="Name" DependentValuePath="Amount" />
</Charting:Chart>
</StackPanel>
c#:
public class SmartPhone
{
public string Name { get; set; }
public int Amount { get; set; }
public int Other { get; set; }
}
public sealed partial class MainPage : Page
{
List<SmartPhone> lstSource = new List<SmartPhone>
{
new SmartPhone() { Name = "IPhone", Amount = 40, Other = 1 },
new SmartPhone() { Name = "Android", Amount = 30, Other = 1 },
new SmartPhone() { Name = "UWP", Amount = 25, Other = 2 }
};
public MainPage()
{
this.InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
LoadChartContent();
}
private void LoadChartContent()
{
(test_chart.Series[0] as ScatterSeries).ItemsSource = lstSource;
}
private void ScatterButton_Click(object sender, RoutedEventArgs e)
{
lstSource[0].Amount = 10;
}
}
The idea is when I click the button "Amount" value change and I want to see it change in the chart.
I tried many packages but this is the only one that really worked for me in UWP. tha NuGet is "WinRTXamlToolkit.Controls.DataVisualization".
Please try to focus on "ScatterSeries" since this is the one I need.
Thanks.
At first you should use ObservableCollection instead of List to automatically notify when items get added or removed.
To notify about changes you have to implement INotifyPropertyChanged and raise PropertyChanged event.
xaml:
<Charting:Chart x:Name="test_chart">
<Charting:ScatterSeries ItemsSource="{x:Bind LstSource}" IndependentValuePath="Name" DependentValuePath="Amount" />
</Charting:Chart>
SmartPhone class example:
public class SmartPhone : INotifyPropertyChanged
{
private int _amount;
public string Name { get; set; }
public int Amount
{
get { return _amount; }
set
{
this._amount = value;
NotifyPropertyChanged();
}
}
public int Other { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainPage class:
public sealed partial class MainPage : Page
{
public ObservableCollection<SmartPhone> LstSource
{
get { return lstSource; }
}
private ObservableCollection<SmartPhone> lstSource = new ObservableCollection<SmartPhone>
{
new SmartPhone() {Name = "IPhone", Amount = 10, Other = 1},
new SmartPhone() {Name = "Android", Amount = 30, Other = 1},
new SmartPhone() {Name = "UWP", Amount = 25, Other = 2}
};
public MainPage()
{
this.InitializeComponent();
//LoadChartContent();
}
private void ScatterButton_Click(object sender, RoutedEventArgs e)
{
lstSource[0].Amount = 30;
//lstSource.Add(new SmartPhone{Amount = 10, Name = "asd", Other = 2});
}
}
I hope it's all you need.
I have two bound textboxes in my View.
<TextBox Text="{Binding BookingWizard.CustomerView.Email,Mode=TwoWay}" />
<TextBox Text="{Binding BookingWizard.CustomerView.ContactNo,Mode=TwoWay}" />
I can populate these fields when another textbox has lost its focus. the code behind for that bit is:
private void txtFirstName_LostFocus(object sender, RoutedEventArgs e)
{
LookUpEmailAndContactNo();
}
private void LookUpEmailAndContactNo()
{
var vm = this.DataContext as ApplicationViewModel;
var customer = vm.BookingWizard.LookUpEmailAndContactNo();
//etc
vm.BookingWizard.CustomerView.Email = customer.Email;
}
public Customer LookUpEmailAndContactNo()
{
var res= InformedWorkerBusinessService.Customer.GetEmailAndContactNo(CustomerView.FName, CustomerView.SName);
if (res!=null)
{
CustomerView.Email = res.Email;
CustomerView.ContactNo = res.ContactNo;
}
return CustomerView;
}
This is the screenshot of my data context when i set a break-point in the LookUpEmailAndContactNo event:
As you can see the data context does have these values but I cannot see what is wrong with my UI binding?
ADDITIONAL:
I set my view model at the App entry point:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
ApplicationView app = new ApplicationView();
ApplicationViewModel context = new ApplicationViewModel();
context.ActiveRecord = new ActiveRecord();
context.CustomerSearch = new CustomerSearch();
context.BookingWizard = new BookingWizard();
context.BookingWizard.CustomerView = new InformedWorkerModel.Customer();
context.BookingWizard.JobView = new InformedWorkerModel.Job();
app.DataContext = context;
app.Show();
}
}
This is inside my BookingWizard class:
public class BookingWizard : ViewModelBase, IDataErrorInfo
{
Customer _Customer;
public bool IsExistingCustomer { get; set; }
public IEnumerable<string> FNames
{
get
{
if (CustomerView.SName == null)
{
CustomerView.SName = string.Empty;
}
return InformedWorkerBusinessService.Customer.GetFirstNames(CustomerView.SName);
}
}
public Customer LookUpEmailAndContactNo()
{
var res= InformedWorkerBusinessService.Customer.GetEmailAndContactNo(CustomerView.FName, CustomerView.SName);
if (res!=null)
{
CustomerView.Email = res.Email;
CustomerView.ContactNo = res.ContactNo;
}
return CustomerView;
}
public Customer CustomerView
{
get { return _Customer; }
set
{
_Customer = value; RaisePropertyChanged("CustomerView");
}
}
}
and in my Customer Class:
[Table("Customer")]
public class Customer
{
[AutoIncrement]
[PrimaryKey]
public int CustomerId { get; set; }
public string SName { get; set; }
public string FName { get; set; }
public string ContactNo { get; set; }
public string Email { get ; set; }
}
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; }
}