WPF Binding to DataRow Columns - wpf

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.

Related

Why Binding DataTable doesn't work in XAML

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.

PopulatingTextBoxes with Items from ListBox

I have a ListBox1 and a series of TextBoxes from TextBox9 onwards.I can populate the ListBox1 with Items(in sequence).I am trying to transfer those items to TextBoxes in the same order(one item for one TextBox).
I tried this code:
Dim x As Integer = ListBox1.SelectedIndex
For x = 0 To 50
For Each ListBoxItem In ListBox1.Items
ListBox1.SelectedItem = "TextBox" & (x+9) & ".Text"
Next
Next x
The code compiles but nothing happens when i click the button.Can anyone help me out?
Thanks in advance
Venkatraman
One way among others could be using the FrameworkElement.FindName() method, see also the following sample.
FWIW, in WPF using the MVVM pattern makes much more sense than one might expect at first glance, where usually a ViewModel would provide UI content among other things, and you usually would not want to do too much in the View's code-behind.
Sample.XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" SizeToContent="WidthAndHeight"
Loaded="Window_Loaded">
<StackPanel x:Name="stackPanel">
<ListBox x:Name="l1"/>
<TextBox x:Name="t1"/>
<TextBox x:Name="t2"/>
<TextBox x:Name="t3"/>
<Button Content="Test" Height="25" Click="Button_Click"/>
</StackPanel>
</Window>
Sample.VB:
Class MainWindow
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
l1.Items.Add("Item1")
l1.Items.Add("Item2")
l1.Items.Add("Item3")
l1.Items.Add("Item4")
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim i As Integer
Dim s As String
For Each item As String In l1.Items
i = i + 1
s = String.Format("t{0}", i)
Dim control = Me.FindName(s)
If TypeOf control Is TextBox Then
control.Text = item
End If
Next
End Sub
End Class

Trying to bind a list of objects to a WPF datagrid control and seeing blank lines

I am stuck and need some help. I have spent a full day trying to sort this one out. I have read a lot of examples of how this should be done and it appears that I have done it correctly but clearly I have missed something. I have a blank form with one button and one datagrid. When I click the button I want to load a list of points into the datagrid control. The data must be getting loaded because I see three blank lines in the grid (no headers), but no data. Help please! Thanks!
Class MainWindow
Class Point
Public Inc As String
Public AZ As String
Public MD As String
Public TD As String
End Class
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim mySurvey As New List(Of Point)
Dim myPoint1 As New Point
Dim myPoint2 As New Point
myPoint1.AZ = "1"
myPoint1.Inc = "2"
myPoint1.MD = "100"
myPoint1.TD = "98"
myPoint2.AZ = "10"
myPoint2.Inc = "20"
myPoint2.MD = "1000"
Point2.TD = "980"
mySurvey.Add(myPoint1)
mySurvey.Add(myPoint2)
dgSurvey.ItemsSource = mySurvey
End Sub
End Class
XAML
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Height="28" Margin="45,30,0,0" VerticalAlignment="Top" Width="57" Click="Button_Click"/>
<DataGrid x:Name="dgSurvey" HorizontalAlignment="Left" Margin="45,78,0,0" VerticalAlignment="Top" Height="172" Width="275"/>
</Grid>
For binding to work you should define properties in your Point class, not public fields.
This is explicitly stated in MSDN: Binding Sources Overview:
The properties you use as binding source properties for a binding must be public
properties of your class. Explicitly defined interface properties cannot be
accessed for binding purposes, nor can protected, private, internal,
or virtual properties that have no base implementation.
You cannot bind to public fields.

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.

Populate ComboBox based on another ComboBox using XAML

I have two ComboBoxes
<ComboBox Name="cmbMake" DisplayMemberPath="MakeName" SelectedValuePath="MakeID"/>
<ComboBox Name="cmbModel" DisplayMemberPath="ModelName"/>
I use LINQ-to-Entities to populate the cmbGroup ComboBox
Dim db as myDataEntity
cmbGroup.ItemsSource = db.Makes
How do I populate my second ComboBox (cmbModels) based on the selection of the first ComboBox (cmbMake) using XAML so that whatever I select in the first ComboBox automatically filters the ItemsSource in the second ComboBox?
Is this even possible?
I am posting the full solution here
XAML
<ComboBox Name="cmbMake" DisplayMemberPath="MakeName" SelectedValuePath="MakeID" Width="200"/>
<ComboBox Name="cmbModel" DisplayMemberPath="ModelName" DataContext="{Binding SelectedItem, ElementName=cmbMake}" Width="200"/>
CODE-BEHIND
Private Sub cmbMake_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs) Handles cmbMake.SelectionChanged
Dim myItem = From m In myModel
Where m.MakeID = cmbMake.SelectedValue
cmbModel.ItemsSource = myItem
End Sub
Whenever the value is changed in the cmbModel ComboBox it will use LINQ to reset the ItemsSource of the cmbModel ComboBox.
Many Thanks to #XAMeLi for the helping hand!
If your data is hierarchical, where each item in db.Makes holds a list of the Models (and lets say this list is in a property called MyModelsList), then:
<ComboBox Name="cmbMake" DisplayMemberPath="MakeName" SelectedValuePath="MakeID"/>
<ComboBox Name="cmbModel" DisplayMemberPath="ModelName" DataContext="{Binding SelectedItem, ElementName=cmbMake}" ItemsSource="{Binding MyModelsList}"/>
It should be possible to use a converter to filter the items, for that you can employ a MultiBinding to get the values for the items and the selection in the other box in.
Would look something like this:
<ComboBox Name="cmbModel" DisplayMemberPath="ModelName">
<ComboBox.ItemsSource>
<MutliBinding>
<MultiBinding.Converter>
<vc:MyFilterConverter/>
</MultiBinding.Converter>
<Binding Path="Items"/> <!-- This should bind to your complete items-list -->
<Binding Path="SelectedValue" ElementName="cmbMake"/>
</MutliBinding>
</ComboBox.ItemsSource>
</ComboBox>
The converter needs to implement IMultiValueConverter.

Resources