WPF - Combobox does not show selecteditem - wpf

Im new to WPF and VB and im having some trouble figuring out why the thing i select in a ComboBox dropdown does not show in the ComboBox after selection.
I have my ComboBox populated through bindings and DataContext. This is my Settings.xaml file
<Window.Resources>
<DataTemplate x:Key="TabList">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Header}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
...
<ComboBox x:Name="tabs"
ItemsSource="{Binding tabList}"
ItemTemplate="{StaticResource ResourceKey=TabList}"
Height="32" />
The views codebehind file (Settings.xaml.vb) then loads the ComboBoxes content in the class' constructor, and the data does show in the ComboBox
Public Class Settings
Private loader As SettingsLoader
Sub New()
InitializeComponent()
Dim sh As New SettingsHandler(True)
loader = New SettingsLoader
loader.tabList = sh.Current.Tabs
DataContext = loader
End Sub
End Class
The SettingsLoader class looks like so. TRTab is my own class that simply inherits from TabItem and only adds a few extra properties, nothing fancy
Public Class SettingsLoader
Private _tabs As List(Of TRTab)
Public Property tabList() As List(Of TRTab)
Get
Return _tabs
End Get
Set(value As List(Of TRTab))
_tabs = value
End Set
End Property
End Class
Do i need to add a property to my SettingsLoader that holds the selected item for the ComboBox to show or what am i missing ?
EDIT: Just to clarify what im trying to achieve: I have a TabControl with a number of tabs. Those tabs' Headers needs to be also shown in a ComboBox for selection

Because TabItem is a ContentControl the ComboBox will display its Content when the item is selected. You could confirm this yourself using the following XAML markup:
<Window.Resources>
<DataTemplate x:Key="TabList">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Header}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ComboBox x:Name="tabs" Height="32"
ItemTemplate="{StaticResource TabList}">
<TabItem Content="Content" Header="Header" />
</ComboBox>
</StackPanel>
When you open the dropdown, you will see "Header" but when you select the item and close the dropdown you will see "Content".
It generally doesn't make a whole much sense to set the ItemsSource property of a ComboBox to an IEnumerable of ContentControls. You could bind the ItemsSource to an IEnumerable(Of String) instead. Just add another property to your SettingsLoader class:
Public Class SettingsLoader
Private _tabs As List(Of TRTab)
Public Property tabList() As List(Of TRTab)
Get
Return _tabs
End Get
Set(value As List(Of TRTab))
_tabs = value
End Set
End Property
Public ReadOnly Property tabHeaders() As IEnumerable(Of String)
Get
If _tabs Is Nothing Then
Return Nothing
End If
Return _tabs.Select(Function(x) x.Header.ToString())
End Get
End Property
End Class
<Window.Resources>
<DataTemplate x:Key="TabList">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ComboBox x:Name="tabs"
ItemsSource="{Binding tabHeaders}"
ItemTemplate="{StaticResource ResourceKey=TabList}"
Height="32" />
</StackPanel>
The other option is to set the Content properties of your TRTab objects to the same values ass their Header properties.

Related

What is wrong with this WPF binding?

The instance of the class is a private member of the view code exposed as a public property named "ViewModel".
You are setting the DataContext of the Grid to a string equal to "ViewModel". You need to make sure the DataContext property is correctly set to actual ViewModel object instance, either with a binding or via code behind.
For more information, see my answer to the question What is DataContext for?
I'm agree with the Rachel's answer. An easy way to set the DataContext of your Grid could be this:
<Window.Resources>
<YourNamespace:ViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource ViewModel}">
<TextBox Text="{Binding Path=TestName}" Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="TextBox1" VerticalAlignment="Top" Width="479" />
</Grid>
This way you don't need to touch the code behind of your Window/UserControl.
If you don't want to change the code in your view and and want to keep your ViewModel property, then you could also do this:
Public Class View Inherits Window
Private m_ViewModel As ViewModel
Public Property ViewModel() As ViewModel
Get
Return m_ViewModel
End Get
Set
m_ViewModel = Value
End Set
End Property
Public Sub New()
InitializeComponent()
ViewModel = New ViewModel()
DataContext = ViewModel
End Sub
End Class
So you don't need to set the DataContext in your view, just do this:
<Grid>
<TextBox Text="{Binding Path=TestName}" Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="TextBox1" VerticalAlignment="Top" Width="479" />
</Grid>

WPF binding to parent ItemsSource DataContext from DataTemplate item

I have a ViewModel named "A", containing a property of type ObservableCollection(Of ChannelViewModel). The Model is actually inside this ChannelViewModel class and I'm ok with that.
In the View "A", I have a stackpanel with ItemsSource filled with the ObservableCollection. I added a DataTemplate to show a custom control "Channel" instead of the ChannelViewModel string. The Channel custom control needs to display ChannelViewModel data.
The problem is the DataContext of the Channel is not connected properly to each items of the list. I tried a bunch of things and nothing seems to work. I would appreciate some help please!
Here is the "A" View code:
<Window.DataContext>
<ctrls:AViewModel/>
</Window.DataContext>
<StackPanel x:Name="uiStack" Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding Channels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ctrls:Channel DataContext="{Binding DataContext}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
Here is "A" ViewModel :
Private aChannels As New ObservableCollection(Of ChannelViewModel)
Property Channels As ObservableCollection(Of ChannelViewModel)
Get
Return aChannels
End Get
Set(value As ObservableCollection(Of ChannelViewModel))
aChannels = value
OnPropertyChanged("Channels")
End Set
End Property
Public Sub New()
AddChannels()
OnPropertyChanged("Channels")
End Sub
Private Sub AddChannels()
For i As Integer = 1 To DeviceConfig.Channels.Count
Channels.Add(New ChannelViewModel(i))
Next
End Sub
The Channel UserControl is (simplified):
<UserControl.DataContext>
<ctrls:ChannelViewModel />
</UserControl.DataContext>
<StackPanel Orientation="Horizontal">
<Label x:Name="lblChannelNo" Content="{Binding ChannelNo}" />
<Label x:Name="lblChannelName" Content="{Binding ChannelName}" />
</StackPanel>
And Channel ViewModel (simplified):
Public Class ChannelViewModel
Inherits ViewModelBase
Private aChannelNo As Integer = 0
Property ChannelNo As Integer
Get
Return aChannelNo
End Get
Set(value As Integer)
aChannelNo = value
OnPropertyChanged("ChannelNo")
End Set
End Property
Private aChannelName As String = "N/A"
Property ChannelName As String
Get
Return aChannelName
End Get
Set(value As String)
aChannelName = value
OnPropertyChanged("ChannelName")
End Set
End Property
Okay, the problem was in the Channel View. I think setting the DataContext was overwriting the context from the "A" View. So removing these lines solved the issue:
<UserControl.DataContext>
<ctrls:ChannelViewModel />
</UserControl.DataContext>
I also had to change the binding to :
<DataTemplate>
<ctrls:Channel DataContext="{Binding}" />
</DataTemplate>

WPF Multicolumn Combobox - Binding value is whole POCO instead of a single POCO property

I built a multicolumn combobox in WPF following this SO post: WPF ComboBox Multiple Columns
(Please excuse the mass of code samples ;))
The Combobox XAML
<ComboBox Name="cmbProductTypeMulti" IsEditable="False" Margin="0,2,10,2" MaxDropDownHeight="250"
Text="{Binding Path=AcctData.ProductType}" ItemsSource="{Binding Path=ProductTypeSelection}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Margin="2" Text="{Binding Path=ProductType}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem" BasedOn="{StaticResource ComboBoxItemStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Name="ComboBoxItemBorder" BorderThickness="1">
<Grid Name="ComboBoxItemGrid" TextElement.Foreground="Black">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="5,3" Grid.Column="0" Text="{Binding ProductType}"/>
<TextBlock Margin="5,3" Grid.Column="1" Text="{Binding Description}" FontSize="10"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!--- snip --->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
ViewModel snippet
Public Class AccountsViewModel
Inherits ViewModelBase
//The view model's main Poco
Dim _AcctData As Models.Account
//The multicolumn combobox's Poco
Dim _ProductTypeSelection As IEnumerable(Of Models.ProductType)
Public Property AcctData As Account
Get
Return _AcctData
End Get
Set(value As Account)
MyBase.Set(Of Account)(Function() AcctData, _AcctData, value)
End Set
End Property
Public Property ProductTypeSelection As IEnumerable(Of ProductType)
Get
Return _ProductTypeSelection
End Get
Set(value As IEnumerable(Of ProductType))
MyBase.Set(Of IEnumerable(Of ProductType))(Function() ProductTypeSelection, _ProductTypeSelection, value)
End Set
End Property
...
End Class
View's Poco snippet
Public Class Account
Inherits ObservableObject
Private _ProductType As String
Public Property ProductType As String
Get
Return _ProductType
End Get
Set(value As String)
MyBase.Set(Of String)(Function() ProductType, _ProductType, value)
End Set
End Property
...
End Class
Combobox's Poco
Public Class ProductType
Inherits ObservableObject
Private _ProductType As String
Private _Description As String
Public Property ProductType As String
...
End Property
Public Property Description As String
...
End Property
...
End Class
The Problem
First off, the when I load an account into the view model's AcctData.ProductType, the value is not displayed in the multicolumn combobox. I have a regular combobox bound to the same value which displays AcctData.ProductType initial value properly.
Secondly, when I select an item from the multicolumn combobox, the regular combobox loses its selected item and goes blank. When I go into debug and look at the vm's AcctData.ProductType, I find that it has been assigned the ToString value of the ProductType poco.
So it looks like the multicolumn combobox is trying to use the whole Poco to bind with. How can I get it use a property from a Poco as its binding value?
Thanks
The first problem would probably be solved if you would call the ProductTypeSelection setter from the viewmodel's constructor to initialize the data, because currently it doesn't get set en thus won't raise the propertychanged event, currently your binding only knows about the initial data which is the default null
the second problem is probably caused because the xaml is taken the datatemplate over the controltemplate you have defined and would probably be solved if you put the xaml thats inside the controltemplate inside of the datatemplate
I managed to find a hacky work around.
Since the multicolumn combobox is binding using the Poco's ToString() value, I simply modified ToString() to return the ProductType property, which is the property that I want to be bound! Now the multicolumn combobox behaves as it should - even though its technically binding to the wrong property.

binding ComboBox to DataGrid in the code

Hi I am trying to dynamically bind ComboBox in the DataGrid inside the code. I saw a few answers related to this but none of them were helpful. The general opinion is to use the DataTempleteColumn but that also gives no result. Here is my code
<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 Name="ButFill" Content="Fill Grid" HorizontalAlignment="Left" Height="22" Margin="373,65,0,0" VerticalAlignment="Top" Width="62"/>
<DataGrid x:Name="DaGrid" HorizontalAlignment="Left" Height="134" Margin="25,38,0,0" VerticalAlignment="Top" Width="289" ItemsSource="{Binding}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="text" Binding="{Binding Path=col1}"/>
<DataGridComboBoxColumn Header="combobox" Width="105" ItemsSource="{Binding Path=fill_items}"/>
<DataGridTemplateColumn Header="template combo" Width="105">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="TempCombo" ItemsSource="{Binding Path=fill_items}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
And the code behind is
Imports System.Collections.ObjectModel
Class MainWindow
Public Property Table As New ObservableCollection(Of Test.dataset)()
Public Property fill_items As New ObservableCollection(Of String)
Private Sub ButFill_Click(sender As Object, e As RoutedEventArgs) Handles ButFill.Click
Dim temp As New Test.dataset()
Dim cb As New ComboBox
fill_items.Add("ItemNo1")
fill_items.Add("ItemNo2")
cb.ItemsSource = fill_items
temp.col1 = " Hello"
temp.col2 = cb
temp.col3 = cb
Table.Add(temp)
DaGrid.DataContext = Table
End Sub
End Class
Public Class dataset
Public Property col1 As String
Public Property col2 As ComboBox
Public Property col3 As ComboBox
End Class
The problem I see are:
1) The DataGridComboBox Column dosen't show the it until entered in the edit mode.
2) Both the Combobox are empty, but the Collections "Table" seems to have a combobox.count of 2.
Am I doing anything wrong?
Could somebody show me a proper complete example of binding ComboBox ?
fill_items needs to be a public property in your dataset class, because bindings in
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="TempCombo" ItemsSource="{Binding Path=fill_items}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
refer to each item of your bound collection. In your case it's a dataset item.
You can't just simply use RelativeSource or ElementName to find the DataGrid and bind to that, because DataGridColumns won't be added to the visual tree, hence those bindings can't work.
If you want to show fill_items in combobox which is in the DataContext of the Window, then you will need to update your comboboxcolumn binding to use the BindingProxy because DataGrid columns are not in the Visualtree of the datagrid.
<Window.Resources>
<local:MyBindingProxy x:Key="myproxy" BindingData="{Binding}" />
</Window.Resources>
and you have to set the DataContext of the Mainwindow to itself like DataContext =this; in constructor
then you need to write the class for MyBindingProxy like:
public class MyBindingProxy : Freezable
{
public static readonly DependencyProperty BindingDataProperty =
DependencyProperty.Register("BindingData", typeof(object),
typeof(MyBindingProxy ), new UIPropertyMetadata(null));
protected override Freezable CreateInstanceCore()
{
return new MyBindingProxy();
}
public object BindingData
{
get { return (object)GetValue(BindingDataProperty ); }
set { SetValue(BindingDataProperty , value); }
}
}
Then you can update binding as:
<DataGridComboBoxColumn Header="combobox" Width="105" ItemsSource="{Binding BindingData.fill_items, Source={StaticResource myproxy}}"/>

binding path in silverlight treeview

my binded treeview work fine with string property but not with a "property of another property".
My code:
Public class A
Public data as string
End Class
Public Class T
Public o As A
Public ReadOnly Property desc As String
Get
Return o.data
End Get
End Property
Property children As New ObservableCollection(Of T)()
End Class
xaml that work:
<sdk:HierarchicalDataTemplate x:Key="NameTemplate"
ItemsSource="{Binding Path=children}" >
<TextBlock Text="{Binding Path=desc}" FontWeight="Bold" />
</sdk:HierarchicalDataTemplate>
xaml that not work:
<sdk:HierarchicalDataTemplate x:Key="NameTemplate"
ItemsSource="{Binding Path=children}" >
<TextBlock Text="{Binding Path=o.data}" FontWeight="Bold" />
</sdk:HierarchicalDataTemplate>
What is the mistake?
thanks.
You can only bind to properties in silverlight. Your o is not a property at the moment, you need to define setters and getters.
I'm not familiar with vb so I'm not sure what the correct syntax is. Either define it as a property or define set and get for o.

Resources