Let's say I have a Person Class like
Public class Person
{
public string firstName {set;get;}
public string lastName {set;get;}
}
in my ViewMode class I have an observableCollection of Person
private ObservableCollection<Person> _people;
public ObservableCollection<Person> People
{
get { return _people; }
set
{
_people= value;
FirePropertyChanged("People");
}
}
in my view I have a datagrid that ItemsSource is binded to People. I have a combobox in my view that its SelectedValue is binded to SelectedFieldValue and I want to add a row to my gridview with the value selected in this Combobox
private string _selectedFieldValue;
public string SelectedFieldValue
{
get { return _selectedFieldValue; }
set
{
_selectedFieldValue = value;
FirePropertyChanged("SelectedFieldValue");
People.Add(New Person{firstName = value});
}
}
the row is added to the Grid, but I don't see the value of the FirstName added. I tried several way to bind the column FirstName but couldn't get it working correctly.What's the correct way to bind my DataGridTextColumn in grid to firstName, lastName properties of the person class.
check out this..
<sdk:DataGrid x:Name="dataGrid4"
Height="160" ItemsSource="{Binding People}" AutoGenerateColumns="False" >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn
Header="First Name"
Width="SizeToHeader"
Binding="{Binding firstName}"
FontSize="20" />
<sdk:DataGridTextColumn
Header="Last Name"
Width="SizeToCells"
Binding="{Binding lastName}"
FontSize="20" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
Related
i have very simple DataGridTextColumn which should be modified on doubleclick event.
question is what should be added to avoid exception System.InvalidOperationException: ''EditItem' is not allowed for this view.'
<DataGrid x:Name="DG" ItemsSource="{Binding}" GridLinesVisibility="None" Grid.Column="3" Grid.Row="2">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding VariantSet, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MinWidth="60" />
</DataGrid.Columns>
</DataGrid>
simple class:
Public Class CName
Public Property Name As String = "not editable name"
End Class
on load simply added to datagrid
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Me.DG.Items.Add(New CName)
End Sub
when declared through template as following, there is no difference, same error
<DataGridTemplateColumn Header="Name" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
even when Implements ComponentModel.INotifyPropertyChanged is added to the CName, no difference
You've not shown us enough to tell what you're doing wrong.
Here's a working window with datagrid.
The code is c# but you can run it through a converter to vb if you particularly want vb. I think it's a bad idea for a beginner to choose vb nowadays. Almost nobody publishes samples using vb.
In my mainwindow:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding People}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding FirstName}" Header="First Name"/>
<DataGridTextColumn Binding="{Binding LastName}" Header="SurName"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
That viewmodel:
public class MainWindowViewModel : BaseViewModel
{
private ObservableCollection<Person> people = new ObservableCollection<Person>();
public ObservableCollection<Person> People
{
get { return people; }
set { people = value; }
}
public MainWindowViewModel()
{
People.Add(new Person { FirstName = "Chesney", LastName = "Brown" });
People.Add(new Person { FirstName = "Gary", LastName = "Windass" });
People.Add(new Person { FirstName = "Liz", LastName = "McDonald" });
People.Add(new Person { FirstName = "Carla", LastName = "Connor" });
}
}
Person just has those two properties first and last name:
public class Person : BaseViewModel
{
private string firstName;
public string FirstName
{
get { return firstName; }
set { firstName = value; RaisePropertyChanged(); }
}
private string lastName;
public string LastName
{
get { return lastName; }
set { lastName = value; RaisePropertyChanged(); }
}
That inherits from BaseViewmodel which just implements inotifypropertychanged.
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Here I am editing a row
EDIT: Solved
(I made another property in the ViewModel wrapper and bound to that)
I am trying to bind a property that is not related to the ObservableCollection that the DataGrid is bound to. The other columns are binding the way they should, it is just this one column that I can't seem to get to work.
I tried binding the property using RelativeSource AncestorType and directly to the DataContext with no luck.
The XAML, The ObservableCollection I am binding to obviously is called MonthlyRecords which is a collection of a different class and this is binding the way it should be. It is the property SelectedTenant.FullName which has nothing to do with the collection that is giving me grief.
<DataGrid ItemsSource="{Binding MonthlyRecords}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!--Trying to bind this Property in the next line-->
<TextBlock Text="{Binding Path=SelectedTenant.FullName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="60" Header="Code" Binding="{Binding UpdateSourceTrigger=LostFocus, Path=TenantCode}" />
This is the class for the property I am trying to bind.
public class Tenant
{
public Tenant()
{
}
public int Code { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string FullName => LastName + " " + FirstName;
public string Section { get; set; }
public Tenant(int code, string lastName = null, string firstName = null, string section = null)
{
Code = code;
LastName = lastName;
FirstName = firstName;
Section = section;
}
}
And this is the property in the ViewModel I am trying to bind to.
private Tenant _selectedTenant;
public Tenant SelectedTenant
{
get { return _selectedTenant; }
set
{
if (Equals(_selectedTenant, value)) return;
_selectedTenant = value;
OnPropertyChanged();
}
}
What else do I need to do to get this to bind to the DataGrid?
<DataGridTextColumn Header="Name" Binding="{Binding Path=SelectedTenant.FullName, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
Edit:
I have set AutoGenerateColumns="True"
<DataGrid ItemsSource="{Binding MonthlyRecords}" AutoGenerateColumns="True">
<DataGridTextColumn Header="Name" Binding="{Binding ElementName=ComboBoxTenant, Path=DisplayMemberPath}"/>
I have a datagrid with a datagridComboBoxColumn. The items source of the datagrid is a custom class called Products which has a property called Installer (also a custom class called Contact).
I want to bind the datagridComboBoxColumn itemsSource to all the Contacts, and the selected value of the comboBox to the Installer. This is not working, could anyone please give me a hand? Thanks in advance
It would be much appreciated. I have seen other similar posts (like this one or this one ) but it's not exactly the same situation.
My xaml code:
<DataGrid x:Name="productsList" AutoGenerateColumns="False" IsReadOnly="True" CanUserResizeRows="False"
CanUserResizeColumns="True" ColumnWidth="*" GridLinesVisibility="None">
<DataGrid.Columns>
<DataGridTextColumn Header="Ref"
Binding="{Binding Ref}"
/>
<DataGridTextColumn Header="Product"
Binding="{Binding Product}"
/>
<DataGridComboBoxColumn Header="Installer" SelectedItemBinding="{Binding Installer, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Contacts}"/>
</DataGrid.Columns>
</DataGrid>
My code-behind:
public partial class CatalogPage : Page
{
ObservableCollection<CatalogProduct> mProductList = new ObservableCollection<CatalogProduct>();
public ObservableCollection<Contact> Contacts
{
get
{
return Parent.mContactsPage.GetContacts();
}
}
private LocalConfigurationPage Parent { get; set; }
public CatalogPage(LocalConfigurationPage localConfigurationPage)
{
InitializeComponent();
Parent = localConfigurationPage;
productsList.ItemsSource = mProductList;
}
}
This is the CatalogProduct class:
public class CatalogProduct
{
public string Ref { get; set; }
public string Product { get; set; }
public Contact Installer { get; set; }
}
Couple of things you have done wrong here.
Contacts is present in CatalogPage so, {Binding Contacts} wont work. This is because DataContext of a DataGridRow is the Item shown for that row. For your row, it would be CatalogProduct, and there is no Contacts there.
Instead you have to do this :
ItemsSource="{Binding DataContext.Contacts, RelativeSource={RelativeSource AncestorType=DataGrid}}
Secondly, there are known issues with DataGridComboBoxColumn, so always use this :
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Installer, UpdateSourceTrigger=PropertyChanged}}" ItemsSource="{Binding DataContext.Contacts, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
And finally, if you want to update your ComboBoxColumn with Installer value, implement change notification for Installer, and set Mode=TwoWay for SelectedItem. Otherwise, right now it will work Combobox -> Installer and not vice versa.
How to bind a WPF DataGrid to get the following result: http://i.stack.imgur.com/7Wne0.png
Note the desired +/- buttons on the left.
The DataGrid's ItemsSource is bound to an IEnumerable of Document.
The question is how to define the columns so they bind to the second and third level of objects and get the expand/collapse buttons.
I don't want to have a second and a third grids for Group and Field items, defined in the RowDetail of the parent.
The goal is to have a single DataGrid, one row of columns' headers and the ability to expand the child elements of the current row (if any).
public class Document
{
public string Name { get; set; }
public IEnumerable<Group> Groups { get; set; }
}
public class Group
{
public string Name { get; set; }
public IEnumerable<Field> Fields { get; set; }
}
public class Field
{
public string Name { get; set; }
public FieldType FieldType { get; set; }
public bool IsRequired { get; set; }
}
Just bind to the property i.e. {Binding Document.Group.Name}.
Note that all your classes must implement INotifyPropertyChanged. All your properties should raise PropertyChanged as is widely documented.
Your collections should be ObservableCollections.
The only trick i found here is to use nested datagrids with template columns and no headers for smooth view.
In Code:
DocumentGrid.ItemsSource = YourDocument;
The XAML:
<DataGrid x:Name="DocumentGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="Document"/>
<DataGridTemplateColumn Header="Group Field FieldType IsRequired">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataGrid x:Name="dgInner" ItemsSource="{Binding Groups}" AutoGenerateColumns="False" GridLinesVisibility="None" BorderThickness="0" HeadersVisibility="None">
<DataGrid.Columns>
<DataGridTextColumn Header="Group" Binding="{Binding Name}"/>
<DataGridTemplateColumn Header="Field">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataGrid x:Name="dgInnerInner" ItemsSource="{Binding Fields}" AutoGenerateColumns="False" GridLinesVisibility="None" BorderThickness="0" HeadersVisibility="None">
<DataGrid.Columns>
<DataGridTextColumn Header="Field" Binding="{Binding Name}"/>
<DataGridTextColumn Header="FieldType" Binding="{Binding FieldType}"/>
<DataGridTextColumn Header="IsRequired" Binding="{Binding IsRequired}"/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
EDIT
If you want +- buttons to collapse details you should use DataGrid's RowDetailsTemplate like described here
I have a
ObservableCollection<CustomObj> DataInfo
in my MVVM WPF project.
The CustomObj class look like this:
public class class1 : ObservableObject
{
public class1()
{
MySecondProperty = new Class2();
}
public string MyStringValue { get; set; }
public Class2 MySecondProperty { get; set; }
public List<Class3> MyThirdProperty{ get; set; }
}
When ever I bind the WPF property like this
<DataGrid Name="dgMyDataGrid"
SelectedItem="{Binding SelectedItem}"
ItemsSource="{Binding DataInfo}">
</DataGrid>
I get the value from "MyStringValue", and object and a collection in my datagrid.
Google gives me no result and I can't find anything similar to this example.
How can I get my data from Class2 and from the List in a easy way to show the data?
you need to define the columns and bind inside the column definition.
The following will show the value of MySecondProperty.SubProperty in the second column
For Class3, if you for want something like a combobox, then use a templated datagrid column
http://blogs.msdn.com/b/vinsibal/archive/2008/08/19/wpf-datagrid-stock-and-template-columns.aspx
has info on column templates
<DataGrid Name="dgMyDataGrid" SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding DataInfo}">
<DataGrid.Columns>
<DataGridTextColumn Header="MyStringValue " Width="*" Binding="{Binding Path=MyStringValue }" />
<DataGridTextColumn Header="MySecondProperty.SubProperty" Width="*" Binding="{Binding Path=MySecondProperty.SubProperty}" />
</DataGrid.Columns>
</DataGrid>