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.
Related
I have a problem trying to bind a DataGrid to a List, or to a Dictionary.
If I set DataContext to an object, and set ItemSource to a List property of that object, I get DataGrid populated with List count, in case of a List, why? How can I bind properly to a List, and how to a Dictionary?
List<string> con = new List<string>();
con.Add("aaaddd");
con.Add("bbb");
this.DataContext = con;
<DataGrid AutoGenerateColumns="True" Height="104" HorizontalAlignment="Left" Margin="34,171,0,0" Name="dg" VerticalAlignment="Top" Width="421" ItemsSource="{Binding}"/>
And I get populated with
Length
6
3
Why? And how to bind to a Dictionary?
Problem is you have set AutoGenerateColumns to True on your dataGrid.
When AutoGenerateColumns is set to true than columns are auto generated based on the properties exposed by the underlying object which is in your case is string which only exposes single property i.e. Length.
In case you want to get the value of string, you need to set that property to false and provide your own columns collection. This will work -
<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Value"
IsReadOnly="True"
Binding="{Binding}"/>
</DataGrid.Columns>
</DataGrid>
As said in the comment, I think you're refering to WPF's DataGrid Control.
If you're setting the list object to the DataContext then just ItemsSource="{Binding}" this will bind to the DataContext root which is your list object.
I have a ComboBox on a VB.Net WPF application.
<ComboBox HorizontalAlignment="Left" Margin="77,49,0,0" VerticalAlignment="Top" Width="120" ItemsSource="{Binding Path=Server.RecipeList()}" DisplayMemberPath="RecipeID" SelectedValuePath="RecipeID"/>
In the code-behind, I have a class variable called Server:
Public Server As BatchServer = New BatchServer
In my Server object, I have a List:
Private mRecipeList As New List(Of Recipe)
And my default constructor for the Server is
Public Sub New()
Server = New BatchRemote.RemoteSupport
PopulateRecipeList()
End Sub
I want to bind this List to the ComboBox, so it should display the RecipeID of each Recipe in my List. My data-binding looks as it does in the first code block, but when I run the application the ComboBox is always empty.
What am I doing wrong here?
First, you need to declare Server as a public Property that correctly implements the INotifyPropertyChanged interface. Then you need to do the same for RecipeList. Then you should be able to Bind like this:
<ComboBox HorizontalAlignment="Left" Margin="77,49,0,0" VerticalAlignment="Top"
Width="120" ItemsSource="{Binding Path=Server.RecipeList}" DisplayMemberPath="RecipeID"
SelectedValuePath="RecipeID" />
You code doesn't work because you're binding to a simple list, and so the property isnt updated when you add or remove some list element for example.
Declare the auxiliar list as an ObservableCollection read more here in the window and binding to the combo like:
Private Property auxRecipeList As New ObservableCollection(Of Recipe)
then in the xaml do the binding like this:
<ComboBox HorizontalAlignment="Left" Margin="77,49,0,0" VerticalAlignment="Top" Width="120" ItemsSource="{Binding auxRecipeList, Mode=TwoWay, UpdateSourcetrigger=PropertyChanged}" DisplayMemberPath="RecipeID" SelectedValuePath="RecipeID"/>
I'm creating a WPF data access layer that probably doesn't require full MVVM at this stage (but I might implement it).
I've successfully created a ComboBox that data binds to a foreign key value of a related table using a CollectionViewSource as the data source (See my XAML below, the combo box works fine but the TextBlock doesn't).
I only want to display the ComboBox as the cell editing template and use a TextBlock for displaying the data when it is not being edited. I can get the TextBlock to almost work (it displays data from the table related in the Foreign Key) but I can't find the equivalent property for "SelectedValuePath" so the TextBlock always displays the first value from the related table, rather than the value that corresponds to the ID in the Foreign Key field.
Is there a way (there must be) to get an equivalent behaviour from the TextBlock as I have in the ComboBox? Is there an equivalent property for SelectedValuePath?
The answer to this question would be hugely useful as there are some other fields I want to display in my data grid without providing the user any ability to edit but I still want to display a field from a related table rather than the Foreign Key ID.
Thanks so much in advance for your help and have a great day!
<CollectionViewSource x:Key="QGradeLookup"/>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Source={StaticResource QGradeLookup}, Path=QGrade}"
>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
IsEditable="False"
ItemsSource="{Binding Source={StaticResource QGradeLookup}}"
DisplayMemberPath="QGrade"
SelectedValuePath="ID"
SelectedValue="{Binding Path=OfficeQualityGradeID}"
IsSynchronizedWithCurrentItem="False"
>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
As requested here's the code for the item's source (thanks NIT):
Dim QGradeLookup As CollectionViewSource
Dim QGradeList = From q In OMRInterfaceEntities.OfficeQualityGrades
Dim QGsource = CType(Me.FindResource("QGradeLookup"), CollectionViewSource)
QGsource.Source = QGradeList.ToList()
I used Beth Massi's post as a template for the above - Beth Massi's Template
And here's the codebehind
Public Class WinPropertyDataEntry
Dim QGradeLookup As CollectionViewSource
Private Function GetOMRMarketsQuery(OMRInterfaceEntities As OMRInterfaceCustomCode.OMRInterfaceEntities) As System.Data.Objects.ObjectQuery(Of OMR.OMRInterfaceCustomCode.OMRMarket)
Dim OMRMarketsQuery As System.Data.Objects.ObjectQuery(Of OMR.OMRInterfaceCustomCode.OMRMarket) = OMRInterfaceEntities.OMRMarkets
'To explicitly load data, you may need to add Include methods like below:
'OMRMarketsQuery = OMRMarketsQuery.Include("OMRMarkets.OMRMarketType").
'For more information, please see http://go.microsoft.com/fwlink/?LinkId=157380
'Update the query to include Properties data in OMRMarkets. You can modify this code as needed.
OMRMarketsQuery = OMRMarketsQuery.Include("Properties")
'Update the query to include OMRBuildingSurveys data in OMRMarkets. You can modify this code as needed.
OMRMarketsQuery = OMRMarketsQuery.Include("Properties.OMRBuildingSurveys").Where("it.ID = 12")
'Returns an ObjectQuery.
Return OMRMarketsQuery
End Function
Private Sub Window_Loaded_1(sender As Object, e As RoutedEventArgs) Handles MyBase.Loaded
Dim OMRInterfaceEntities As OMR.OMRInterfaceCustomCode.OMRInterfaceEntities = New OMR.OMRInterfaceCustomCode.OMRInterfaceEntities()
'Load data into OMRMarkets. You can modify this code as needed.
Dim OMRMarketsViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("OMRMarketsViewSource"), System.Windows.Data.CollectionViewSource)
Dim OMRMarketsQuery As System.Data.Objects.ObjectQuery(Of OMR.OMRInterfaceCustomCode.OMRMarket) = Me.GetOMRMarketsQuery(OMRInterfaceEntities)
OMRMarketsViewSource.Source = OMRMarketsQuery.Execute(System.Data.Objects.MergeOption.AppendOnly)
Dim QGradeList = From q In OMRInterfaceEntities.OfficeQualityGrades
Dim QGsource = CType(Me.FindResource("QGradeLookup"), CollectionViewSource)
QGsource.Source = QGradeList.ToList()
End Sub
The requirement you put here can be handled in the following manner. First of all binding the TextBlock here to Collection source and giving Path is not going to give you anything.
Each row in your DataGrid represents object of type OMRBuildingSurvey which has property OfficeQualityGradeID which I assume is of type string or int. Now what you want here is whenever the ID for OfficeQualityGrade is changed, the non-editable template i.e TextBlock should display the Grade name for the selected OfficeQualityGrade.
Problem here is you are not capturing the selected OfficeGrade here. So what you need to do is to first bind SelectedItem of the Combobox to the property of type "OfficeQualityGrade" (lets say SelectedOfficeGrade). The property should raise the property change notifications. And then you can bind your textblock to this property as Text = {Binding SelectedOfficeGrade.Grade}. you will have to define this property in your entity and implement INotifyPropertychanged.
Hope this will help.
Thanks
Okay, thanks so much Nit for your help. Your solution would absolutely work in a different scenario but each building survey has it's own unique Quality Grade. The answer is that I was referencing the object incorrectly. I was using the name of the column (OfficeQualityGradeID) Rather than the name of the object OfficeQualityGrade.
The following code provides a text block for the display of Quality Grades and a Combo Box for editing Quality Grades:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding OfficeQualityGrade.QGrade}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
x:Name="QGradeSelector"
IsEditable="False"
ItemsSource="{Binding Source={StaticResource QGradeLookup}}"
DisplayMemberPath="QGrade"
SelectedValuePath="ID"
SelectedValue="{Binding Path=OfficeQualityGradeID}"
IsSynchronizedWithCurrentItem="False"
>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
how can i bind the data returning from WCF in combox in grid . the combobox is not in edit mode. I am able to display the static data but not the data returning from WCF.
u can use RelativeSource to search needed DataContext/ViewModel/Page/UserControl in the elements tree
example for the ViewModel where DataGrid itemssource defined near ComBoxCollectionSource:
<ComboBox ItemsSource={Binding RelativeSource={RelativeSource AncestorType=sdk:DataGrid}, Path=DataContext.ComboBoxColloctionSource} />
I'm assuming the static data is the stuff you enter into the Items property by hand. The syntax for a combobox is a bit different, but here it is:
<ComboBox ItemsSource="{Binding Path=<your collection>,Mode=OneTime}" SelectedValuePath="<id-field>" DisplayMemberPath="<display-field>" SelectedItem="{Binding Path=<your-property>,Mode=TwoWay}" />
The parameters ending in PATH above just have the names of the properties as a string.
EDIT: If you're using a dictionary, you would use:
<ComboBox ItemsSource="{Binding Path=<your dictionsry>,Mode=OneTime}" SelectedValuePath="Key" DisplayMemberPath="Value" SelectedItem="{Binding Path=<your-int-property>,Mode=TwoWay}" />
how do I add items to a listbox control by using < ListBox.ItemTemplate> ?
here is the xaml part:
<ListBox HorizontalAlignment="Stretch" Name="ListBox1" VerticalAlignment="Stretch" Margin="0,20">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="???????" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and here is the code for adding items:
private sub Button1_Click() Handles Button1.Click
ListBox1.Items.Add("Hello World")
End Sub
if I click on the buton1, a list item will be added with "??????" - I need to replace the "?????" with {Binding} or something so it can get the value correct value from button1_click ("Hello World")
Yes, just replace it with {Binding}, that alone should do it.
({Binding} binds to the DataContext and the DataContext of the ItemTemplate will be the respective item, if the item is a simple string as in your example that will do, if it is a complex data object you will want to specify a Path to bind to a property of the item)