How can I factor out a DataTemplate's binding in WPF? - wpf

I have a DataTemplate I want to reuse. The part I want to factor out is the binding, because it's the only thing that changes. My DataTemplate looks roughly like this. (There's actually quite a bit more to it, but I've taken out the extraneous stuff.)
<DataTemplate>
<TextBox Text="{Binding Name}" />
</DataTemplate>
How can I reuse this DataTemplate while simply varying the property to which I'm binding? (Note that if it were as simple as just a TextBox, I wouldn't worry about it, but the DataTemplate actually contains a StackPanel with a number of other elements in it. I want to centralize that in one place, hence the DataTemplate.)
I've thought about two ways to tackle this problem.
Create a simple custom control. Reuse that, and don't worry about reusing the DataTemplate.
Experiment with some kind of subclass of DataTemplate. (I'm told this is possible.) I'd add a dependency property to it that lets me specify the name of the property to which I want to bind.
Suggestions?

I hate answering my own questions, but for the sake of completeness, here's my solution.
<ListBox ItemsSource="{Binding}">
<ListBox.Resources>
<ControlTemplate x:Key="textBoxControlTemplate" TargetType="ContentControl">
<TextBox Text="{TemplateBinding Content}" />
</ControlTemplate>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding Name}" Template="{StaticResource textBoxControlTemplate}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This of course is a very contrived example. In my own app, I'm not actually putting textboxes inside of a listbox. In a listbox, this is not very useful, but imagine it inside of a DataGrid, where each column might be displayed in a similar way, but binds to a different property.

Create a UserControl and use it within the DataTemplate.
<DataTemplate>
<local:MyComplexUserControl DataContext="{Binding Name}"/>
</DataTemplate>
and within the UserControl:
<StackPanel>
<TextBlock>Value:</Text>
<TextBox Text="{Binding}"/>
</StackPanel>
Have a separate DataTemplate with its own binding for each occasion.

Related

Adding a small popup on a wpf listbox that is already using itemsource

Here is a portion of my wpf-xaml code :
<ListBox x:Name="TestJobSuiteListBox" Grid.Row="1" ItemsSource="{Binding AvailableJobs}" MouseRightButtonDown="TestJobSuiteListBox_OnMouseRightButtonDown">
<ListBox.ItemTemplate>
<HierarchicalDataTemplate>
<ListBoxItem Content="{Binding Name}"/>
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I would like to add another listboxitem to that ListBox and I dont want it to be visible before you rightclick on the listbox. It also should not be bound to the "AvailableJobs" property.
Something like this :
<ListBox x:Name="TestJobSuiteListBox" Grid.Row="1" ItemsSource="{Binding AvailableJobs}" MouseRightButtonDown="TestJobSuiteListBox_OnMouseRightButtonDown">
<ListBox.ItemTemplate>
<HierarchicalDataTemplate>
<ListBoxItem Content="{Binding Name}"/>
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
<ListBoxItem x:Name="AddJobbListBoxItem" Visibility="Hidden"></ListBoxItem>
</ListBox>
This doesn't work, because of "itemsource must be empty problem"
Anyone have a good Idea of how I could do it ?
I don't need help with the visibility/rightclick functionality.
Thanks in advance, I hope the problem is understandable.
You can put listbox to stackPanel, and disable it's scrolling. Then add that one element also to the stackPanel under the listbox, and finally add that stackPanel to scrollViewer. Then you have listbox with AddButton.
<ScrollViewer>
<StackPanel>
<ListBox ItemsSource="{Binding AvailableJobs}"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<!-- ... -->
</ListBox>
<Button x:Name="AddJobbButton" Visibility="Collapsed" />
</StackPanel>
</ScrollViewer>
Notice that if you have lots of items in listbox, there might be some performance problems, because I'm not sure that listbox's virtualizing is working correctly if it's in stackPanel.
EDIT: of course you have to listen mouse events and set then button's visibility to visible and so on...
I think you have to use ItemTemplateSelector to achieve this functionality. You can create different DataTemplate as per your requirement in the Resources section and bind properly in the xaml. Check the answer here, it will give you the idea about the approach. Please refer this examlpe also. Hope this will help.

Dynamic Telerik RadOutlookBar headers come out wrong with ItemTemplate

I'm trying to use Telerik RadControls in a MVVM kind of way but having some strange problems.
The Viewmodel behind the RadOutlookBar has a collection of ViewModels that each have a Title string property. I wanted to define it so way the they get Wrapped inside a RadOutlookBarItem and bind the header/title properties together.
XAML:
<telerik:RadOutlookBar x:Name="Items">
<telerik:RadOutlookBar.TitleTemplate>
<DataTemplate>
<ContentControl Content="{Binding Path=Title}" />
</DataTemplate>
</telerik:RadOutlookBar.TitleTemplate>
<telerik:RadOutlookBar.ItemTemplate>
<DataTemplate>
<telerik:RadOutlookBarItem Header="{Binding Path=Title}" >
<ContentControl Content="{Binding}" />
</telerik:RadOutlookBarItem>
</DataTemplate>
</telerik:RadOutlookBar.ItemTemplate>
</telerik:RadOutlookBar>
This works as intended except that the Header comes out strange. Instead of being like a static string item it seems to get wrapped inside another object that behaves like a similar to a RadOutlookBarItem ( it' changes color when mouseover and such )
Even if I cahnge to straightforward string instead of binding it's still strange. But If I don't define a ItemTemplate inside the RadOutlookBar (that is, not a dynamic control) it looks all right.
What could be going on there?
Solved this and another problem in one fell swoop. I was binding to the wrong Template the whole time. This made me think I had to add OutLookBarItem myself.
In the end I was supposed to bind what I was trying to bind to the ContentTemplate.
<telerik:RadOutlookBar x:Name="Items">
<telerik:RadOutlookBar.ContentTemplate>
<DataTemplate >
<ContentControl Content="{Binding}" />
</DataTemplate>
</telerik:RadOutlookBar.ContentTemplate>
<telerik:RadOutlookBar.TitleTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</telerik:RadOutlookBar.TitleTemplate>
<telerik:RadOutlookBar.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</telerik:RadOutlookBar.ItemTemplate>
</telerik:RadOutlookBar>
Should work I think.

Is it possible to bind to a control's property from a datatemplate?

Ok, sounds odd, and there's likely a better way, but I haven't seen it yet.
What I'm trying to do is restyle a ListPicker under Windows Phone 7.
What I need is to
get rid of the header (that's easy, just define a null ListPicker.HeaderTemplate).
Force the picker to always go to full mode when clicked (again, easy, just set the ItemCountThreshold to 1).
Restyle the itemtemplate used when in FullMode (again, easy, just define a FullModeItemTemplate)
Incorporate the ListPicker's "HEADER" property value into the ItemTemplate (since only one item will ever show, i need the header text "embedded" within the one item).
It's that number 4 that I can't seem to get.
I've defined a listpicker like so (i'm directly defining the templates inline instead of in resources for now, just to keep things simple).
<phonekit:ListPicker Header="Header Text" x:Name="ListOfSounds"
SelectedItem="{Binding Path=DepartureChime, Mode=TwoWay, Converter={StaticResource EnumDescriptionToStringConverter}}"
ItemCountThreshold="1">
<phonekit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Background="Transparent">
<TextBlock Text="{TemplateBinding Header}" />
<TextBlock Text="{Binding}" />
<TextBlock Text=">" />
</StackPanel>
</DataTemplate>
</phonekit:ListPicker.ItemTemplate>
Ignoring all the obvious formatting bits for now, the problem I'm having is that I can't use {TemplateBinding Header} from within a datatemplate. I've used it from a ControlTemplate no problem.
The result of this ItemTemplate should be an item displayed such as
{TextOfHeader}{Content of selected Item}>
I'm just not sure how to go about getting at a property of the templated control (the listpicker in this case).
Any ideas?
Take advantages of RelativeSource:
<TextBlock Text="{Binding Path=Header, RelativeSource={RelativeSource AncestorType={x:Type phonekit:ListPicker}}}" />

Is there something in WPF similar to Style.BasedOn for DataTemplate?

At the moment, I have two very large DataTemplate objects to display two sets of items in two ListBoxes. The DataTemplates are referenced in the ContentTemplate property in two Styles that are set in the ItemContainerStyle properties of the two ListBoxes. The items are of the same type and the DataTemplates are identical except for the following control:
From DataTemplate1
<TextBlock Style="{StaticResource TextStyle}" FontSize="20" Foreground="White"
HorizontalAlignment="Left" Panel.ZIndex="2" Text="{Binding RemainingTime.TotalHours,
Converter={StaticResource DoubleToIntegerConverter}, StringFormat={}{0:#00}}" />
From DataTemplate2
<TextBlock Style="{StaticResource TextStyle}" FontSize="20" Foreground="White"
HorizontalAlignment="Left" Panel.ZIndex="2" Text="{Binding ElapsedTime.TotalHours,
Converter={StaticResource DoubleToIntegerConverter}, StringFormat={}{0:#00}}" />
Is there some way to avoid duplicating the whole Dataemplate but still have this one difference in the text binding of this TextBlock in the second template?
No, there is no inheritance for DataTemplate. If you think about, how would you override a part of a DataTemplate?
Solution: Use another Style to capture the common properties between the two templates. You can scope it in the same Resources block if it only place you need it. It is much cleaner or more WPF way of doing things.
I've already asked this question here once and unfortunately there isn't.
but in this specific situation you can move the fontsize,foreground,horizontalalignment..etc to a style (lets say textstyle2) that based on your current textstyle.
I got an answer to this from another post (by Liz). Basically, you can put all common controls into one DataTemplate and then create two more DataTemplates that each use the first one as a ContentTemplate in a ContentPresenter. Then, you can add different controls into one or both of the latter DataTemplates. Liz provided a code example.
<DataTemplate x:Key="UserTemplate">
<!-- show all the properties of the user class here -->
</DataTemplate>
<DataTemplate DataType="{x:Type local:User}">
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource UserTemplate}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Author}">
<StackPanel>
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource UserTemplate}"/>
<!-- show all the additional Author properties here -->
</StackPanel>
</DataTemplate>
Thanks once again Liz.
Adding to what Dennis suggested, you can always create a custom control that you just stick inside your DataTemplate and re-style that control instead of the DataTemplate.

WPF: Problem with ItemsControl datatemplate as a user control

This works fine:
<ItemsControl ItemsSource="{Binding Persons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBlock Text="{Binding Path=LastName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But, I want to make use of a User Control in place of the stack panel with two TextBlocks using something like:
<DataTemplate>
<jasControls:NameView/>
</DataTemplate>
But, with this and a million other syntaxes, the NameView shows nothing, yet it works fine in a separate test, outside the ItemsControl, with explicitly set (dependency) property - e.g.
<jasControls:NameView FName="{Binding Path=Person.FirstName}" />
I can find hardly any examples of this anywhere, and need to know how to specify the properties of the user control - or how can the user control 'receive' the individual item type (a Person)? What syntax to use? Do I need to specify the datatype? Is ItemsControl causing a problem or should any similar control do e.g. ListBox? Can I design it so that the user control gets an entire Person object? What dependency properties do I need in the user control? In short, how to get data into the user control?! The actual business types involved will be a bit more complicated involving some logic in the user control, hence my desire to use a user control.
ANY direction on this will be very gratefully received - TIA
Assuming your Persons collection has many Person objects in it, the DataContext property of your NameView control will automatically be set to the Person object.
You wouldn't need any dependency properties to achieve this feat. Your NameView usercontrol would simply be:
<UserControl ...>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding LastName}"/>
</StackPanel>
</UserControl>
You should have no codebehind to get this information to display.
From there, you should be able to access the Person object from the DataContext property:
Person person = this.DataContext as Person;

Resources