Why Binding DataTable doesn't work in XAML - wpf

My MainWindow Class have a property called "DataTable":
'Table To import the Excel Sheet
Public Property DataTable As DataTable
An Excel Sheet sets the DataTable property. My question is why I cannot bind that property to a DataGrid. The following code doesn't work:
<DataGrid Name="TestGrid" Visibility="Visible" ItemsSource="{Binding Path=DataTable}">
</DataGrid>
The answer to this question seems to be unclear and doesn't explain too much about how to use the ObjectDataProvider Class.

Related

How to bind DataTable to Datagrid?

I have a problem binding a DataTable to a Gridview. I have already searched for solutions but just can't get rid of the error. The bindwing works fine when using WindowsForms, so the DataTable is correct. I just can't bind it to a WPF Gridview.
XAML:
<ListView x:Name="listView1" ItemsSource="{Binding phnDirectory}"
C#
DataTable phnDirectory = obj.Getphone_directory();
listView1.ItemsSource = phnDirectory;
But it says "can not convert datatable to Collection.IEnumerables"
so what is the solution?
thanks in advance
Use this listView1.ItemsSource = phnDirectory.DefaultView
XAML:
<ListView Name="listView1" ItemsSource="{Binding}">
C#
DataTable phnDirectory = obj.Getphone_directory();
listView1.DataContext = phnDirectory.DefaultView;
A DataTable doesn't enumerate.
This is because what do you want to enumerate ? Columns, Rows, Constraints, Properties? you either need to tell it which you want to enumerate or link to the DataView via DefaultView
e.g.
<ListView x:Name="listView1" ItemsSource="{Binding phnDirectory.DefaultView }"
or
<ListView x:Name="listView1" ItemsSource="{Binding phnDirectory.Columns}"
in windows forms a datagrid is locked into showing Tables, a WPF GridView is merely a formatting note that says you want to view the data in a grid style there is no conversion on data

Cannot bind DataGrid's ItemsSource inside WPF User Control

I am designing a UserControl that contains a datagrid. The datagrid is not displaying any rows despite my best efforts to bind it to the ItemsSource.
Here is the basic flow of the binding:
A window that contains the UserControl.
The window viewmodel contains an instance of the viewmodel designed for the user control.
I bind the UserControl's ViewModel to this viewmodel via dependency property.
The datagrid is then binded to the observable collection inside the view model, but nothing is showing.
Code (Xaml and VB.Net):
The window view model, binded to all window controls:
Public Class WindowVM
...
Public Property UserControlViewModel as New UserControlVM
End Class
Window Xaml:
<local:MyUserControl ViewModel="{Binding UserControlViewModel, Mode=OneWay}"/>
User Control code:
Public Shared ReadOnly ViewModelProperty As DependencyProperty = DependencyProperty.Register("ViewModel", GetType(UserControlVM), GetType(MyUserControl), New PropertyMetadata(Nothing))
...
Public Property ViewModel As UserControlVM
Get
Return CType(Me.GetValue(ViewModelProperty), UserControlVM)
End Get
Set(value As UserControlVM)
Me.SetValue(ViewModelProperty, value)
End Set
End Property
....
Public Class UserControlVM
Public Property RunItems As New ObservableCollection(Of RunVM)
End Class
User control xaml, datagrid binding:
<DataGrid DataContext="{Binding ViewModel}"
ItemsSource="{Binding RunItems}" ...
It seems a lot of steps, but to my knowledge this is the correct MVVM way to snake the binding to the DataGrid. So far, nothing.
Looks like I might have solved it. The DataGrid's DataContext needed to be pointed at the UserControl.
Just a simple oversight:
<DataGrid DataContext="{Binding ViewModel, RelativeSource={RelativeSource AncestorType=UserControl}}"
ItemsSource="{Binding RunItems}"

in Silverlight, filling a combobox with data from another source than the dataform?

Using Silverlight I have a dataform bound to table A and I need to fill a combobox of this dataform with data from table B. How can I declare this in the XAML code?
If your dataform is inside a UserControl which has a property called "TableBItems" for example, you could write:
<ComboBox SelectedItem={Binding FieldInTableA,Mode=TwoWay}
ItemsSource={Binding TableBItems, RelativeSource={RelativeSource AncestorType='UserControl'}}
DisplayMemberPath="FieldInTableBYouWantToShow"/>

ListBox Binding with Global Index

My application has a couple of ObservableCollections, one which is nested within an element of the other. Each contain a number of fields e.g.
ObservableCollectionA (called Listings)
Title
Description
Address
Images As MediaItems
ObservableCollectionB (called MediaItems)
ImageName
Src
DisplayTime
Currently I have been accessing ObservableCollections as follows:
Listings(0).MediaItems(0).ImageName
My application has the main Window display the items from Listings and a UserControl which contains a ListBox which displays the items from MediaItems.
Currently my Window is bound to Listings using code in the New method:
Dim AppLocal As Program = Application.CurrentItem
AppLocal.CurrentItem = 0
Me.DataContext = Listings.Item(AppLocal.CurrentItem)
For the Listings ObservableCollection, the UserControl has a XAML DataContext which references a local method which pulls the records from the nested MediaItems ObservableCollection.
<UserControl.DataContext>
<ObjectDataProvider ObjectType="{x:Type local:ThumbImageLoader}" MethodName="LoadImagesv2" IsAsynchronous="True" />
</UserControl.DataContext>
<Grid x:Name="ThumbListBoxGrid">
<ListBox x:Name="ThumbListBox" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />
</Grid>
The method is here:
Public NotInheritable Class ThumbImageLoader
Public Shared Function LoadImagesv2() As List(Of MediaItems)
Dim AppLocal As Program = Application.Current
Dim ThumbImages As New List(Of MediaItems)
ThumbImages = Listings(AppLocal.CurrentItem).MediaItems
Return ThumbImages
End Function
End Class
Whilst developing the UI layout I have just been binding the first item (0 index). I now want to be able to set AppLocal.CurrentItem from anywhere in the application so the Window and the ListBox are updated.
Ideally I would like it so when the global property index changes, the UI is updated.
How do I do this?
Is there a better way to go about it?
Ben
Ok, I discovered the joy of CollectionView. Offered exactly what I was after and was excrutiatingly easy to implement. I was blown away at not only how easy it was to implement, but I managed to cut out more lines of code than I used to implement it.
I implemented a public CollectionViewSource
Public ListingDataView As CollectionViewSource
In my main Window, I implemeted it as follows:
<CollectionViewSource x:Key="ListingDataView" />
and bound my top-level Grid to it:
<Grid DataContext="{Binding Source={StaticResource ListingDataView}}">
In my Application Startup I set the CollectionView Source
AppLocal.ListingDataView = CType(Application.Current.MainWindow.Resources("ListingDataView"), CollectionViewSource)
AppLocal.ListingDataView.Source = Listings
The next part which impressed me the most was implementing it for my User Control. I remembered the UserControl is inheriting from the main window so it has access to the CollectionView already, so I ditched the separate Class and Method binding in favour for this:
<ListBox ItemsSource="{Binding Path=MediaItems}" VerticalAlignment="Top" IsSynchronizedWithCurrentItem="True" />
Now whene I want to set the Current List Index, I simply call this:
AppLocal.ListingDataView.View.MoveCurrentToPosition(AppLocal.CurrentProperty)
A few milliseconds later, the UI updates automatically.
Done!!
When you want multiple source data (like your observable collection properties and the index for the observable collection) to a single target you should use MultiBinding.
So in your case somethign like this should help...
<ListBox x:Name="ThumbListBox" IsSynchronizedWithCurrentItem="True" >
<ListBox.ItemsSource>
<MultiBinding Converter="{StaticResource CollectionAndIndexCollaborator}">
<Binding Path="Listings" />
<Binding Path="Application.CurrentItem.CurrentItem" />
</MultiBinding>
</ListBox.ItemsSource>
</ListBox>
provided that ....
Your data context is some class that holds the Application object via a property of same name Application and the Listings collection via property of same name Listings.
Your DataContext class and Application class must have INotifyPropertyChanged implemented. It should also raise notifications for Application and Setter of CurrentItem and CurrentItem.CurrentItem properties.
CollectionAndIndexCollaborator.Convert() method returns the same indexed value as the final collection....
return ((ObservableCollection)values[0]).Item(int.Parse(values[1])) ;
where assuming MyListingType is the T of your Listings collection.
This way when anyone changes the global Application.CurrentItem.CurrentItem the multi binding above will get notified and will select the required Listings item.

WPF Binding to DataRow Columns

I've taken some sample code from http://sweux.com/blogs/smoura/index.php/wpf/2009/06/15/wpf-toolkit-datagrid-part-iv-templatecolumns-and-row-grouping/ that provides grouping of data in a WPF DataGrid. I'm modifying the example to use a DataTable instead of a Collection of entities.
My problem is in translating a binding declaration {Binding Parent.IsExpanded}, which works fine where Parent is a reference to an entity that has the IsExpanded attribute, to something that will work for my weakly typed DataTable, where Parent is the name of a column and references another DataRow in the same DataTable. I've tried declarations like {Binding Parent.Items[IsExpanded]} and {Binding Parent("IsExpanded")} but none of these seem to work.
How can I create a binding to the IsExpanded column of the DataRow Parent in my DataTable?
Thanks in advance,
Dave
EDIT: I've created some sample code for a generic case of this problem:
Window1.xaml:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfToolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
Title="Window1" Height="300" Width="300">
<Grid>
<WpfToolkit:DataGrid
Name="dgSampleData"
ItemsSource="{Binding}"
AutoGenerateColumns="True"
Margin="0,75,0,0">
<WpfToolkit:DataGrid.Columns>
<WpfToolkit:DataGridTextColumn
Header="Bound Data"
Binding="{Binding Col3.Item(0)}"
/>
</WpfToolkit:DataGrid.Columns>
</WpfToolkit:DataGrid>
</Grid>
</Window>
Window1.xaml.vb:
Imports System.Data
Class Window1
Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Dim dtSampleData As New DataTable
dtSampleData.Columns.Add("Col1")
dtSampleData.Columns.Add("Col2")
dtSampleData.Columns.Add("Col3")
dtSampleData.Rows.Add(dtSampleData.NewRow())
dtSampleData.Rows.Add(dtSampleData.NewRow())
dtSampleData.Rows(0).Item(0) = "r1c1"
dtSampleData.Rows(0).Item(1) = "r1c2"
dtSampleData.Rows(0).Item(2) = dtSampleData.Rows(0)
dtSampleData.Rows(1).Item(0) = "r2c1"
dtSampleData.Rows(1).Item(1) = "r2c2"
dtSampleData.Rows(1).Item(2) = dtSampleData.Rows(0)
dgSampleData.DataContext = dtSampleData
End Sub
End Class
I've tried using the line Binding="{Binding Col3.Item(0)}" to show the value r1c1, but nothing appears in the cell contents. Why is that? Shouldn't Item(0) be just another property of Col3?
This binding expression will bind to a column named IsExpanded on a DataTable property named BindingDataTable on the object which is set as the views DataContext.
<CheckBox IsChecked="{Binding BindingDataTable.(Rows)[0][IsExpanded]}"
Content="Test"></CheckBox>
However I am specifying the first row ([0]) of the DataTable explicitly. I've not used the WPF DataGrid before so I'm not sure at this point how to get the current row index that is being bound...I'll see what I can work out and update if found.

Resources