I have two classes
Company
CompanyKey
CompanyName
Person
FirstName
LastName
CompanyKey
The list items on the combo box is bound to a collection of CompanyObjects.
How to I databind the selected item property of the Combobox to the Person.CompanyKey property?
If I've understood your question correctly, here is a demo app that explains databinding of combo box: Demo App
Hope this helps.
Regards,
Mihir Gokani
EDIT: Fragment from code sample
<Window
x:Class="WpfApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"
Height="300"
Width="300">
<StackPanel>
<TextBlock
Margin="10">Persons</TextBlock>
<ComboBox
x:Name="comboPersons"
Height="25"
Margin="10"
ItemsSource="{Binding Persons}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation="Horizontal">
<TextBlock
Text="{Binding FirstName}"
Margin="0,0,5,0" />
<TextBlock
Text="{Binding LastName}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock
Margin="10">Companies</TextBlock>
<ComboBox
x:Name="comboCompanies"
Height="25"
Margin="10"
ItemsSource="{Binding Companies}"
DisplayMemberPath="CompanyName"
SelectedValuePath="CompanyKey"
SelectedValue="{Binding SelectedItem.CompanyKey, ElementName=comboPersons}" />
</StackPanel>
</Window>
The solution is simple, you need to use an IValueConverter to convert to Company to the Person object.
For more information on IValueConverter, please see:
http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx
You can then bind it in your xaml with something like:
{Binding Path=combox.SelectedItem, Converter={StaticResource CompanyToPersonConvertor}}
Related
I'm trying to make some data binding on a TreeView in a WPF solution. However, I can't seem to figure it out.
my XAML is as follows:
<Window x:Class="NoteNest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Note Nest" Height="512" Width="756">
<DockPanel LastChildFill="True">
<StackPanel Width="190">
<TreeView HorizontalAlignment="Left" VerticalAlignment="Top" BorderThickness="0" SelectedItemChanged="NoteSelect" DockPanel.Dock="Left" Name="Nest">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type Note}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</StackPanel>
</DockPanel>
</Window>
I keep getting the error:
Note is not supported in a Windows Presentation Foundation (WPF) project.
For the record. I have a public class named Note in the same namespace, so ether I have completely misunderstood how DataType works, or I'm just missing something.
What do I do?
I'm new to WPF and this following has stumped me for a while now:
I have an observableCollection of People object in my model that is bound to my tabControl. So each my a new People object is added, a new tab is created with People.Title as the Tab's header.
Each People object has an ObservableCollection of Friend object. Inside of the Tab I would like to have a List of two textboxes, one for Friend.FirstName and another for Friend.LastName.
My first requirement is working fine, but the second one is giving me an error 'ItemsSource is already in use'
Here's my code so far:
<TabControl Name="ConversationTabs" Grid.Row="0"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource HeaderInfoTabControl}"
ContentTemplate="{StaticResource DialogueList}" />
<Window.Resources>
<DataTemplate x:Key="HeaderInfoTabControl">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
<DataTemplate x:Key="DialogueList">
<ItemsControl ItemsSource="{Binding Path=DialogueCollectionVM}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Sent}" />
<TextBlock Text="{Binding Path=DateSent}" />
<TextBlock Text="{Binding Path=Message}" />
</StackPanel>
</ItemsControl>
</DataTemplate>
</Window.Resources>
I appreciate your help.
You cannot add items to an ItemsControl and use the automatic population (via ItemsSource) at the same time. If that StackPanel is supposed to be used for the items in the ItemsSource you should do this:
<ItemsControl ItemsSource="{Binding Path=DialogueCollectionVM}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Sent}" />
<TextBlock Text="{Binding Path=DateSent}" />
<TextBlock Text="{Binding Path=Message}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I have a class something like:
public class Section
{
private IEnumerable<Section> sections;
private IEnumerable<KeyValuePair<string, string>> attributes
public string Name {get;set;}
public IEnumerable<Section> Sections
{
get {return sections;}
}
public IEnumerable<KeyValuePair<string, string>> Attributes
{
get {return attributes;}
}
public Section(...)
{
//constructor logic
}
}
Which is contained in another class, lets call it OtherClass for sake of argument, that wraps around it and is used in WPF in an ObjectDataProvider.
As you can see, an instance of Section can contain many other instances of Section.
Is there a way in XAML to create a template that deals with this recursion?
This is what I've got so far:
<UserControl x:Class="OtherClassEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:OtherClassNS="clr-namespace:NameSpace"
Height="300" Width="300">
<UserControl.Resources>
<ObjectDataProvider ObjectType="{x:Type OtherClassNS:OtherClass}" x:Key="ViewModel" />
<DataTemplate x:Key="listViewAttr">
<WrapPanel>
<TextBlock Text="{Binding Path=Key}"></TextBlock><TextBlock Margin="0,0,4,0">: </TextBlock>
<TextBlock Text="{Binding Path=Value}"></TextBlock>
</WrapPanel>
</DataTemplate>
<DataTemplate x:Key="listViewSection">
<StackPanel>
<WrapPanel Margin="0,0,0,8">
<TextBlock Margin="0,0,4,0">Section:</TextBlock>
<TextBlock Text="{Binding Path=Name}"/>
</WrapPanel>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="listViewData">
<StackPanel>
<WrapPanel Margin="0,0,0,8">
<TextBlock Margin="0,0,4,0">Section: </TextBlock>
<TextBlock Text="{Binding Path=Name}"/>
<ListView DataContext="{Binding Path=Sections}" ItemTemplate="{StaticResource listViewSection}" ItemsSource="{Binding Sections}">
</ListView>
<ListView DataContext="{Binding Path=Attributes}" ItemsSource="{Binding Attributes}" ItemTemplate="{StaticResource listViewAttr}">
</ListView>
</WrapPanel>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ListView DataContext="{StaticResource ViewModel}" ItemTemplate="{StaticResource listViewData}" ItemsSource="{Binding Sections}">
</ListView>
</Grid>
</UserControl>
But I can't get recursion, as a DataTemplate can only reference one that is declared before it.
Can this be done in XAML? If so, how? Is this something that I'll have to do in code behind?
Are DataTemplates even the way to go? Is there a better way to display and edit this data?
In case anyone needs to see how to do this without using an HierarchicalDataTemplate:
<UserControl x:Class="OtherClassEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:OtherClassNS="clr-namespace:NameSpace"
Height="300" Width="300">
<UserControl.Resources>
<ObjectDataProvider ObjectType="{x:Type OtherClassNS:OtherClass}" x:Key="ViewModel" />
<DataTemplate x:Key="listViewAttr">
<WrapPanel>
<TextBlock Text="{Binding Path=Key}"></TextBlock>
<TextBlock Margin="0,0,4,0">: </TextBlock>
<TextBlock Text="{Binding Path=Value}"></TextBlock>
</WrapPanel>
</DataTemplate>
<DataTemplate x:Key="listViewData">
<StackPanel>
<WrapPanel Margin="0,0,0,8">
<TextBlock Margin="0,0,4,0">Section: </TextBlock>
<TextBlock Text="{Binding Path=Name}"/>
</WrapPanel>
<ListView ItemTemplate="{DynamicResource listViewData}" ItemsSource="{Binding Sections}" />
<ListView ItemsSource="{Binding Attributes}" ItemTemplate="{StaticResource listViewAttr}" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ListView DataContext="{StaticResource ViewModel}" ItemTemplate="{DynamicResource listViewData}" ItemsSource="{Binding Sections}">
</ListView>
</Grid>
</UserControl>
This gets me basically what I want.
The main change is as Dan Bryant suggested: changing the ItemTemplate to use a Dynamic rather than static resource for the recursive object (Section).
Perhaps I'm misunderstanding your scenario, but is HierarchicalDataTemplate what you're looking for?
I have a ComboBox setup in xaml and have set the ItemsSource binding. When I run the project nothing shows up in the ComboBox. If I inspect it with snoop the ItemsSource of the ComboBox is blank.
Anyone come across this before?
I checked the binding errors this is the error it displays
System.Windows.Data Error: 39 : BindingExpression path error: 'WasteTypeData' property not found on 'object' ''JobItems' (HashCode=28494546)'. BindingExpression:Path=WasteTypeData; DataItem='JobItems' (HashCode=28494546); target element is 'ComboBox' (Name='CboWasteTypes'); target property is 'ItemsSource' (type 'IEnumerable')
WasteTypeData is a public property of ObservableCollection<WasteTypes>.
This is what I have set as the binding of the ComboBox and if I debug the app WasteTypeData is populated with the list of WasteTypes as expected.
I can't figure out why it's looking for WasteTypeData on object JobItems. The WasteTypeData property is not found on the object JobItems.
JobItemsData is a public property of ObservableCollection<JobItems>.
My xaml has a ListBox with its ItemsSource Binding set to JobItemsData.
The ListBox has a DataTemplate with a couple of TextBoxes and one ComboBox. All the TextBoxes display their data properly.
Here's xaml if it will help shed any light on what's going on:
<UserControl
x:Class="WorkItems.View.ViewJobItems"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:JobItemsViewModel="clr-namespace:WorkItems.ViewModel"
Height="300" Width="500">
<ListBox
x:Name="LstJobItems"
ItemsSource="{Binding JobItemsData}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Column="0"
Margin="5">
<StackPanel
Orientation="Horizontal"
Margin="0,5,0,0">
<Label
Content="Customer Details"
FontWeight="Bold"
FontSize="24"></Label>
</StackPanel>
<StackPanel
Orientation="Horizontal">
<Line
StrokeThickness="3"></Line>
</StackPanel>
<StackPanel
Orientation="Horizontal"
Margin="0,5,0,0">
<Label
Content="Customer: "
FontWeight="Bold"
Width="110" />
<TextBox
Text="{Binding Customer, Mode=OneWay}"
Width="200" />
</StackPanel>
<StackPanel
Orientation="Horizontal"
Margin="0,5,0,0">
<Label
Content="Address: "
FontWeight="Bold"
Width="110" />
<TextBox
Text="{Binding Address1, Mode=OneWay}"
Width="200" />
</StackPanel>
<StackPanel
Grid.Column="1"
Margin="5">
<StackPanel
Orientation="Horizontal"
Margin="0,5,0,0">
<Label
Content="Job Details"
FontWeight="Bold"
FontSize="24"></Label>
</StackPanel>
<StackPanel
Orientation="Horizontal">
<Line
StrokeThickness="3"></Line>
</StackPanel>
<StackPanel
Orientation="Horizontal"
Margin="0,5,0,0">
<Label
Content="Date: "
FontWeight="Bold"
Width="110" />
<TextBox
Text="{Binding JobDate, Mode=OneWay}"
Width="200" />
</StackPanel>
<StackPanel
Orientation="Horizontal"
Margin="0,5,0,0">
<Label
Content="Waste Type: "
FontWeight="Bold"
Width="110" />
<ComboBox
x:Name="CboWasteTypes"
IsEditable="False"
ItemsSource="{Binding Path=WasteTypeData}"
DisplayMemberPath="WasteType"
SelectedValuePath="WasteTypeID"
SelectedValue="{Binding WasteTypeID}"
Width="200" />
</StackPanel>
<StackPanel
Orientation="Horizontal"
Margin="0,5,0,0">
<Label
Content="Status: "
FontWeight="Bold"
Width="110" />
<TextBox
Text="{Binding Status, Mode=OneWay}"
Width="200" />
</StackPanel>
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
Thanks
Paul
Check the Output window for any binding errors. You may have misspelled something or not set the DataContext correctly.
I think its failing because when you use {Binding Path=WasteTypeData} in your combobox, it expects to find it as a property in JobsItems instead of the observable collection, since that is what the parent control (your ListBox) is bound to.
Add WasteTypeData as a static resource in your user control, then bind your combobox to that, specifying it using "{Binding Source={StaticResource..."
<UserControl
...
xmlns:local="WorkItems"
...
Height="300" Width="500">
<UserControl.Resources>
<local:WasteTypeData x:Key="WasteTypeData"/>
</UserControl.Resources>
..
<ComboBox
x:Name="CboWasteTypes"
IsEditable="False"
ItemsSource="{Binding Source={StaticResource WasteTypeData}}"
DisplayMemberPath="WasteType"
SelectedValuePath="WasteTypeID"
SelectedValue="{Binding WasteTypeID}"
Width="200" />
See if that helps!
It seems that every time I read an article on "how to do WPF data binding", it is done with some different variation, sometimes with DataContext, sometimes without, sometimes with Itemssource or both Itemssource and DataContext, there's also ObjectDataProvider, and you can have any of these in XAML or codebehind, or no codebehind and bind directly from XAML to your ViewModels.
It seems like there are dozens of different syntaxes to use within the XAML itself, e.g.:
<ListBox ItemsSource="{Binding Source={StaticResource Customers}}">
and
<ListBox DataContext="{StaticResource Customers}" ItemsSource="{Binding}">
These two code samples, for example, do the same thing:
1. Using ObjectDataProvider with no code-behind:
<Window x:Class="TestDataTemplate124.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestDataTemplate124"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ObjectDataProvider x:Key="Customers"
ObjectType="{x:Type local:Customer}"
MethodName="GetAllCustomers"/>
</Window.Resources>
<StackPanel>
<ListBox DataContext="{StaticResource Customers}" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding LastName}"/>
<TextBlock Text=" ("/>
<TextBlock Text="{Binding Age}"/>
<TextBlock Text=")"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
2. Example with no DataContext:
<Window x:Class="TestDataTemplate123.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestDataTemplate123"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ListBox x:Name="ListBox1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding LastName}"/>
<TextBlock Text=" ("/>
<TextBlock Text="{Binding Age}"/>
<TextBlock Text=")"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
using System.Collections.ObjectModel;
using System.Windows;
namespace TestDataTemplate123
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
ListBox1.ItemsSource = Customer.GetAllCustomers();
}
}
}
Does anyone know of a source that explains WPF Databinding by instead of just saying "here's how you do databinding" and then explain one particular way, but instead attempt to explain the various ways to go about databinding and show perhaps what the advantages and disadvantages of e.g. having DataContext or not, binding in XAML or code-behind, etc.?
Check out this cheatsheet
I can recommend you the blog from Bea Stollnitz. If I'm not mistaken, she works at Microsoft and is involved in the development on WPF, particularly in databinding. She really has some great WPF tutorials, many on databinding. You should find some really good info here.