Data Binding with WPF - wpf

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>

Related

WPF DataTemplate and binding with TextBlock

<Window.Resources>
<DataTemplate x:Key="pointGroupTemplate" DataType="{x:Type ac:PointGroup}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
<DataTemplate x:Key="tt2" DataType="{x:Type ac:PointGroup}">
<TextBlock Text="{Binding PointsCount}"/>
</DataTemplate>
</Window.Resources>
the first template works well with:
<ComboBox x:Name="comboBox1" ItemTemplate="{StaticResource pointGroupTemplate}" SelectionChanged="ComboBox1_SelectionChanged"
Height="25" Margin="0,5,0,5">
but i want to use the second one in a textblock
<TextBlock x:Name="textBlock1" Text="{Binding ElementName=comboBox1 ,Path=SelectedItem ?????}"/>
anyhelp.
In order to have a DataTemplate applied, you need a ContentPresenter or ContentControl:
<ContentControl ContentTemplate="{StaticResource tt2}"
Content="{Binding SelectedItem, ElementName=comboBox1}"/>

How to disable DataTemplate at a specific place?

Consider the following model:
var model = new object[] { "Ala ma kota", DateTime.Now };
Now, let's suppose, that we have the following DataTemplates for ListBox displaying this model:
<ListBox ItemsSource="{Binding Data}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type sys:String}">
<StackPanel Orientation="Horizontal">
<TextBlock>String:</TextBlock>
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType={x:Type sys:DateTime}">
<StackPanel Orientation="Horizontal">
<TextBlock>Data i czas:</TextBlock>
<Calendar DisplayDate={Binding Mode=OneWay}" />
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
If we run such program, quite funny effect will be achieved:
Is there a way to disable DataTemplate on some level? I'd like to "block" it in the second DataTemplate, such that the calendar will be displayed correctly.
One way i can think of is to take advantage of resource lookup behaviour in WPF. It traverses up the Visual tree till found a default resource for a control.
You can define resource locally for string inside dataTemplate of DateTime so that it gets applied locally for your DateTime template and for others it will continue to behave same:
<DataTemplate DataType="{x:Type sys:DateTime}">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding}"/>
</DataTemplate>
</StackPanel.Resources>
<TextBlock>Data i czas:</TextBlock>
<Calendar DisplayDate="{Binding Mode=OneWay}" />
</StackPanel>
</DataTemplate>
Output:

WPF binding Listbox layoutpanel

I´m using devexpress and I want to do a binding with a Listbox, but I have an error. Here my code:
<ListBox x:Name="_list" >
<ListBox.ItemTemplate>
<DataTemplate>
<dxd:LayoutPanel
Caption="{Binding nameList}"
AllowHide ="False" AllowFloat="False"
GotFocus="panel_GotFocus" >
<TextBox Text="Hello" />
</dxd:LayoutPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
With this code, Caption {Binding nameList} is empty.
I have tried this.
<ListBox x:Name="_list" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBox Text="{Binding nameList}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In this case, text in TextBox is correct, I need to use the first code.
You seem to be a bit confused about how to use this ListBox. First, you need a collection property to bind to the ListBox.ItemsSource property. Let's say you have a collection of User objects called Users:
<ListBox ItemSource="{Binding Users}" />
Now we want to define how each User should be displayed... all the properties of the User class will be available to the Binding inside the DataTemplate. Let's say that the User class has two properties; Name and Age:
<DataTemplate DataType="{x:Type YourDataTypeNamespace:User}">
<StackPanel>
<TextBox Text="{Binding Name}" />
<TextBox Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
Then putting it all together:
<ListBox ItemSource="{Binding Users}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type YourDataTypeNamespace:User}">
<StackPanel>
<TextBox Text="{Binding Name}" />
<TextBox Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

MVVM Binding in Silverlight3 ItemsControl to get the parent controls DataContext

I have the following ItemsControl in Silverlight 3.
<ItemsControl ItemsSource="{Binding ValueCollectionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="MyBtn" Height="40" Content="{Binding Name}"
Tag="{Binding Value}"
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot, Path=ButtonCommand}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding ElementName=MyBtn, Path=Tag}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The Problem is that I have the ItemsControl bound to the Collection in my ViewModel, but I need the button to trigger a command on the ViewModel which is of course not Available in the DataContext of the button since it only contains the collection.
I can make the command fire by setting my ViewModel as a Resource and then binding to it as a StaticResource, but I want to know why the element to element binding won't work in this scenario. I would prefer not to use the StaticResource binding because that requires the default constructor on the ViewModel and so I can't inject my data easily.
UPDATE
I'm working through this slowly... Looking at the suggestions from Peter I realized that I may have more serious binding issues because of my page setup. I have two possible road blocks, but first things first.
My Items control above is wrapped in another items control that is bound to an observable collection. I moved my items control so that its a direct child of the root items control. It was wrapped in another control that I'll get to. So I tried the element binding to the items control ControlItemList, but its a collection so it can't find my ButtonCommand method in that Collection. What I need to do is bind to an item within that collection. How do I bind to a single item within the collection?
<Grid x:Name="LayoutRoot" Background="White"><!-- DataContext="{Binding Path=., Source={StaticResource lvvm}}">-->
<StackPanel Orientation="Vertical">
<ItemsControl x:Name="ControlItemList" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="100" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="ControlName" Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Center" />
<ItemsControl ItemsSource="{Binding ValueCollectionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
So, Assuming I can get the above to work the other road block is that my items control is wrapped in another usercontrol that I'm using to get DataTemplateSelector type functionality. I think this control may be blocking me from getting to the parent DataContext. Is there a limit as to how far up the tree you can go?
<common:DeviceControlTemplateSelector Grid.Column="1" FieldType="{Binding ValueType}" Margin="0,2,0,2">
<common:DeviceControlTemplateSelector.StringTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, Mode=TwoWay}" Width="100"/>
</DataTemplate>
</common:DeviceControlTemplateSelector.StringTemplate>
<common:DeviceControlTemplateSelector.DateTimeTemplate>
<DataTemplate>
<TextBox Text="this is date time binding" Width="100"/>
</DataTemplate>
</common:DeviceControlTemplateSelector.DateTimeTemplate>
<common:DeviceControlTemplateSelector.BooleanTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Value, Mode=TwoWay}" />
</DataTemplate>
</common:DeviceControlTemplateSelector.BooleanTemplate>
<common:DeviceControlTemplateSelector.IntegerTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ValueCollection}" DisplayMemberPath="Value" SelectedIndex="{Binding Value, Mode=TwoWay}" >
</ComboBox>
</DataTemplate>
</common:DeviceControlTemplateSelector.IntegerTemplate>
<common:DeviceControlTemplateSelector.MultiButtonTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding ValueCollectionList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="MyBtn"
Height="40" Content="{Binding Name}"
Tag="{Binding Value}"
cmd:ButtonBaseExtensions.Command="{Binding ElementName=ControlItemList, Path=DataContext.ButtonCommand}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding Value}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</common:DeviceControlTemplateSelector.MultiButtonTemplate>
<Button x:Name="MyBtn"
Height="40" Content="{Binding Name}"
Tag="{Binding Value}"
cmd:ButtonBaseExtensions.Command="{Binding ElementName=ControlItemList, Path=ButtonCommand}"
cmd:ButtonBaseExtensions.CommandParameter="{Binding Value}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Thanks again everyone for your help!
the best solution i have found so far to deal with this situation is by Dan Wahl where he uses a DataContextProxy.
http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx
Try to change
from
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot, Path=ButtonCommand}"
to
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot, Path=DataContext.ButtonCommand}"
OR
cmd:ButtonBaseExtensions.Command="{Binding ElementName=LayoutRoot.DataContext, Path=ButtonCommand}"
In MVVM messaging is a strong concept for cummunication between ViewModels. You could use PRISM Eventaggregator or the Messenger class of MVVM Light Toolkit. On the button command you can publish a message and subscribe to it in the viewmodel.

How can I change Item and Content Templates based on the object type?

I have a WPF TabControl that I have set the ItemTemplate as well as the ContentTemplate. This tab control displays Call Log information based on customer tech support information.
Inside of this same control, I would also like to be able to show a ReturnAuthorization template.
I would like to swap these out based on the object type added to the TabControl's Items collection. Is this something that is possible?
I have some Pseudo-code that sort of shows what I want to pull off:
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True" ItemContainerStyle="{StaticResource ClosableTabItemTemplate}" >
<TabControl.ItemTemplate>
if ( Type is Entities:Case )
{
<DataTemplate DataType="{x:Type Entities:Case}">
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
}
else if ( Type is Entities1:RAMaster )
{
<DataTemplate DataType="{x:Type Entities1:RAMaster}">
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
}
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
One way to do this is using something like an ItemTemplateSelector, which you can set on the TabControl. However, if you only need the different templates within the TabControl and know what they all are ahead of time, then you can just let them automatically be applied by using the DataTypes.
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True" ItemContainerStyle="{StaticResource ClosableTabItemTemplate}" >
<TabControl.Resources>
<DataTemplate DataType="{x:Type Entities:Case}">
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
<DataTemplate DataType="{x:Type Entities1:RAMaster}">
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
</TabControl.Resources>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>

Resources