I am trying to data-bind a List to a DataGrid.
So far it seems to work only one way - when I manually change something entered in the displayed DataGrid, the List gets updated, but not vice-versa (DataGrid does not change when I change something in the List).
This is my DataGrid:
<DataGrid x:Name="MyDataGrid" AutoGenerateColumns="false" ItemsSource="{Binding}">
<DataGrid.Columns>
<DataGridTextColumn x:Name="ColName" Binding="{Binding Name}" Header="Name" />
<DataGridTextColumn x:Name="ColProperty" Binding="{Binding MyProperty}" Header="My Property" />
</DataGrid.Columns>
</DataGrid>
I enter my data as follows:
Public Class Res
Public Shared TableData As New List(Of DataItem)
End Class
Public Class DataItem
Public Property Name() As String
Public Property MyProperty() As String
End Class
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim entry As New DataItem
entry.Name = "Test Name"
entry.MyProperty = "Test Property"
Res.TableData.Add(entry)
Me.MyDataGrid.ItemsSource = Res.TableData
End Sub
But then, when I try to change my data, by doing something like:
Res.TableData.Item(0).Name = "Changed"
it does not work, the value displayed in the DataGrid does not change.
Why is this? How can I update my DataGrid?
If there is a better way to bind data to DataGrid, I am open for suggestions.
After some more searching, found a solution, that worked.
What I did, I added:
Me.MyDataGrid.Items.Refresh()
after I changed the value in the List.
Thanks.
Related
I'm having two classes Cricket, Football and List of Observable collections of type object. Based on certain condition I want to add the object of type Cricket/Football to Observable Collection. I'm not assigning any data i.e just creating and instance of class Cricket/Football and adding that instance to Observable Collections and binding to UI. My expectation is, as I'm not assigning any data to the instance of Cricket/Football, only header has to create in the datagrid. But what I found was a row with the default value of the variables defined under the respective class along with the row header as I'm creating the instance of that class. How shall I avoid creating void row where my datagrid header is unaffected.
<DataGrid SelectionMode="Single" VerticalAlignment="Stretch" ItemsSource="{Binding itemSource, UpdateSourceTrigger=PropertyChanged}" CanUserReorderColumns="False" CanUserAddRows="False" IsReadOnly="True" CanUserDeleteRows="False" CanUserResizeColumns="False" HorizontalGridLinesBrush="Black" VerticalGridLinesBrush="Black" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled" />
Edit
I think your datagrid's CanUserAddRows property is set to true. Just set it to false to fix your issue.
CanUserAddRows="false"
IsReadOnly="True"
EDIT :
Sorry. I read your question properly right now. It’s because the ObservableCollection’s type is object. I will show you a small sample so that you understand how binding works in a datagrid. Your collection should have public properties, so datagrid could bind columns to it. If you use collection type of Object than you have no properies for binding, so the empty rows will be displayed.
XAML :.
<DataGrid AutoGenerateColumns="False" Height="253" HorizontalAlignment="Left" Margin="27,24,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="448">
<DataGrid.Columns>
<DataGridTextColumn Header="First" Binding="{Binding Path=Field, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
Code behind :
public partial class MainWindow : Window
{
public ObservableCollection dataSource;
public MainWindow()
{
InitializeComponent();
this.dataSource = new ObservableCollection<SomeDataSource>();
this.dataSource.Add(new SomeDataSource { Field = "123" });
this.dataSource.Add(new SomeDataSource { Field = "1234" });
this.dataSource.Add(new SomeDataSource { Field = "12345" });
this.dataGrid1.ItemsSource = this.dataSource;
}
}
public class SomeDataSource
{
public string Field {get;set;}
}
SOLVED Thanks to Clemens. I just needed to add the "Property" attribute after the word "Public" in my class definition:
In VB.NET I'm trying to make a very simple Datagrid example work (i.e. an example without a database connection, etc). But no matter what I try, the Datagrid always displays the correct number of rows and the correction number of columns (and the correct column headers), but the cells are empty:
Public Class GeneratedImage
Public Property AssignmentId As Integer
Public Property DocumentPageNumber As Integer
Public Property FullPathToImage As String
Public Sub New(id As Integer, pg As Integer, docpath As String)
AssignmentId = id
DocumentPageNumber = pg
FullPathToImage = docpath
End Sub
End Class
Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Dim myList As New List(Of GeneratedImage)
myList.Add(New GeneratedImage(1, 1, "c:\nowhere"))
myList.Add(New GeneratedImage(2, 2, "c:\nowhere2"))
GeneratedImagesInformationDatagrid.ItemsSource = myList
' GeneratedImagesInformationDatagrid.DataContext = myList
end sub
Then, in the XAML, I bind as follows:
<DataGrid x:Name="GeneratedImagesInformationDatagrid"
Height="500"
VerticalAlignment="Top"
ItemsSource="{Binding}"
Width="300">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=DocumentPageNumber}"
Header="Pg" />
<DataGridTextColumn Binding="{Binding Path=AssignmentId}"
Header="Assignment Id" />
<DataGridTextColumn Binding="{Binding Path=FullPathToImage}"
Header="Full Path To Image" />
</DataGrid.Columns>
</DataGrid>
I've tried it with and without the "ItemsSource="{Binding}" field.
Any help appreciated!
SOLVED Thanks to Clemens. I just needed to add the "Property" attribute after the word "Public" in my class definition:
Public Class GeneratedImage
Public Property AssignmentId As Integer
Public Property DocumentPageNumber As Integer
Public Property FullPathToImage As String
.
. etc
etc...
i have a DataGridTemplateColumn that i created, nothing much
Public Class DataGridAutoCompleteColumn
Inherits DataGridTemplateColumn
Protected Overrides Function PrepareCellForEdit(editingElement As FrameworkElement, editingEventArgs As RoutedEventArgs) As Object
editingElement.MoveFocus(New TraversalRequest(FocusNavigationDirection.First))
Return MyBase.PrepareCellForEdit(editingElement, editingEventArgs)
End Function
End Class
i also have an Auto complete control (which works very well)
and i am using it inside this column like this
<DataGrid.Resources>
<DataTemplate x:Key="EditingDateTemplate">
<Auto:AutoCompleteTextBox FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
x:Name="EditingBox"
Text="{Binding Path=Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Source={StaticResource MyComplList}}"/>
</DataTemplate>
<DataTemplate x:Key="NormalCellDataTemplate">
<TextBlock Text="{Binding Path=Name,UpdateSourceTrigger=PropertyChanged,FallbackValue=Add New ...}"/>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<Auto:DataGridAutoCompleteColumn
CellTemplate="{StaticResource NormalCellDataTemplate}"
CellEditingTemplate="{StaticResource EditingDateTemplate}"
Header="Test Auto Complete"/>
</DataGrid.Columns>
the focusing mechanism and every thing works well , but i am facing one problem
"Editing"
When double clicking the cell , it goes into Edit mode BUT when i start typing without double clicking it doesn't (not like what the DataGridTextColumn does)
so i made a workaround for this in the keyup event within the datagrid
Private Sub MyDataGrid_KeyUp(sender As Object, e As KeyEventArgs)
Dim ignorablekeys() As Key = {Key.Up, Key.Down, Key.Left, Key.Right, Key.Delete, Key.Return, Key.LeftShift, Key.RightShift, Key.LeftCtrl, Key.RightCtrl, Key.LeftAlt, Key.RightAlt}
If Not ignorablekeys.Contains(e.Key) Then
If TypeOf e.OriginalSource Is DataGridCell Then
Dim cell = DirectCast(e.OriginalSource, DataGridCell)
If Not cell.IsReadOnly Then
Dim datagrid = DirectCast(sender, DataGrid)
datagrid.BeginEdit()
'Push the entered Text Into the cell <<<<< Problem >>>>>
Dim e1 = New KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key)
e1.RoutedEvent = KeyUpEvent
InputManager.Current.ProcessInput(e1)
End If
End If
End If
End Sub
the problem is , it doesn't work , it doesn't send the text
I am using Silverlight Application in that I am using Datagrid and binding Data based on Observable Collection, but when I am trying to Bind the Observable Collection to Dropdown it's not binding, do we need to write code to Bind in the xaml Code behind.
My Code :
<sdk:DataGridTemplateColumn Header="lab Validated?" CanUserSort="True">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox Height="Auto" HorizontalAlignment="Left"
Name="cboLabValidated" VerticalAlignment="Center" Width="80"
ItemsSource="{Binding Path=LabValidatedList}">
</ComboBox>
</Grid>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
ViewModel :
Public LabValidatedList As New List(Of String)() From { _
"Yes", _
"No"
}
In order to be usable as the source of a binding, LabValidatedList has to be a public property, not a field:
Private labValidatedListValue As New List(Of String)() From { _
"Yes", _
"No"
}
Public Property LabValidatedList() As List(Of String)
Get
Return Me.labValidatedListValue
End Get
Set(ByVal value As List(Of String))
Me.labValidatedListValue = value
End Set
End Property
Sorry if the above does not compile immediately, but VB is not my language. Note also that a List(Ofd String) is not an ObservableCollection.
I've encountered a problem when converting a WPF project from vs2008 to vs2010.
I have a DataGrid that contains a ListBox. Each ListBoxItem has a Label and a Button. After converting to vs2010 the button no longer renders but crashes the app as soon as it comes into view. (Ie. the app loads but when the ListBox is created I get a NullReferenceException. What does work though is to remove the click event from the button and then it renders fine :) Same type of setup with Button within ListBoxItem also works when not inside a DataGrid. The content of ListBox obviously is meant to be dynamic but when working with a static collection I get the same error. Also removing the CommandParam does not help at all. Any pointers most welcome.
Code:
<DataGrid x:Name="DgTest" AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<ListBox ItemsSource="{Binding ItemList}">
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel Style="{StaticResource hzp}">
<Label />
<Button Click="Button_Click" Content="TestButton"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Code-behind:
Imports System.Collections.ObjectModel
Class MainWindow
Public TestList As New ObservableCollection(Of TestClass)
Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
DgTest.ItemsSource = TestList
TestList.Add(New TestClass(0))
TestList.Add(New TestClass(1))
End Sub
Public Class TestClass
Private _ItemList As New List(Of String)
Private _id As Integer
Public Property ItemList() As List(Of String)
Get
Return _ItemList
End Get
Set(ByVal value As List(Of String))
_ItemList = value
End Set
End Property
Public Property Id() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
Public Sub New(ByVal id As Integer)
_ItemList.Add("String1")
_id = id
End Sub
End Class
Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
End Sub
End Class
And in App Resources:
<Style TargetType="StackPanel" x:Key="hzp">
<Setter Property="Orientation" Value="Horizontal"/>
<Setter Property="Background" Value="Orange"/>
</Style>
Now here's the strange thing. If the Stackpanel Style is removed, the button will work. If the Click event for the button is removed, it will load normally.
Seems like your event handler is gone from the code-behind file, check that first. Comment if that is not the case.
I believe I have found the answer to my own question. In the ListBox bound to an ObservableCollection all Styles must be DynamicResource. Using StaticResource worked well in 3.5 but not 4! Took a few hours of randomly testing everything to find this. Case closed