binding path in silverlight treeview - silverlight

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.

Related

WPF - Combobox does not show selecteditem

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.

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 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 with Static properties in ListView ItemTemplate

I'm having some problems with WPF binding.
I have an assembly with some const properties in class Values, that correspond to columns from datatable.
I want to bind the value from a column to a TextBlock using the const property to specify the column at a ListView ItemTemplate like shown in the code:
xmlns:C="clr-namespace:WPFApplication1.Entities;assembly=WPFApplication1">
<Grid>
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding {x:Static C:Values.FieldCode}}" /> /*<- Don't work*/
/*Works like this: <TextBlock Text="{Binding [CODE]}" />*/
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
If I use binding with the static property I I'm not able to show the value in the datarow but if I use the Binding like this [CODE] I'm able to show the value.
What is appening?
Any clue?
Thanks in advance.
You need to use your static property as the Source, not the Path, which is the default attribute for Binding:
{Binding Source={x:Static C:Values.FieldCode}}
the italic text is not correct, please read from EDIT1:
It is not possible to bind to static properties. Binding always needs an instance of the Class. It is possible by instantiating the class as resource of in the code behind and set that class as the datacontext
EDIT1:
Add a static property of type
public static string FieldCode = "Code";
public static PropertyPath FieldCodePath = new PropertyPath(FieldCode);
Change the Binding to the binding below:
<TextBlock Text="{Binding Path={x:Static C:Values.FieldCodePath}, IsAsync=true}" />
I hope this helps

WPF binding to Listbox selectedItem

Can anyone help with the following - been playing about with this but can't for the life of me get it to work.
I've got a view model which contains the following properties;
public ObservableCollection<Rule> Rules { get; set; }
public Rule SelectedRule { get; set; }
In my XAML I've got;
<ListBox x:Name="lbRules" ItemsSource="{Binding Path=Rules}"
SelectedItem="{Binding Path=SelectedRule, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:" />
<TextBox x:Name="ruleName">
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged" />
</TextBox.Text>
</TextBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Now the ItemsSource works fine and I get a list of Rule objects with their names displayed in lbRules.
Trouble I am having is binding the SelectedRule property to lbRules' SelectedItem. I tried binding a textblock's text property to SelectedRule but it is always null.
<TextBlock Text="{Binding Path=SelectedRule.Name}" />
The error I'm seeing in the output window is:
BindingExpression path error: 'SelectedRule' property not found.
Can anyone help me with this binding - I can't see why it shouldn't find the SelectedRule property.
I then tried changing the textblock's text property as bellow, which works. Trouble is I want to use the SelectedRule in my ViewModel.
<TextBlock Text="{Binding ElementName=lbRules, Path=SelectedItem.Name}" />
Thanks very much for your help.
First off, you need to implement INotifyPropertyChanged interface in your view model and raise the PropertyChanged event in the setter of the Rule property. Otherwise no control that binds to the SelectedRule property will "know" when it has been changed.
Then, your XAML
<TextBlock Text="{Binding Path=SelectedRule.Name}" />
is perfectly valid if this TextBlock is outside the ListBox's ItemTemplate and has the same DataContext as the ListBox.
Inside the DataTemplate you're working in the context of a Rule, that's why you cannot bind to SelectedRule.Name -- there is no such property on a Rule.
To bind to the original data context (which is your ViewModel) you can write:
<TextBlock Text="{Binding ElementName=lbRules, Path=DataContext.SelectedRule.Name}" />
UPDATE: regarding the SelectedItem property binding, it looks perfectly valid, I tried the same on my machine and it works fine. Here is my full test app:
XAML:
<Window x:Class="TestWpfApplication.ListBoxSelectedItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxSelectedItem" Height="300" Width="300"
xmlns:app="clr-namespace:TestWpfApplication">
<Window.DataContext>
<app:ListBoxSelectedItemViewModel/>
</Window.DataContext>
<ListBox ItemsSource="{Binding Path=Rules}" SelectedItem="{Binding Path=SelectedRule, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:" />
<TextBox Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
Code behind:
namespace TestWpfApplication
{
/// <summary>
/// Interaction logic for ListBoxSelectedItem.xaml
/// </summary>
public partial class ListBoxSelectedItem : Window
{
public ListBoxSelectedItem()
{
InitializeComponent();
}
}
public class Rule
{
public string Name { get; set; }
}
public class ListBoxSelectedItemViewModel
{
public ListBoxSelectedItemViewModel()
{
Rules = new ObservableCollection<Rule>()
{
new Rule() { Name = "Rule 1"},
new Rule() { Name = "Rule 2"},
new Rule() { Name = "Rule 3"},
};
}
public ObservableCollection<Rule> Rules { get; private set; }
private Rule selectedRule;
public Rule SelectedRule
{
get { return selectedRule; }
set
{
selectedRule = value;
}
}
}
}
Yocoder is right,
Inside the DataTemplate, your DataContext is set to the Rule its currently handling..
To access the parents DataContext, you can also consider using a RelativeSource in your binding:
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ____Your Parent control here___ }}, Path=DataContext.SelectedRule.Name}" />
More info on RelativeSource can be found here:
http://msdn.microsoft.com/en-us/library/system.windows.data.relativesource.aspx
For me, I usually use DataContext together in order to bind two-depth property such as this question.
<TextBlock DataContext="{Binding SelectedRule}" Text="{Binding Name}" />
Or, I prefer to use ElementName because it achieves bindings only with view controls.
<TextBlock DataContext="{Binding ElementName=lbRules, Path=SelectedItem}" Text="{Binding Name}" />
There is a shorter version to bind to a selected item's property:
<TextBlock Text="{Binding Rules/Name}" />
since you set your itemsource to your collection, your textbox is tied to each individual item in that collection. the selected item property is useful in this scenario if you were trying to do a master-detail form, having 2 listboxes. you would bind the second listbox's itemsource to the child collection of rules. in otherwords the selected item alerts outside controls that your source has changed, internal controls(those inside your datatemplate already are aware of the change.
and to answer your question yes in most circumstances setting the itemsource is the same as setting the datacontext of the control.

Resources