i'm trying to bound a list of object to DataGrid but i'm getting wrong value:
the object class:
public class Attribute
{
public Attribute()
{
}
private string _name;
public string name
{
get { return _name; }
set { _name = value; }
}
private List<Value> _valueList = new List<Value>();
public List<Value> ValueList
{
get { return _valueList; }
set { _valueList = value; }
}
}
public class Value
{
private string _value;
public string value
{
get { return _value; }
set { _value = value; }
}
public override string ToString()
{
return _value.ToString();
}
}
and i'm having a list of objects: List<Attribute> attributes
attributeDataGrid.ItemsSource = attributes;
when i bound i get a grid with name column correct
but the "ValueList" shown as "(Collection)" instead of the string...
how should i bound the List ?
Your overriden ToString method in Value is not called, because WPF displays the content of ValueList in your second column, i.e. it displays ValueList.ToString().
What do you expect to see in the second column? A comma separated list of the values in the ValueList?
Try below things it works for me.
On Load Method write below code
Attribute atr = new Attribute();
atr.ValueList.Add(new Value() { value = "One" });
atr.ValueList.Add(new Value() { value = "Two" });
atr.ValueList.Add(new Value() { value = "Three" });
atr.ValueList.Add(new Value() { value = "Four" });
dataGrid.DataContext = atr.ValueList;
On XAML file try below
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False" ItemsSource="{Binding}" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding value}" />
</DataGrid.Columns>
</DataGrid>
Hope this code snippet helps to you.
Related
I want to show the Items of a List in a DataGrid. So I did something which should probably work.
First I set the ItemsSource of my DataGrid to the List which is located inside a class, and bound the DataGridTextColumns of my custom Columns to the Items of the List.
The Class:
public class Auftrag : INotifyPropertyCanged
{
private int _id; // ID is an example to show that there is more then `Services`
private List<Services> _services = new List<Services>();
[...] // There are more Fields
[...] // There are more Properties
public int ID { get; set; } // ID is just an Example. My Properties aren't {get; set;} My properties has all the same structure like `Services` Property
public List<Services> Services
{
get => _services;
set
{
if(_services == value) return;
_services = value;
OnPropertyChanged("Services");
}
}
}
The Services class contains the Items which you can see in the XAML of the View where the DataGridTextColumn is bind to.
The ViewModel
In the ViewModel I created a Property which holds the results from my DataBase:
private Auftrag _sAuftrag = new Auftrag();
public Auftrag SAuftrag
{
get => _sAuftrag;
set
{
if (_sAuftrag == value) return;
_sAuftrag = value;
OnPropertyChanged("SAuftrag");
}
}
Im reading the DataBase and add the result to the Properties:
public async Task<bool> LoadSingleAuftrag(int aID)
{
return await Task.Run(() =>
{
// Check SQL Connection, Run Query etc.
[...]
using(var reader = cmd.ExecuteReader())
{
while(reader.Read())
{
SAuftrag.AuftragsId = reader["AuftragsID"] as int? ?? default(int);
SAuftrag.KundenId = reader["KundenID"] as int? ?? default(int);
SAuftrag.Status = reader["Status"] as string;
SAuftrag.CreatedDate = reader["ErstelltAm"] as DateTime? ?? default(DateTime);
SAuftrag.FinishedDate = reader["FertigGestelltAm"] as DateTime? ?? default(DateTime);
SAuftrag.Services.Add(new Services
{
Servicename = reader["Servicename"] as string,
Servicebeschreibung = reader["Servicebeschreibung"] as string,
Einzelpreis = reader["Preis"] as double? ?? default(double),
Anzahl = reader["Anzahl"] as int? ?? default(int),
Preis = Convert.ToDouble(reader["Preis"]) * Convert.ToInt32(reader["Anzahl"])
});
}
// Is working fine, so the Items are sucessfully stored into the `Services` list.
Debug.WriteLine("Anzahl: " + SAuftrag.Services[0].Anzahl);
Debug.WriteLine("Einzelpreis: " + SAuftrag.Services[0].Einzelpreis);
Debug.WriteLine("Gesamtpreis: " + SAuftrag.Services[0].Preis);
}
}
}
The View
<Window.DataContext>
<viewModels:AuftragViewModel />
</Window.DataContext>
[...]
<DataGrid ItemsSource="{Binding SAuftrag.Services}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn x:Name="Servicename" Header="Servicename" Binding="{Binding Path=Servicename}" />
<DataGridTextColumn x:Name="Beschreibung" Header="Beschreibung" Binding="{Binding Path=Servicebeschreibung}" />
<DataGridTextColumn x:Name="Anzahl" Header="Anzahl" Binding="{Binding Path=Anzahl}" />
</DataGrid.Columns>
</DataGrid>
For me it looks right and should work. But for some Reason I'm getting InvalidOperationException Error.
If I commented the DataGrid the application works and it Bindings are visible (for example the ID Binding). So there is no Instance Error.
Short Version of Error message:
An ItemsControl is not consistent with its element source. For more information, see the inner exception.
May the Properties of the Services class causes the Error?:
Services Class
[..]
private double _preis;
private double _einzelpreis;
[..]
public double Preis
{
get => _preis;
set
{
if (_preis == value) return; // Possible loss of precision while rounding values warning
_preis = value;
OnPropertyChanged("Preis");
}
}
public double Einzelpreis
{
get => _einzelpreis;
set
{
if (_einzelpreis == value) return; // Possible loss of precision while rounding values warning
_einzelpreis = value;
OnPropertyChanged("Einzelpreis");
}
}
View.CodeBehind
In the Code behind, I call a Method to Load data:
private readonly AuftragViewModel _viewModel;
public AuftragsInfo(int aID)
{
InitializeComponent();
_viewModel = (AuftragViewModel) DataContext;
LoadAuftrag(aID);
}
public async void LoadAuftrag(int aID)
{
if (!await _viewModel.LoadSingleAuftrag(aID))
{
Close();
}
}
#ASh wrote in the comments to try out ObservableCollection instead of List.
I don't know why, but after changing the List to ObservableCollection and adding the items via Dispatcher, it's working.
So if someone have this kind of error in the future too, dont use List anymore. I guess it's the best way to use ObservableCollection.
Thanks to #ASh.
Initially, ComboBox DataContext is set with Profession1 and SelectedValue as Politician. At Runtime, i changed the Datacontext to Profession2. Doing this is changing the Profession1 to null.
Please refer the below code:
<Page.Resources>
<local:MainPageViewModel x:Key="datacontent"></local:MainPageViewModel>
</Page.Resources>
<ComboBox x:Name="comboBox"
ItemsSource="{Binding Professions,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Profession, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Width="100"
Height="100"
VerticalAlignment="Center"
HorizontalAlignment="Stretch" />
Code Behind:
var datacontent = (this.Resources["datacontent"] as MainPageViewModel);
this.comboBox.DataContext = datacontent.Profession1;
Model:
public class MainPageViewModel
{
public MainPageViewModel()
{
Profession1 = new Person();
Profession2 = new Person();
}
private Person profession1;
public Person Profession1
{
get { return profession1; }
set { this.profession1 = value; }
}
private Person profession2;
public Person Profession2
{
get { return profession2; }
set { this.profession2 = value; }
}
}
public class Person : INotifyPropertyChanged
{
public Person()
{
_professions = new List<string>();
_professions.Add("Lawyer");
_professions.Add("Politician");
_professions.Add("Other");
}
private string _profession;
public string Profession
{
get
{
if (string.IsNullOrWhiteSpace(_profession))
{
// _profession = _professions.LastOrDefault();
}
return _profession;
}
set
{
if (_profession != value)
{
_profession = value;
NotifyPropertyChanged("Profession");
}
}
}
private List<string> _professions;
public List<string> Professions
{
get
{
return _professions;
}
}
}
I have used the below code, to check the previous datacontext (Profession1->Professon) value .
Code
((this.Resources["datacontent"] as MainPageViewModel).Profession1 as Person).Profession
Output is : null.
Expected value : Politician
Please someone suggest on this.
((this.Resources["datacontent"] as MainPageViewModel).Profession1 as Person).Profession
Output is : null. Expected value : Politician
Please someone suggest on this.
The problem is that when you modify the DataContext of combobox, the DataContext is set null first and then turns to Profession2. So the Profession property of Profession1 will be set null. For your requirement, you could set the judgment condition to solve this issue.
public string Profession
{
get
{
return _profession;
}
set
{
if (_profession != value && value != null)
{
_profession = value;
OnPropertyChange();
}
}
}
I'm trying to making the following
<platform:TablePrac.Columns>
<platform:TextColumn Caption="it"/>
<platform:TextColumn Caption="is"/>
</platform:TablePrac.Columns>
But when I run this the error 'Property Columns is null or is not IEnumerable' is occurred.
The code flow is following.
When I wrote like above .xaml code, the property named Columns set the value.
(Columns is defined like below)
public List<Column> Columns
{
set
{
columns = value;
SetValue(ColumnsProperty, value);
}
get
{
return (List<Column>)GetValue(ColumnsProperty);
}
}
public class Column
{
public string caption;
public Type type;
}
public class TextColumn : Column
{
public TextColumn() : base()
{
this.type = typeof(string);
}
public TextColumn(string cap) : base()
{
this.caption = cap;
this.type = typeof(string);
}
public string Caption
{
set { caption = value; }
get { return caption; }
}
public Type Type
{
get { return type; }
}
}
As a very similar case, defining StackLayout and making new views in it like below
<StackLayout>
<Label Text="it"/>
<Label Text="is"/>
</StackLayout>
is same in .cs code like below
StackLayout stack = new StackLayout
{
Children =
{
new Label { Text = "it"},
new Label { Text = "is"}
}
};
So, I want to make property Columns work as StackLayout in .xaml but I don't know how. I spend two days to solve it.... I need your help
Thank you.
(Plus, StackLayout and Children are defined like below
StackLayout
public class StackLayout : Layout<View>
Layout
[Xamarin.Forms.ContentProperty("Children")]
public abstract class Layout<T> : Layout, IViewContainer<T>
where T : View
{
public IList<T> Children { get; }
...
}
)
The problem is not IEnumerable but Null value.
When using BindableProperty in Xamarin.Forms, you can assign a default value to the Property. For example, give default value 'new List()' solve this problem.
Follwing is my code, if you have same problem, check it.
Before :
public static readonly BindableProperty ColumnsProperty = BindableProperty.Create("Columns", typeof(List<Column>), typeof(TablePrac));
public List<Column> Columns
{
set
{
SetValue(ColumnsProperty, value);
}
get
{
return (List<Column>)GetValue(ColumnsProperty);
}
}
After :
public static readonly BindableProperty ColumnsProperty = BindableProperty.Create("Columns", typeof(IEnumerable<Column>), typeof(TablePrac), new List<Column>());
public IEnumerable<Column> Columns
{
set
{
SetValue(ColumnsProperty, value);
}
get
{
return (IList<Column>)GetValue(ColumnsProperty);
}
}
I convert type of return value of Columns to IList since in case of 'StackLayout's Children', the type of Children is IList type. There is no other reason.
I'm trying to construct a ComboBox that will display a selected item. I can get the combo box to display the options easily enough, but getting the selected item to display is not working. The selected item refers to a value being pulled from a database.
I know that the set method is working because I can successfully store the user's choice. However, I cannot seem to get the ComboBox to populate the selected item when I grab it from the database.
I've been frustrated over this for a few weeks now, and I feel like the mistake is simple.
Scenario:
I have an Animal Model
public class AnimalModel
{
public string AnimalName { get; set; }
...
}
I have an AnimalViewModel for each Animal:
public class AnimalViewModel: BindableBase
{
private AnimalViewModel _animal;
public AnimalViewModel(AnimalModel animal)
{
_animal = animal;
}
public string AnimalName
{
get { return _animal.Name; }
set
{
if (value != this._animal.Name)
{
this._animal.Name = value;
OnPropertyChanged("AnimalName");
}
}
}
...
}
I have an ObservableCollection of AnimalViewModel objects:
public class TemplateViewModel : BindableBase
{
private ObservableCollection<AnimalViewModel> _animals;
public TemplateViewModel(...)
{
_animal = methodReturnsAnObservableCollectionOfAnimalViewModels();
}
public ObservableCollection<AnimalViewModel> Animals
{
get { return _animals; }
set
{
if (value != this._animals)
{
this._animals = value;
OnPropertyChanged("Animals");
}
}
}
}
With this in place, I can easily display a list of AnimalNames in the ComboBox:
<ComboBox ItemsSource="{Binding Animals}"
DisplayMemberPath="AnimalName"
IsEditable="True"
IsReadOnly="True"
Text="--- Select Animal ---"/>
I now bind to a SelectedItem
public class TemplateViewModel
{
...
private AnimalViewModel _selectedAnimal;
public TemplateViewModel(MyObject, ...)
{
...
_selectedAnimal = new AnimalViewModel(new AnimalModel() { AnimalName = MyObject.AnimalName });
}
...
public AnimalViewModel SelectedAnimal
{
get { return _selectedAnimal; }
set
{
if (value != _selectedAnimal)
{
_selectedAnimal = value;
AnimalName = value.AnimalName;
OnPropertyChanged("SelectedAnimal");
}
}
}
}
So now, I have:
<ComboBox ItemsSource="{Binding Animals}"
DisplayMemberPath="AnimalName"
SelectedItem={Binding SelectedAnimal}
SelectedValuePath="AnimalName"
IsEditable="True" IsReadOnly="True"
Text="--- Select Animal ---"/>
Unfortunately, this does not populate the ComboBox with an Animal. It just pulls up the default choice Select Animal with the options populated. It does set the item correctly.
Any help would be appreciated.
You need to access the actual reference to the animal in question.
In the codebehind you create an animal which looks like the animal in the ObservableCollection, but its not the animal by reference.
Change (after Animals is loaded)
_selectedAnimal = new AnimalViewModel(...);
to (only use the Property accessor, not the backing store variable; so the change event fires, btw)
SelectedAnimal = Animals[0];
Test app (formatting like grid row/column removed)
<Label>SelectedItem</Label>
<TextBlock Text="{Binding SelectedItem, ElementName=cbMain}"/>
<Label >SelectedValue</Label>
<TextBlock Text="{Binding SelectedValue, ElementName=cbMain}"/>
<Button Click="ChangeSelectedValue">Set Selected Value</Button>
<ComboBox Name="cbMain"
ItemsSource="{Binding Ships}"
SelectedItem="{Binding Ships[0]}"
SelectedValuePath="Name"
Text="Select a Ship"/>
Result on Load of screen:
I think this is a naming discrepancy between SelectedValuePath="AnimalName" and your property.
public string Animal //<----Should Be AnimalName!
{
get { return _animal.Name; }
set
{
if (value != this._animal.Name)
{
this._animal.Name = value;
OnPropertyChanged("AnimalName");
}
}
}
change your property to AnimalName and you're good to go.
I have defined a data template for a c# class in xaml as follows
<DataTemplate x:Key="ApplicationTemplate">
<StackPanel Orientation="Vertical">
<telerik:RadComboBox DisplayMemberPath="Name"
ItemsSource="{Binding CurrentItem.Apps, RelativeSource={RelativeSource FindAncestor, AncestorType=telerik:RadDataForm}}"
IsEnabled="{Binding IsReadOnly, RelativeSource={RelativeSource AncestorType=ContentControl}, Converter={StaticResource BooleanInverterConverter}}" />
</StackPanel>
</DataTemplate>
My "Apps" Enumerable is composed of this object:
public class InteractiveApplicationModel : ValidatingModel
{
public string Name
{
get { return GetProperty(() => Name); }
set { SetProperty(() => Name, value); }
}
public string Type
{
get { return GetProperty(() => Type); }
set { SetProperty(() => Type, value); }
}
public string URL
{
get { return GetProperty(() => URL); }
set { SetProperty(() => URL, value); }
}
public string Image
{
get { return GetProperty(() => Image); }
set { SetProperty(() => Image, value); }
}
public InteractiveApplicationModel()
{
this.Type = string.Empty;
this.Name = string.Empty;
this.URL = string.Empty;
this.Image = string.Empty;
}
public InteractiveApplicationModel(string name, string type, string url, string image)
{
this.Name = name;
this.Type = type;
this.URL = url;
this.Image = image;
}
}
}
In the View model i have:
public IList<InteractiveApplicationModel> Apps
{
get
{
return new List<InteractiveApplicationModel>()
{
new InteractiveApplicationModel(null,null,null,null),
new InteractiveApplicationModel("name","type","url","image"),
new InteractiveApplicationModel("name2","type2","url2","image2")
};
}
}
I then have a form page which loads "complex" objects which contain "InteractiveApplicationModel" objects and use the data template to display those objects.
Everything is working except one thing. I need the selected value of the combobox to be the same as the value of the "InteractiveApplicationModel" objects in my "complex" objects.
My "complex" objects can have up to 5 "InteractiveApplicationModel" objects inside them.
You have to Bind the SelectedItem property on the RadComboBox
SelectedItem="{Binding SelectedInteractiveApplicationModel, [.... ancestor ....] Mode=TwoWay}"
with a Selected property on the ViewModel
public InteractiveApplicationModel SelectedInteractiveApplicationModel
{
get { return GetProperty(() => SelectedInteractiveApplicationModel); }
set { SetProperty(() => SelectedInteractiveApplicationModel, value); }
}