MVVM WPF C# - Combobox binding foreign key FormatException - c#

I have two entities: Client and Manager.
Code for them is:
public partial class Manager
{
public Manager()
{
this.Clients = new HashSet<Client>();
}
public int Id_manager { get; set; }
public string Name_manag { get; set; }
public virtual ICollection<Client> Clients { get; set; }
}
public partial class Client
{
public Client()
{}
public int Id_cl { get; set; }
public int Manager { get; set; }
public virtual Manager Manager1 { get; set; }
}
ViewModel class has this code:
private ObservableCollection<Manager> _managerList;
public ObservableCollection<Manager> ManagerList
{
get { return _managerList; }
set
{
_managerList = value;
OnPropertyChanged("ManagerList");
}
}
On my view i have combobox, where i can choose a client manager.
Also i have a DataGrid, which shows the list of clients.
I binded ComboBox with this XML code:
<ComboBox Name="cbManager"
Margin="5"
Padding="3"
ItemsSource="{Binding ManagerList}"
DisplayMemberPath="Name_manag"
SelectedValuePath="Id_manager"
SelectedValue="Binding Manager1.Id_manager"
SelectedItem="{Binding Path=Manager1, Mode=TwoWay}"/>
So the problem is - when i open my view, i get an exception :
System.Windows.Markup.XamlParseException:
System.Number.StringToNumber(string, System.Globalization.NumberStyles, ref System.Number.NumberBuffer, System.Globalization.NumberFormatInfo, bool)
System.Number.ParseInt32(string, System.Globalization.NumberStyles, System.Globalization.NumberFormatInfo)
string.System.IConvertible.ToInt32(System.IFormatProvider)
System.Convert.ChangeType(object, System.Type, System.IFormatProvider)
MS.Internal.Data.SystemConvertConverter.Convert(object, System.Type, object, System.Globalization.CultureInfo)
MS.Internal.Data.DynamicValueConverter.Convert(object, System.Type, object, System.Globalization.CultureInfo)
System.Windows.Controls.Primitives.Selector.VerifyEqual(object, System.Type, object, MS.Internal.Data.DynamicValueConverter)
System.Windows.Controls.Primitives.Selector.FindItemWithValue(object, out int)
System.Windows.Controls.Primitives.Selector.SelectItemWithValue(object, bool)
System.Windows.Controls.Primitives.Selector.OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs)
I binded my ComboBox incorrectly, so help me to write correct bind.

Related

Correct way of binding xamarin forms picker value

My issue is that I have 2 picker controls. 1 of these picker controls is bound to list a and one of these picker controls is bound to list b. Both of these controls display the data I need them to with the correct display bindings working fine.
My problem is when I bind the selectedItem property, it works for list a but doesn't for list b. I've checked that the code is literally a like for like copy of each other.
I have been using the syncfusion Combobox but switched to the picker as I thought there was an issue here but there isn't. Whatever is happening is totally down to whatever I'm doing.
The usage scenario is that I bring down a list of payment types from my API and populate a picker based on this. This works.
The datasource for my main view contains an ID. When I am modifying a record, I run a method called update to find the selectedItem. I'm not happy with this approach and would be interested to see what other people use.
The update method gets the datasources for the pickers and finds what I would expect to be the selected item. This works fine also but doesn't bind.
[Serializable]
public class PaymentInformation :BaseModel
{
public int? ID { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int? PaymentTypeId { get; set; }
public string PaymentTo { get; set; }
public string Location { get; set; }
public string Notes { get; set; }
public PersonInformation PersonBudget { get; set; }
public decimal AmountPaid { get; set; }
public decimal AmountReceived { get; set; }
public double TotalHours { get; set; }
public void Update(ObservableCollection<ResourceInformation> resources , ObservableCollection<PaymentTypeInformation> paymentTypes)
{
if(PaymentTypeId != null) this.PaymentTypeInformation1 = paymentTypes?.FirstOrDefault((paymentType) => paymentType.ID == PaymentTypeId.Value);
this.Resource = resources?.FirstOrDefault((resource) => resource.ResourceId == PersonBudget?.ID);
}
private PaymentTypeInformation _paymentTypeInformation;
private PaymentTypeInformation PaymentTypeInformation1 { get { return _paymentTypeInformation; } set { _paymentTypeInformation = value; OnPropertyChanged(nameof(PaymentTypeInformation1)); } }
private ResourceInformation _resource;
public ResourceInformation Resource { get { return _resource; } set { _resource = value; OnPropertyChanged(nameof(Resource)); } }
}
The underlying xaml is:
<Label Grid.Row="8" Grid.Column="0" Text="Payment Type:" />
<Picker BackgroundColor="White" Grid.Row="8" Grid.Column="1" ItemsSource="{Binding PaymentTypesDataSource}" ItemDisplayBinding="{Binding Path=DisplayText}" IsEnabled="{Binding IsProcessing, Converter={StaticResource reverseBoolConvertor}}" SelectedItem="{Binding DataSource.PaymentTypeInformation1, Mode=TwoWay}" />
The expected result is that the drop down initializes with the selectedItem which it doesn't (in one usage scenario - the other one works fine).
Couldn't see the wood for the trees.
private PaymentTypeInformation PaymentTypeInformation1
{
get
{
return _paymentTypeInformation;
}
set
{
_paymentTypeInformation = value;
OnPropertyChanged(nameof(PaymentTypeInformation1));
}
}
Can't bind to a private property - changed to public and immediately work. Stuck on this for a day as bonkers as that is to believe.

No filled in data in RadDataGrid

I have an UWP app and I am using a RadDataGrid to show some data. When I do a API call to my API to set data in the grid, the data grid shows empty rows.
My XAML
<tg:RadDataGrid Grid.Row="2" UserGroupMode="Disabled" ColumnDataOperationsMode="Flyout" x:Name="infoGrid" ItemsSource="{x:Bind Path=ViewModel.history}" AutoGenerateColumns="False" FontSize="24" VerticalContentAlignment="Top" Margin="0,0,0,50" VerticalAlignment="Top" MaxHeight="500">
<tg:RadDataGrid.Columns>
<tg:DataGridTextColumn PropertyName="UpdatedBy" Header="Verplaatst door"/>
<tg:DataGridDateColumn PropertyName="UpdateDate" Header="Bijgewerkt op" CellContentFormat=" {0:dd/MM/yyyy}" />
<tg:DataGridTextColumn PropertyName="Location" Header="Verplaatst naar"/>
</tg:RadDataGrid.Columns>
</tg:RadDataGrid>
My C# code to set data
public async void APICALL()
{
var result = await LotService.GetLotInfo(ViewModel.scanField);
ViewModel.lot = result;
ViewModel.history = result.LotHistory;
infoGrid.ItemsSource = null;
infoGrid.ItemsSource = ViewModel.history;
IsBusy = false;
}
}
ViewModel.history is a List of LotHistoryInfo Class
EDIT: Added getters and setters as mm8 suggested
public class LotHistoryInfo
{
public LotInfo lot { get; set; }
public string scanField { get; set; }
public List<LotHistoryInfo> history { get; set; }
public LotHistoryInfo(LotStoreHistory his)
{
Location = new Location(his.LshStoreid.ToString(), his.LshStorex, his.LshStorey, his.LshStorez);
UpdatedBy = his.UpdatedBy;
UpdateDate = his.Updated;
}
public LotHistoryInfo()
{
}
}
My data grid acknowledges there should be 3 items in it, but it doesnt fill them in.
Sooo, what am I missing?
You can only bind to public properties:
public class LotHistoryInfo
{
public Location Location { get; set; }
public string UpdatedBy { get; set; }
public DateTimeOffset UpdateDate { get; set; }
public LotHistoryInfo(LotStoreHistory his)
{
Location = new Location(his.LshStoreid.ToString(), his.LshStorex, his.LshStorey, his.LshStorez);
UpdatedBy = his.UpdatedBy;
UpdateDate = his.Updated;
}
public LotHistoryInfo()
{
}
}
You have implemented Location, UpdatedBy and UpdateDate as fields:
public Location Location;
public string UpdatedBy;
public DateTimeOffset UpdateDate;

WPF DataGrid binding doesn't update

Here's my DataGrid:
<DataGrid x:Name="MoonMining"
ItemsSource="{Binding MarketData.MoonMinerals, ElementName=window}">
<DataGrid.DataContext>
<local:MoonMineral/>
</DataGrid.DataContext>
<DataGrid.Columns>
.. Yes i have columns and they are irrelevant to my question .
</DataGrid.Columns>
</DataGrid>
MarketData is a class which contains most of my programs logic. MoonMinerals is defined in that class:
public class MarketData
{
private ObservableCollection<MoonMineral> _moonMinerals = new ObservableCollection<MoonMineral>();
public ObservableCollection<MoonMineral> MoonMinerals
{
get { return _moonMinerals; }
set { _moonMinerals = value; }
}
}
And here's my MoonMineral class:
[NotifyPropertyChanged]
public class MoonMineral
{
public MoonMineral()
: this("Def", "Def")
{
}
public MoonMineral(string name, string rarity)
{
Name = name;
Rarity = rarity;
}
public string Name { get; set; }
public double Price { get; set; }
public double Volume { get; set; }
public string Rarity { get; set; }
public double TransportVolume { get; set; }
public double TransportCosts { get; set; }
public double GrossProfit { get; set; }
public double NetProfit { get; set; }
}
As you can see, I'm using PostSharp to clear up my code, but when I manually implement INotifyPropertyChanged I have the same problem.
Now the problem is that my DataGrid doesn't update by itself, I have to manually call this in a method which modifies MoonMinerals:
var bindingExpression = MoonMining.GetBindingExpression(ItemsControl.ItemsSourceProperty);
if (bindingExpression != null)
bindingExpression.UpdateTarget();
I know this isn't big of a deal, but I wanted to finally manage to bind data to ui entirely using xaml. All my previous attempts involved setting DataGrids ItemsSource property every time I updated the data.
To sum up comments you're implementing INotifyPropertyChanged interface for MoonMineral class and use ObservableCollection which will handle changes to the collection but there seems to be nothing in place to handle changes to MoonMinerals property
private ObservableCollection<MoonMineral> _moonMinerals = new ObservableCollection<MoonMineral>();
public ObservableCollection<MoonMineral> MoonMinerals
{
get { return _moonMinerals; }
set { _moonMinerals = value; }
}
You can either implement INotifyPropertyChanged interface in the class that exposes MoonMinerals property or change it to read-only and use only one instance of _moonMinerals and simply clear it and add/remove items
private readonly ObservableCollection<MoonMineral> _moonMinerals = new ObservableCollection<MoonMineral>();
public ObservableCollection<MoonMineral> MoonMinerals
{
get { return _moonMinerals; }
}
Also, as a side note, you don't need
<DataGrid.DataContext>
<local:MoonMineral/>
</DataGrid.DataContext>
as this will set DataContext of the DataGrid to new instance of MoonMineral. It works in your case as you change binding context of ItemsSource using ElementName so DataContext is not used in your case.

Displaying child records in data grid in WPF using Entity Framework

I have two tables named Process and ProcessTriggers. ProcessTriggers referring Process table.And need to create a screen to diplay Process as well as the ProcessTrigger related to the Process. I have created data objects using entity framework and my classes look like.
public partial class Process
{
public Process()
{
this.ProcessTriggers = new ObservableCollection<ProcessTrigger>();
}
public int ID { get; set; }
public int WorkflowID { get; set; }
public string Name { get; set; }
public bool IsBatch { get; set; }
public Nullable<System.DateTime> ModifiedOn { get; set; }
public virtual ObservableCollection<ProcessTrigger> ProcessTriggers { get; set; }
}
And my view model look like..
public class ProcessViewModel : ViewModel<Process>
{
private RelayCommand saveCommand;
private RelayCommand cancelCommand;
public ProcessViewModel()
{
using(var context = new PACEContext())
{
this.Entity = context.Processes.FirstOrDefault(i => i.ID == 1);
IsInEditMode = true;
}
}
}
I am binding these viewModel to my View, Entity properties are bound correctly, but the ProcessTriggerCollection is not binding to datagrid..
<DataGrid ItemsSource="{Binding Entity.ProcessTriggers}" AutoGenerateColumns="True">
</DataGrid>
I juts started learning WPF, MVVM and Entity framework.Can any one help.?
I understand the Process have a ObservableCollection of other class called ProcessTriggers.
Why you don't create a ObservableCollection and binding this?
private ObservableCollection<ProcessTrigger> _listProcessTriggers;
public ObservableCollection<ProcessTrigger> ListProcessTriggers
{
get { return _listProcessTriggers; }
set { _listProcessTriggers= value; RaisePropertyChanged("ListProcessTriggers"); }
}
public ProcessViewModel()
{
using(var context = new PACEContext())
{
this.Entity = context.Processes.FirstOrDefault(i => i.ID == 1);
ListProcessTriggers = Entity.ProcessTriggers;
IsInEditMode = true;
}
}
And In Xmal Binding
<DataGrid ItemsSource="{Binding ListProcessTriggers}" AutoGenerateColumns="True />

How to fill in the DataGridTextColumn through part of the aggregated object?

I want to fill in the field DataGridTextColumn through part of the aggregated object.
I want to binding only a field Nazwa (not whole object Wynik) of the class Rezultat to the following DataGridTextColumn.
How I can do this ?
XAML:
<DataGridTextColumn Binding="{Binding Rezultat}" Header="Rezultat"/>
C#:
public class Rezultat
{
public int IdWyniku { get; set; }
public int IdZdarzenia { get; set; }
public int IdUżytkownika { get; set; }
public string Nazwa { get; set; }
public Rezultat() { }
public Rezultat(int IdZdarzenia, int IdUżytkownika, string Nazwa, int IdWyniku=0)
{
this.IdZdarzenia = IdZdarzenia;
this.IdUżytkownika = IdUżytkownika;
this.Nazwa = Nazwa;
this.IdWyniku = IdWyniku;
}
}
public class Zdarzenie
{
public Rezultat Wynik { get; set; }
}
C# (piece of code from another class):
this.ListaKuponów[0].ListaZdarzeń.Add(new Zdarzenie()
{
Wynik = new Rezultat(Convert.ToInt32(napis.ItemArray.GetValue(7)), this.IdRejestracji, wynik, idwyniku),
});
As pointed out earlier in the comments, you haven't shown us the property you are binding.
Assuming you implemented property Result of type Rezultat you'd need to bind to Nazwa in the markup as follows:
<DataGridTextColumn Binding="{Binding Result.Nazwa}" Header="Rezultat"/>
Note: If you haven't got implemented INotifyPropertyChanged on this class, you won't receive any updates if the field changes. You'd need to create either an extra ViewModel or implement INotifyPropertyChanged in your model. (Assuming that you are actually using the MVVM pattern)

Categories