Binding ObservableCollection to DataGrid - wpf

How do I bind a ObservableCollection<updateData> updateCollection to a DataGrid? I tried several solution but none seem to work as rows are added to the collection but don't show up on the grid. I tried to bind to the class only, then I can add rows but when I try to edit them i get the error 'EditItem' is not allowed for this view. The grid is the following
<DataGrid Name="dgv" Grid.ColumnSpan="7" AutoGenerateColumns="False" ItemsSource="{Binding updateCollection}" IsSynchronizedWithCurrentItem="True" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Hour" SelectedValueBinding="{Binding Active}" ItemsSource="{StaticResource hoursList}" DisplayMemberPath="Key" SelectedValuePath="Value"/>
<DataGridComboBoxColumn Header="Origin" SelectedValueBinding="{Binding Origin}" ItemsSource="{StaticResource originList}" DisplayMemberPath="Key" SelectedValuePath="Value"/>
<DataGridTextColumn Header="P" Binding="{Binding Path=Price}"/>
<DataGridTextColumn Header="Q" Binding="{Binding Path=Quantity}"/>
</DataGrid.Columns>
And the updateData class is the following:
public class updateData
{
public string Price { get; set; }
public string Quantity { get; set; }
public string Origin { get; set; }
public string Hour { get; set; }
}

What you did looks correct, but if you miss one single thing, the DataContext… nothing will work.
Here an example just for you:
This is your Model:
public class updateData
{
public string Price { get; set; }
public string Quantity { get; set; }
public string Origin { get; set; }
public string Hour { get; set; }
}
Note that, if you want to tell to your view that something has changed, you have to implement the INotifyPropertyChanged interface.
This is your ViewModel:
public class updateDataVm
{
public ObservableCollection<updateData> updateCollection { get; set; }
public updateDataVm()
{
updateCollection = new ObservableCollection<updateData>();
}
}
And finally here is your View (note that i have changed ItemsSource to ItemsSource="{Binding}"):
<Grid>
<DataGrid Name="dgv" Grid.ColumnSpan="7" AutoGenerateColumns="False" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Hour" SelectedValueBinding="{Binding Active}" ItemsSource="{StaticResource hoursList}" DisplayMemberPath="Key" SelectedValuePath="Value"/>
<DataGridComboBoxColumn Header="Origin" SelectedValueBinding="{Binding Origin}" ItemsSource="{StaticResource originList}" DisplayMemberPath="Key" SelectedValuePath="Value"/>
<DataGridTextColumn Header="P" Binding="{Binding Path=Price}"/>
<DataGridTextColumn Header="Q" Binding="{Binding Path=Quantity}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
In your window (or generally control):
public partial class MainWindow : Window
{
public updateDataVm collection;
public MainWindow()
{
InitializeComponent();
collection = new updateDataVm();
DataContext = collection;
}
}

Related

WPF: Access SelectedItem of nested DataGrid

I have a nested DataGrid setup. How do I access the SelectedItem for the inner grid?
<DataGrid ItemsSource="{Binding OuterGrid}" RowDetailsVisibilityMode="Visible">
<DataGrid.Columns>
<DataGridCheckBoxColumn ..... />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding InnerGrid}" SelectedItem="{Binding SelectedInner}">
<DataGridTextColumn ..... />
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
SelectedInner is a property and even if I select a row in the InnerGrid in the UI, that property is still null.
How do I know which row was selected in the inner grid? I also use caliburn-micro.
It depends on where the 'SelectedInner' object is in your ViewModel. If the the object is in your ViewModel of your view (as is in the example below), you need to use RelativeResource in order to point out the datagrid where your bound object is.
class ShellViewModel:Screen
{
public BindableCollection<DataGridObject> OuterGrid { get; set; } = new BindableCollection<DataGridObject>();
public BindableCollection<DataGridObject> InnerGrid { get; set; } = new BindableCollection<DataGridObject>();
public DataGridObject selectedInner { get; set; } = new DataGridObject();
public ShellViewModel()
{
OuterGrid.Add(new DataGridObject {String1="water",String2="air",String3="earth" });
OuterGrid.Add(new DataGridObject {String1="water1",String2="air2",String3="earth2" });
OuterGrid.Add(new DataGridObject { String1 = "water3", String2 = "air3", String3 = "earth3" });
InnerGrid.Add(new DataGridObject {String1="water",String2="air",String3="earth" });
InnerGrid.Add(new DataGridObject {String1="water1",String2="air2",String3="earth2" });
InnerGrid.Add(new DataGridObject {String1="water3",String2="air3",String3="earth3" });
NotifyOfPropertyChange(() => OuterGrid);
NotifyOfPropertyChange(() => InnerGrid);
}
}
public class DataGridObject
{
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
}
On the xaml you need to use RelativeResource
<DataGrid ItemsSource="{Binding OuterGrid}" RowDetailsVisibilityMode="Visible">
<!--<DataGrid.Columns>
<DataGridCheckBoxColumn />
</DataGrid.Columns>-->
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<DataGrid ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},Path=DataContext.InnerGrid}" SelectedItem="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},Path=DataContext.selectedInner}" >
</DataGrid>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
Make sure you specify the correct ancestor which ma be window or usercontrol

Bind Unrelated property to DataGrid

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}"/>

MVVM Light DataGrid binding

I'm developing a WPF with C#, .NET Framework 4.5.1, MVVM Light framework and Entity Framework 6.1.3.
I have this DataGrid in a window:
<DataGrid x:Name="LevelConfigurationDataGrid" Grid.Column="1" Grid.Row="1" Margin="20,0" ItemsSource="{Binding LevelConfigs}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CODE_LEVE}" Header="Nivel"/>
<DataGridTextColumn Binding="{Binding CODE_NAME}" Header="Nombre"/>
<DataGridTextColumn Binding="{Binding IS_ACTIVE}" Header="¿Activa?"/>
<DataGridTextColumn Binding="{Binding CODE_TYPE}" Header="Tipo de código"/>
<DataGridTextColumn Binding="{Binding CODE_SOURCE}" Header="Origen del código"/>
<DataGridTextColumn Binding="{Binding HELPER_CODE_TYPE}" Header="Tipo de Helper Code"/>
<DataGridTextColumn Binding="{Binding HELPER_CODE_SOURCE}" Header="Origen del helper code"/>
<DataGridTextColumn Binding="{Binding QUANTITY}" Header="Cantidad"/>
<DataGridTextColumn Binding="{Binding REMAINING_CODES}" Header="Restantes"/>
<DataGridTextColumn Binding="{Binding TRZ_ENDPOINT}" Header="TRZ IP"/>
<DataGridTextColumn Binding="{Binding TRZ_ENDPORT}" Header="TRZ Puerto"/>
</DataGrid.Columns>
</DataGrid>
And on MainViewModel:
public ObservableCollection<CODE_LEVEL_CONFIGURATION> LevelConfigs
{
get { return m_LevelConfigs; }
set
{
m_LevelConfigs = value;
RaisePropertyChanged("LevelConfigs");
}
}
And on CODE_LEVEL_CONFIGURATION:
public class CODE_LEVEL_CONFIGURATION
{
public byte CODE_LEVEL { get; set; }
public string LEVEL_NAME { get; set; }
public bool IS_ACTIVE { get; set; }
public byte CODE_TYPE { get; set; }
public byte CODE_SOURCE { get; set; }
public byte? HELPER_CODE_TYPE { get; set; }
public byte? HELPER_CODE_SOURCE { get; set; }
public int QUANTITY { get; set; }
public int REMAINING_CODES { get; set; }
public string TRZ_ENDPOINT { get; set; }
public int? TRZ_ENDPORT { get; set; }
public virtual ICollection<CODES> Codes { get; set; }
public virtual ICollection<HELPER_CODES> HelperCodes { get; set; }
}
But, when I load the data on DataGrid I get more columns that I have defined on XAML. I get my columns and 13 columns more (on for each CODE_LEVEL_CONFIGURATION property).
What am I doing wrong?
Try setting AutoGenerateColumns to False
<DataGrid x:Name="LevelConfigurationDataGrid" AutoGenerateColumns="False"
Grid.Column="1" Grid.Row="1" Margin="20,0" ItemsSource="{Binding LevelConfigs}">
<DataGrid.Columns>
...
</DataGrid.Columns>
</DataGrid>

WPF DataGrid binding don't work caliburn micro

I can't understand why it does not work..
This is my class
public class Article : Screen
{
public string Code { get; set; }
public string Description{ get; set; }
public decimal Cost{ get; set; }
public decimal Price{ get; set; }
}
This is XAML code of DataGrid:
<DataGrid Height="211" HorizontalAlignment="Left"
Margin="12,31,0,0" VerticalAlignment="Top" Width="521"
AutoGenerateColumns="False" ItemsSource="{Binding List}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Code}" Header="Code" />
<DataGridTextColumn Binding="{Binding Path=Description}" Header="Description" />
<DataGridTextColumn Binding="{Binding Path=Cost}" Header="Cost" />
<DataGridTextColumn Binding="{Binding Path=Price}" Header="Price" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Button" Height="39" HorizontalAlignment="Left"
Margin="223,262,0,0" VerticalAlignment="Top" Width="110"
x:Name="AllArticles"/>
And this is my viewmodel
[Export(typeof(IShell))]
public class ArtsViewModel : Screen
{
public List<Article> List = new List<Article>();
public void AllArticles()
{
Recover recover = new Recover(); //a model called Recover
List = recover.Import().Articles; //return a List of Article
NotifyOfPropertyChange("List");
}
}
WHY THE DATAGRID DON'T WORK ?
To enable the binding conventions in Caliburn.Micro, you normally use the x:Name property in your XAML. If you, instead of explicitly binding the List property to ItemsSource of your DataGrid, use the name convention like this:
<DataGrid x:Name="List" Height="211" HorizontalAlignment="Left"
Margin="12,31,0,0" VerticalAlignment="Top" Width="521"
AutoGenerateColumns="False">
I believe the subsequent bindings should work as desired.
Oh, and you also need to make List a property instead of a field:
public List<Article> List { get; private set; }
If you want to make sure that modifications to List are properly reflected in your data grid, you should also make your List property an IObservableCollection with a backing field:
private IObservableCollection<Article> _list;
public IObservableCollection<Article> List {
get { return _list; }
set {
_list = value;
NotifyOfPropertyChange(() => List);
}
}
a) Did you set the DataContext?
b) Try "Binding="{Binding Code}" (Without Path=), works fine for me.
For my UWP app I found that the x:Name convention didn't work and I need to use a bindable collection and bind directly to ItemsSource. I then used Path=PropertyName to specify the columns.
XAML:
<controls:DataGrid Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" ItemsSource="{Binding RawCSVData}" AutoGenerateColumns="false">
<controls:DataGrid.Columns>
<controls:DataGridTextColumn Header="Time" Binding="{Binding Path=Time}"/>
<controls:DataGridTextColumn Header="Signal" Binding="{Binding Path=Signal}"/>
<controls:DataGridTextColumn Header="Response" Binding="{Binding Path=Response}"/>
</controls:DataGrid.Columns>
</controls:DataGrid>
CSVData.cs:
public class CSVData: Screen
{
public double Time { get; set; }
public double Signal { get; set; }
public double Response { get; set; }
}
ViewModel.cs snippet:
public BindableCollection<CSVData> RawCSVData { get; set; }
private void _bookeditpage_ConfirmBook(Book _book, bool _isnewone)
{
if (_isnewone)
{
Books.Add(_book);
}
else
{
//doesn't refresh datagrid
SelectedBook = _book;
//
//works fine :)
SelectedBook.BookName = _book.BookName;
SelectedBook.IsPrenum=_book.IsPrenum;
SelectedBook.Publisher=_book.Publisher;
//
}
}

WPF DataGridCheckColumn, check checkbox after selection changed

I have DataGrid like:
<DataGrid Name="paragonyDG" AutoGenerateColumns="False" CanUserAddRows="False" ItemsSource="{Binding zaznaczone}" >
<DataGrid.Columns>
**<DataGridCheckBoxColumn Header="Zaznacz" Binding="{Binding check}"></DataGridCheckBoxColumn>**
<DataGridTextColumn Header="Nazwa paragonu" Binding="{Binding nazwa}"></DataGridTextColumn>
<DataGridTextColumn Header="Wystawiony przez" Binding="{Binding osoba}"></DataGridTextColumn>
<DataGridTextColumn Header="Kwota paragonu" Binding="{Binding kwota}"></DataGridTextColumn>
<DataGridTextColumn Header="Rabat" Binding="{Binding rabat}"></DataGridTextColumn>
<DataGridTextColumn Header="Data otwarcia" Binding="{Binding dataO}"></DataGridTextColumn>
<DataGridTextColumn Header="Data zamknięcia" Binding="{Binding dataZ}"></DataGridTextColumn>
<DataGridTextColumn Header="Formy płatności" Binding="{Binding formy}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
And its bind into class :
public class zaznaczone
{
public bool check { get; set; }
public int docID { get; set; }
public string nazwa { get; set; }
public string osoba { get; set; }
public decimal kwota { get; set; }
public decimal rabat { get; set; }
public string dataO { get; set; }
public string dataZ { get; set; }
public string formy { get; set; }
}
Now, in datagrid.selectionchanged i'd like to check this checkbox.
I've tried to parse selectedItem.columns[0] to checkbox, but i cannot parse datagridrow to "zaznaczone".
How can i check checkbox?
Specify SelectedItem property of DataGrid: SelectedItem="{Binding zaznaczoneItem}". After that you can modify check property of zaznaczoneItem.
Please note that your DataContext should implement INotifyPropertyChanged. I recommend you to look on MVVMLight framework

Resources