I have a comboBox filed from a list thus:
locationCcomboBox.DataSource = ReadExcelFile(ExcelFilePath, "some properties"); \\ returns a list of class property.
locationCcomboBox.DisplayMember = "Location";
the Class is a simple class:
public string chain { get; set; }
public string location { get; set; }
public string postcode { get; set; }
public string phone { get; set; }
What I can't get into my head is how when the user selects an option from the combobox is how I select the phone,chain etc to write the correct value out to a text box for each!
BrainGoneSouth!
Handle the SelectedIndexChanged event of your locationCcomboBox an then get your class instance by the SelectedItem property:
//At form load or constructor:
locationCcomboBox.SelectedIndexChanged += locationCcomboBox_SelectedIndexChanged;
private void locationCcomboBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (locationCcomboBox.SelectedIndex > -1)
{
Class myClass = locationCcombo.SelectedItem as Class;
if (myClass != null)
{
//access the members of myClass here
}
}
}
Related
I'm having trouble copying data from one ObservableCollection to another. I have an api call GetItemsAsync from http that puts the response into a model called ShipList.cs. Inside of ShipList.cs there is ShipCatalog[] ships. I have created a second model called HangarList.cs with HangarCatalog[] hangars. I have a page that displays the master list of ships (ShipsList) I want the user to select the ship (ShipList.name is bound to this particular ListVIew. I tried to use .Where() to filter ShipList to only the match to the selected item and copy that data to HangarCatalog. I'm getting Cannot convert GallogForms.Api.ShipCatalog to GallogForms.Api.HangarCatalog using the following code.
ViewModel
private ShipCatalog _selectedShip;
public ShipCatalog SelectedShip
{
get {return _selectedShip; }
set
{if (_selectedShip != value)
_selectedShip = value;
id = _selectedShip.id;
CopyShipData();
private async void CopyShipData()
{
var _container = Items.Where(s =>
s.name.FirstOrDefault().ToString() == id.ToString()).ToList();
foreach (var item in _container.Where(s =>
s.name.FirstOrDefault().ToString() == id.ToString()).ToList())
// var items = await _gallogClient.GetItemsAsync<ShipList>();
// foreach (var item in items.ships.Where(s =>
// s.name.FirstOrDefault().ToString() == id.ToString()).ToList())
{
Hangars.Clear();
Hangars.Add(item);
}
}
I haven't found any answer yet, and I've read plenty, that can address my situation. myShipsList is bound to a new model I've created in the API that perfectly mirrors ShipCatalog[].
I've also keep running across answers that suggest ListViewItem.Item or in my case SuggestedShipView.Items. .Items is not an option for my ListViews in the view model.
AddShipPage.xaml
<StackLayout Orientation="Vertical">
<SearchBar x:Name="HangarListView" Text="Add To Your Fleet!"
TextChanged="HangarList_TextChanged"
BackgroundColor="Azure"
/>
<Grid>
<ListView x:Name="SuggestedShipView"
ItemsSource="{Binding Items}"
SelectedItem="{Binding selectedShip}"
BackgroundColor="Silver">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
.....................
ShipList.cs (API Query)
[ApiPath("ships")]
public class ShipList : ApiQueryable
{
public ShipCatalog[] ships { get; set; }
}
public class ShipCatalog : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public int id { get; set; }
public string name { get; set; }
public string uri { get; set; }
public int rsi_id { get; set; }
public string img { get; set; }
public string mfr { get; set; }
public string flyable { get; set; }
public string scu { get; set; }
public string value { get; set; }
public string bgcolor { get; set; }
public string color { get; set; }
public string role { get; set; }
public bool _isVisible { get; set; }
public bool IsVisible
{
get { return _isVisible; }
set
{
if (_isVisible != value)
{
_isVisible = value;
OnPropertyChanged();
}
}
}
}
}
HangarList mirrors ShipList perfectly with the exception it's named HangarList, public HangarCatalog[] hangars
And Finally, the query which populates ShipCatalog[]
AddShipViewModel
Items.Clear();
var items = await _gallogClient.GetItemsAsync<ShipList>();
foreach (var item in items.ships.ToList())
{
Items.Add(item);
}
No error messages per se, but I have not been able to structure a method to complete this task. If you would like to see the entire project to see more of what I have going on, http://github.com/dreamsforgotten/GallogMobile
when you tap or select an item in a ListView, the second parameter of the event handler will contain a reference to the item selected/tapped. You just need to cast it to the correct type. Then you can reference all of its properties as needed
private void SuggestedShipView_ItemTapped(object sender, ItemTappedEventArgs e)
{
// e.Item is the specific item selected
ShipCatalog ship = (ShipCatalog)e.Item;
// you can then use this ship object as the data source for your Hangar list/control,
// and/or add it to another List that is just the items the user has selected
}
I have a datagrid
<DataGrid Name="dtgFeatures" Height="100" Margin="10" ColumnWidth="*" CanUserAddRows="True" MouseLeftButtonUp="DtgFeatures_MouseLeftButtonUp"/>
which is binded to an observable collection
ObservableCollection<CfgPartPrograms> obcCfgPartPrograms = new ObservableCollection<CfgPartPrograms>();
with
[Serializable]
public class CfgPartPrograms
{
public CfgPartPrograms() { }
public string Group{ get; set;}
public string Description{ get; set;}
public string Filename{ get; set;}<------set with openfiledialog
public string Notes{ get; set;}
}
Now since I want to be able to insert the filename with an openfileDialog I have add this code:
private void DtgFeatures_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
int column = (sender as DataGrid).CurrentCell.Column.DisplayIndex;
if ( column == 2)
{
OpenFileDialog ofdPP = new OpenFileDialog();
if (ofdPP.ShowDialog() == true)
{
if (obcCfgPartPrograms.Count == 0)
obcCfgPartPrograms.Add(new CfgPartPrograms() { Filename = ofdPP.FileName });
else
obcCfgPartPrograms[selectedIndex].Filename = ofdPP.FileName;
dtgFeatures.ItemsSource = null;
dtgFeatures.ItemsSource = obcCfgPartPrograms;
}
}
the problem is that when I set the filename the observable collection has not been updated yet.
I'll explain that with images:
So I have added aaaa and bbb now I want to force the filename with the code above but when I do that the bind action has not been done yet on the observable collection so that aaaa and bbbb are not present.
In short how to tell the binded datagrid to update the binding??
Thanks in advance
Patrick
Your CfgPartPrograms class should implement the INotifyPropertyChanged interface and raise the PropertyChanged event whenever a data bound property is set to a new value: https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx
[Serializable]
public class CfgPartPrograms : System.ComponentModel.INotifyPropertyChanged
{
public CfgPartPrograms() { }
public string Group { get; set; }
public string Description { get; set; }
private string _fileName;
public string Filename
{
get { return _fileName; }
set { _fileName = value; NotifyPropertyChanged(); }
}
public string Notes { get; set; }
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
I found it here: I was missing the
dtgFeatures.CommitEdit(DataGridEditingUnit.Row, true);
command
I need some help with passing the ListView Tapped Id (which I get from a json).
I populate the listView with an API call to a server:
private async void searchButton_Click(object sender, RoutedEventArgs e)
{
var textFrom = odTextBox.Text;
var textTo = doTextBox.Text;
var searchResult = await PrevoziApi.SearchRidesAsync(textFrom, textTo, datePicker.Date.UtcDateTime);
var array = searchResult.CarshareList
.OrderBy(cs => cs.Time)
.Select(cs => cs.Contact + " " + cs.Time)
.ToArray();
listView.ItemsSource = array;
}
Now, when I click on an item of listView, I want to navigate to another page(CarShareDetailedPage) and make another call to the API, to get more detailed data about that item. So I need to pass the selectedItem id from one page to other. How do I do that ?
I'm navigating to another page like this:
private void listView_Tapped(object sender, TappedRoutedEventArgs e)
{
Frame.Navigate(typeof(CarShareDetailedPage), listView.SelectedIndex);
}
The OnNagiatedMethod on that page is:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var value = e.ToString();
carShareTextBox.Text = value;
}
And my json class is:
public class CarshareList
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("from_id")]
public string FromId { get; set; }
[JsonProperty("from_country")]
public string FromCountry { get; set; }
[JsonProperty("from_country_name")]
public string FromCountryName { get; set; }
[JsonProperty("to_id")]
public string ToId { get; set; }
[JsonProperty("to")]
public string To { get; set; }
[JsonProperty("to_country")]
public string ToCountry { get; set; }
[JsonProperty("to_country_name")]
public string ToCountryName { get; set; }
[JsonProperty("time")]
public string Time { get; set; }
[JsonProperty("date_iso8601")]
public DateTime DateIso8601 { get; set; }
[JsonProperty("added")]
public DateTime Added { get; set; }
[JsonProperty("price")]
public double? Price { get; set; }
[JsonProperty("num_people")]
public double NumPeople { get; set; }
[JsonProperty("author")]
public string Author { get; set; }
[JsonProperty("is_author")]
public string IsAuthor { get; set; }
[JsonProperty("comment")]
public string Comment { get; set; }
[JsonProperty("contact")]
public string Contact { get; set; }
[JsonProperty("date")]
public string Date { get; set; }
[JsonProperty("full")]
public string Full { get; set; }
[JsonProperty("insured")]
public string Insured { get; set; }
[JsonProperty("share_type")]
public string ShareType { get; set; }
[JsonProperty("confirmed_contact")]
public string ConfirmedContact { get; set; }
[JsonProperty("bookmark")]
public object Bookmark { get; set; }
[JsonProperty("from")]
public string From { get; set; }
}
public class CarshareResponse
{
[JsonProperty("search_type")]
public string SearchType { get; set; }
[JsonProperty("carshare_list")]
public IList<CarshareList> CarshareList { get; set; }
}
Let me say this is the first time ever I'm doing any work with Apis and json.
Thanks for your help!
EDIT: I added the code for the API below, so this now should be all the code I have.
public class PrevoziApi
{ public static async Task<CarshareResponse> SearchRidesAsync(
string fromCity,
string toCity,
DateTime date,
string type = "shares",
CancellationToken token = default(CancellationToken))
{
using (var client = new RestClient("https://prevoz.org/api/"))
{
var request = new RestRequest("search/" + type + "/", HttpMethod.Get);
request.AddQueryParameter("f", fromCity);
request.AddQueryParameter("fc", "SI");
request.AddQueryParameter("t", toCity);
request.AddQueryParameter("tc", "SI");
request.AddQueryParameter("d", date.ToString("yyyy-MM-dd"));
request.AddQueryParameter("exact", "true");
return
(await client.Execute<CarshareResponse>(request, token)).Data;
}
}
}
So with this, you are ordering by the time but displaying a string only that says "[Contact] [Time]". This in-and-of-itself does not hold any relation to the JSON that was returned from your search method. What you'll want to do is instead of making it an array, instead making a List<> object that can store some additional "background" data about that request to send off.
This will require a bit more effort though on your end. You will want to create a class
public class CarItemView {
public string DisplayText {get; set;}
public int ID {get; set;}
}
and fill it with whatever data you want to pass along. Then in your filtering you would do:
List<CarItemView> array = searchResult.CarshareList
.OrderBy(cs => cs.Time)
.Select(cs => new { DisplayText = cs.Contact + " " + cs.Time, ID = cs.Id}).ToList();
You will then, in your XAML, have to add a template to your listview for display. (Note, this is a real rough outline for a XAML Template)
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding DisplayText}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
NOW when you get your selected item changed event fired, you can handle it and get the ID.
private void listView_Tapped(object sender, TappedRoutedEventArgs e)
{
var obj = (CarItemView) listView.SelectedItem; // convert item to our new class
Frame.Navigate(typeof(CarShareDetailedPage), obj.Id.ToString()); // send ID as string
}
Then for the receiving page:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var value = e.ToString();
carShareTextBox.Text = value; // will show the ID number
var caritemret = /* write a new restful function to return based on ID */
}
UPDATE: This answer was updated from original to reflect the use of an array instead of a list<> object
I hope this helps!
This works:
private async void searchButton_Click(object sender, RoutedEventArgs e)
{
var textFrom = odTextBox.Text;
var textTo = doTextBox.Text;
var searchResult = await PrevoziApi.SearchRidesAsync(textFrom, textTo, datePicker.Date.UtcDateTime);
List<CarItemView> array = searchResult.CarshareList
.OrderBy(cs => cs.Time)
.Select(cs => new CarItemView { DisplayText = cs.Contact + " " + cs.Time, Id = cs.Id })
.ToList();
listView.ItemsSource = array;
}
private void listView_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
var obj = (CarItemView)listView.SelectedItem; // convert item to our new class
Frame.Navigate(typeof(CarShareDetailedPage), obj.Id); // send ID as string
}
And on navigated to method on the destination page:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var value = e.Parameter.ToString();
carShareTextBox.Text = value; // will show the ID number
/* write a new restful function to return based on ID */
}
Thanks #daniel, it was mostly as you suggested, with a few errors, but with the help of some guys at the c# chat channel I managed. Thanks to all.
This is my code:
But I will Add the values (see code) if someone clicked the Button "Einfügen".
But it doesn't work, It only change his values!
Thanks for all helpers!
private void Einfügen_Click(object sender, RoutedEventArgs e)
{
var itemsEnd = new List<Plan>();
itemsEnd.Add(new Plan(LinieZ, Convert.ToString(Kurs.SelectedItem), AbfZ, VonZ, NachZ, AnkZ, "---"));
Plan.ItemsSource = itemsEnd;
}
class Plan
{
public string Linie { get; set; }
public string Kurs { get; set; }
public string Abfahrt { get; set; }
public string Von { get; set; }
public string Nach { get; set; }
public string Ankunft { get; set; }
public string Pause { get; set; }
public Plan(string Linie, string Kurs, string Abfahrt, string Von, string Nach, string Ankunft, string Pause)
{
this.Linie = Linie;
this.Kurs = Kurs;
this.Abfahrt = Abfahrt;
this.Von = Von;
this.Nach = Nach;
this.Ankunft = Ankunft;
this.Pause = Pause;
}
}
The problem is that you are resetting the ItemsSource each time to a brand new List (of size 1). You are not appending to the List, but instead, you are creating a List that only has the new item, then setting that List to the DataGrid.
You can have a predefined list that you add to.
Something like:
private ObservableCollection<Plan> _items = new ObservableCollection<Plan>();
public Window()
{
InitializeComponent();
Plan.ItemsSource = _items;
}
private void Einfügen_Click(object sender, RoutedEventArgs e)
{
_items.Add(new Plan(LinieZ, Convert.ToString(Kurs.SelectedItem), AbfZ, VonZ, NachZ, AnkZ, "---"));
}
Though, I would suggest not going this route. Look into MVVM, DataBinding, and Commands. Ideally, you would want to create a ViewModel that contains an ObservableCollection that is bound to the DataGrid. Inside that ViewModel will be a command that will add items to that ObservableCollection.
I have a separate project for Data layer and there are two basic classes there:
[Serializable]
public class NesInfo
{
public string FileName { get; private set; }
public string Directory { get; private set; }
public MapperInfo MapperInfo { get; set; }
}
and
[Serializable]
public class MapperInfo
{
public string Number { get; set; }
public string Prop1{ get; set; }
public string Prop2 { get; set; }
}
Now I want my DataGridView to Display columns like this:
[FileName][Directory][Number][Prop1][Prop2]
How I can achieve this using BindingSource?
I've tried using my BindingSource in my DataGridView, but instead of 5 columns, I get 3 (the nested class is treated like one column, where the inner properties should be there instead):
And I cannot select the inner properties of MapperInfo class when trying to add columns:
You can create a new class with all the properties that you want to be display in the grid and map it with your existing class either manually or using third-party libraries (ex. AutoMapper). Then bind the new class to Grid.
public class MyGridClass
{
public string FileName { get; set; }
public string Directory { get; set; }
public string Number { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
NesInfo ni = ...
MyGridClass gc = new MyGridClass ( );
gc.FileName = ni.FileName;
gc.Directory = ni.Directory;
gc.Number = ni.MapperInfo.Number;
gc.Prop1 = ni.MapperInfo.Prop1;
gc.Prop2 = ni.MapperInfo.Prop2;
Use CellFormatting event handler. DataGridView.CellFormatting Event
private void dataGridView1_CellFormatting(object sender,
DataGridViewCellFormattingEventArgs e)
{
if (e.RowIndex < 0 || e.ColumnIndex < 0)
return;
DataGridViewColumn column = this.dataGridView1.Columns[e.ColumnIndex];
//For getting right column you can compare to the index
//Or as in this example comparing to the names of the predefined columns
if (column.Name.Equals(this.ColumnMapperInfoNumber.Name) == true)
{
MapperInfo temp = e.Value as MapperInfo;
if (temp != null)
e.Value = temp.Number;
}
else if(column.Name.Equals(this.ColumnMapperInfoProp1.Name) == true)
{
MapperInfo temp = e.Value as MapperInfo;
if (temp != null)
e.Value = temp.Prop1;
}
else if(column.Name.Equals(this.ColumnMapperInfoProp2.Name) == true)
{
MapperInfo temp = e.Value as MapperInfo;
if (temp != null)
e.Value = temp.Prop2;
}
}
Another way which can be used is overriding .ToString() method in your class,
because DataGridViewTextBoxColumn will execute this method on the bounded item for getting displayed text (that is why you see name of your class there).
[Serializable]
public class MapperInfo
{
public string Number { get; set; }
public string Prop1{ get; set; }
public string Prop2 { get; set; }
public override string ToString()
{
return this.Number + ", " + this.Prop1 + ", " + this.Prop2;
}
}
But I afraid this approach isn't for you, because you want different properties in the different columns
Ended up creating a flat class used for grid view, and set up Mapping using AutoMapper from scratch:
private void Map(NesInfo ni,out RomInfoView romInfo)
{
Mapper.CreateMap<NesInfo, RomInfoView>()
.ForMember(x => x.MapperNumber, options => options.MapFrom(src => src.MapperInfo.Number))
.ForMember(x => x.Prop1, options => options.MapFrom(src => src.MapperInfo.Prop1))
.ForMember(x => x.Prop2,
options => options.MapFrom(src => src.MapperInfo.Prop2));
romInfo = Mapper.Map<RomInfoView>(ni);
}
As suggested as #Vidhyardhi Gorrepati