WPF Data Template Selector - wpf

I have a data template selector, which I am using to pick between two different templates. Problem is, these templates include a grid as the outer element etc. What I have done is I have my template selector in my resource control in my UserControl.Resources:
<l:UIDataTemplateSelector x:Key="UISelector"
ClassicDataTemplate="{StaticResource ClassicDataTemplate}"
ModernDataTemplate="{StaticResource ModernDataTemplate}" />
Then outside of this, I have my Content Control, where my templates are in Control Templates:
<ContentControl ContentTemplateSelector="{StaticResource UISelector}"></ContentControl>
When I run my code, this causes it to crash, but if I use:
<ContentControl Template="{StaticResource ClassicDataTemplate}"></ContentControl>
To use one template or the other, it works perfectly fine. Is there a simple way to get it to use the template selector to display it?

A ContentControl has a Template of type ControlTemplate. It also accepts a ContentTemplate of type DataTemplate and a ContentTemplateSelector which is expected to select a DataTemplate. In your case, since Template="{StaticResource ClassicDataTemplate}" is working, I suspect you created ControlTemplate instead of DataTemplate. You need a working ContentTemplate="{StaticResource ClassicDataTemplate}" in order to transfer to the selector.

Related

How to combine multiple xaml pages in one xaml page?

Is there any idea of master layout and child layouts in wpf for xaml pages?
I have one xaml file which I want to use in two different Xaml files with different Data but same UI.
I don't want to create same design two times, I just want to have only one xaml page on both places.
Use a ContentControl with different ContentTemplate. Define these ContentTemplate as Resource. Specify DataType in the DataTemplate of your ContentTemplate. And in different places just set appropriate Content of your ContentControl using Binding. This Content can be any property of any kind.
Eg;
<ContentControl Content="{Binding View1}" />
<ContentControl Content="{Binding View2}" />
Beauty is DataTemplate is automatically picked up by the ContentControl.

Why do I need to use a lookless control instead of a UserControl when I have swappable sub-parts

I have a WPF application with some custom controls (defined inside the same project) that have swappable sub parts. As a basic example, lets says I have some Xaml like:
<Border Background="White" CornerRadius="9">
<ContentPresenter/>
</Border>
Which is used for my class derived from ContentControl, lets call it MrWhiteControl
If I make MrWhiteControl a xaml and code-behind file pair (so it has an InitializeComponent() call in the ctor), then whatever I place in the Content property has lots of problems e.g. the DataContext won't inherit properly, and using ElementName in bindings won't work.
However, if I use a lookless control - so a MrWhiteControl.cs file with the style defined in Themes/Generic.xaml (I don't care about theming support in this app) - then everything works fine.
What I want to know is why this is the case. What's going on behind the scenes that means the lookless control works fine, but that the xaml with code-behind doesn't work properly?
I've created an example project you can clone from GitHub. If you run that, you'll see that the ElementName binding doesn't work inside the control that has a code-behind (2nd in the list), but does work fine with the lookless control (bottom of the list).
What difference is there between lookless and user controls hosting a content presenter?
Edit: Added a solution to get the binding to work with this approach at the bottom.
Nice question.
AFAICT this is just because of the way the DP is setup and when the Binding is resolved respecting NameScope's.
From MainWindow.xaml you're assigning the DP MyContent property for the control with code-behind which takes the entire TextBox code as the DP value. Hence the binding is not resolved at this point.
When the Binding is applied, in the scope of the UserControl, the ElementName is not found, which we can verify if we add another TextBox into that UserControl, say
<StackPanel>
<TextBox Name="textBox" Text="Sampleeeeeeee" />
<ContentPresenter Content="{Binding MyContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type LookslessVsUserControl:MrWhiteWithCodeBehind}}}" />
</StackPanel>
Now we get
as the point the Binding is resolved the new TextBox is the one which is in scope not the one from MainWindow.xaml
As for the Style, the scope remains the same where the Style is applied, so it finds the MainWindow.xaml's text-box. We hence only have the one level of nesting, which we can also see from Snoop
Solution:
If this is the approach you prefer to set the DP with the control, you can still get the Binding to work:
In your MainWindow.xaml.cs you need to set the namescope for the UserControl accordingly so it doesn't use it's own WPF Xaml NameScope
In ctor() of MainWindow.xaml.cs:
Loaded += (sender, args) => NameScope.SetNameScope(problemControl, NameScope.GetNameScope(this));
and in xaml I named it problemControl such as:
<LookslessVsUserControl:MrWhiteWithCodeBehind x:Name="problemControl">
<LookslessVsUserControl:MrWhiteWithCodeBehind.MyContent>
<TextBlock Text="{Binding ElementName=textBox, Path=Text}" />
</LookslessVsUserControl:MrWhiteWithCodeBehind.MyContent>
</LookslessVsUserControl:MrWhiteWithCodeBehind>
With this when the UserControl loads and tries to resolve the Binding it should find the TextBox fine and give you the output you desire.

How to render different template for some tabs in WPF?

I have a tab content template set to my tab control:
<TabControl SelectedIndex="0"
ItemsSource="{Binding Tabs}"
ItemTemplate="{StaticResource AppTabItemTemplate}"
ContentTemplate="{StaticResource AppTabContentTemplate}" />
The thing is that most of the times the current template is wanted, but there are times when I want to display another template instead. The item source provides this data whether it should show one or another, but how can I do an "if" in XAML and use an alternative UI when the other layout is wanted?
Should this logic be part of the template or the containing XAML that includes this tab control? The information that is used to make the decision between UIs is in the item source.
Use a DataTemplateSelector.
You will need to define your selection logic in a class that derives DataTemplateSelector, create a resource for your selector in XAML and then use it by assigning the resource to the ItemTemplateSelector property of your tab control.

How do you navigate a complex Visual Tree in order to re-bind an existing element?

In the above image, child is a ContentPresenter. Its Content is a ViewModel. However, its ContentTemplate is null.
In my XAML, I have a TabControl with the following structure:
<local:SuperTabControlEx DataContext="{Binding WorkSpaceListViewModel}"
x:Name="superTabControl1" CloseButtonVisibility="Visible" TabStyle="OneNote2007" ClipToBounds="False" ContentInnerBorderBrush="Red" FontSize="24" >
<local:SuperTabControlEx.ItemsSource>
<Binding Path="WorkSpaceViewModels" />
</local:SuperTabControlEx.ItemsSource>
<TabControl.Template>
<ControlTemplate
TargetType="TabControl">
<DockPanel>
<TabPanel
DockPanel.Dock="Top"
IsItemsHost="True" />
<Grid
DockPanel.Dock="Bottom"
x:Name="PART_ItemsHolder" />
</DockPanel>
<!-- no content presenter -->
</ControlTemplate>
</TabControl.Template>
<TabControl.Resources>
<DataTemplate DataType="{x:Type vm:WorkSpaceViewModel}">
....
WorkSpaceViewModels is an ObservableCollection of WorkSpaceViewModel. This code uses the code and technique from Keeping the WPF Tab Control from destroying its children.
The correct DataTemplate - shown above in the TabControl.Resource - appears to be rendering my ViewModel for two Tabs.
However, my basic question is, how is my view getting hooked up to my WorkSpaceViewModel, yet, the ContentTemplate on the ContentPresenter is null? My requirement is to access a visual component from the ViewModel because a setting for the view is becoming unbound from its property in the ViewModel upon certain user actions, and I need to rebind it.
The DataTemplate is "implicitly" defined. The ContentPresenter will first use it's ContentTemplate/Selector, if any is defined. If not, then it will search for a DataTemplate resource without an explicit x:Key and whose DataType matches the type of it's Content.
This is discussed here and here.
The View Model shouldn't really know about it's associated View. It sounds like there is something wrong with your Bindings, as in general you should not have to "rebind" them. Either way, an attached behavior would be a good way to accomplish that.
I think the full answer to this question entails DrWPF's full series ItemsControl: A to Z. However, I believe the gist lies in where the visual elements get stored when a DataTemplate is "inflated" to display the data item it has been linked to by the framework.
In the section Introduction to Control Templates of "ItemsControl: 'L' is for Lookless", DrWPF explains that "We’ve already learned that a DataTemplate is used to declare the visual representation of a data item that appears within an application’s logical tree. In ‘P’ is for Panel, we learned that an ItemsPanelTemplate is used to declare the items host used within an ItemsControl."
For my issue, I still have not successfully navigated the visual tree in order to get a reference to my splitter item. This is my best attempt so far:
// w1 is a Window
SuperTabControlEx stc = w1.FindName("superTabControl1") as SuperTabControlEx;
//SuperTabItem sti = (SuperTabItem)(stc.ItemContainerGenerator.ContainerFromItem(stc.Items.CurrentItem));
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(stc);
//ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(sti);
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
The above code is an attempt to implement the techniques shown on the msdn web site. However, when I apply it to my code, everything looks good, except myDataTemplate comes back null. As you can see, I attempted the same technique on SuperTabControlEx and SuperTabItem, derived from TabControl and TabItem, respectively. As described in my original post, and evident in the XAML snippet, the SuperTabControlEx also implements code from Keeping the WPF Tab Control from destroying its children.
At this point, perhaps more than anything else, I think this is an exercise in navigating the Visual Tree. I am going to modify the title of the question to reflect my new conceptions of the issue.

Is it possible to create a WPF tab control template that looks nothing like a tab control?

What I'd like is a control that functions just like the tab control but instead of having the tabs along the top, the items would be displayed in a list box along the side. I imagine it's possible but haven't found any examples, I'm hoping there's someone here that's done something like this.
WPF controls are designed to enable exactly what you want. To reuse control functionality while completely replacing the visual representation. You will have to create your own ControlTemplate for the TabControl. You can find a TabControl ControlTemplate Example on MSDN. You will also have to study the Control Authoring Overview on MSDN.
I actually find the Silverlight 3 documentation somewhat easier to digest, and even though there are some differences when it comes to control styling the fundamental concepts are still the same. You can read Customizing the Appearance of an Existing Control by Using a ControlTemplate on MSDN to learn about control templates and then study TabControl Styles and Templates to discover what is required to create you own control template in Silverlight.
You can use Expression Blend to extract the the default TabControl template in WPF.
You don't need to use a TabControl at all. You could just bind your ListBox to a list of items, and put a ContentControl beside it, bound to the selected item :
<DockPanel>
<ListBox Name="listBox"
DockPanel.Dock="Left"
ItemsSource="{Binding Items}"
DisplayMemberPath="Name"/>
<ContentControl Content="{Binding SelectedItem, ElementName=listBox}"
ContentTemplate="{StaticResource theTemplate}"/>
</DockPanel>

Resources