I'm just learning XAML so bear with me.
When you nest an element in XAML it seems like that element is set to the "Child" property of the parent UI.
However in the following code the child element is set to the value. That sort of makes sense - kinda.
However then Border Element below was set to the ControlTemplate, yet ControlTemplate has no Child element, so can someone tell me what exactly is the relationship between the Border and ControlTemplate below? May be you could re-write this snippet in c# as an explanation.
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="dtp:PickerSelectorItem">
<Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Picker">
<VisualState x:Name="Focused">
<Storyboard>
<!-- There is more code but snipped for irrelevance-->
Also how does the XAML compiler makes sense of what the child element actually does? Ie, how does it know that the child element should be set to the "Child" property, whereas other times it'd be set to the "Value" property as seen above.
The XAML parser uses the ContentPropertyAttribute to determine how to handle child xaml elements. For example, if you look at the following two base controls you'll see how their usage is:
ContentControl:
[ContentPropertyAttribute("Content")]
public class ContentControl : Control, IAddChild { ... }
ItemsControl:
[ContentPropertyAttribute("Items")]
public class ItemsControl : Control, IAddChild, IContainItemStorage { ... }
It used to be that you would implement the IAddChild interface, but that is obsolete now. Also, the xaml parsing engine can recognize if your "content" property is pointing to a single object or a collection of objects. Basically, if you want to create your own custom control, make sure to use the correct attribute to control how your child(ren) are handled.
The XAML parser knows which property to assign the inner XAML to according to the parent object type. For example, nesting XAML under a ContentControl will call parentContentControl.Content = child, while for ItemsControl it will add the children to the Items collection: parentItemsControl.Items.Add(child).
I guess the same is true for FrameworkTemplate objects (which ControlTemplate derives from), and child controls for these types are assigned to the VisualTree property.
In your question, ControlTemplate uses FrameElementFactory to construct the controls defined on XAML and builds the visual tree which is finally assigned to ControlTemplate's VisualTree property.
Related
What is ItemContainerTemplate used for? It is derived from DataTemplate, but I don't see any difference between them except the ItemContainerTemplateKey property. When should I use one and when the other?
The only difference between DataTemplate and ItemContainerTemplate is the way the resource dictionary key is automatically provided (assuming it is not set explicitly). Namely, DataTemplate is decorated with [DictionaryKeyProperty("DataTemplateKey")] attribute, and the DataTemplateKey is basically defined as:
public object DataTemplateKey
{
get { return (DataType != null) ? new DataTemplateKey(DataType) : null;
}
See DataTemplate source for reference.
ItemContainerTemplate derives from DataTemplate, but is decorated with [DictionaryKeyProperty("ItemContainerTemplateKey")] attribute (which in practice replaces the inherited one), and ItemContainerTemplateKey property is defined as follows:
public object ItemContainerTemplateKey
{
get { return (DataType != null) ? new ItemContainerTemplateKey(DataType) : null; }
}
See ItemContainerTemplate source for reference.
The difference seems small - DataTemplate returns an instance of DataTemplateKey and ItemContainerTemplate returns an instance of ItemContainerTemplateKey (both derive from TemplateKey). So basically these two are equivalent1:
<ItemContainerTemplate DataType="{x:Type sys:String}" />
<DataTemplate x:Key="{ItemContainerTemplateKey {x:Type sys:String}}" />
and so are these:
<ItemContainerTemplate x:Key="{DataTemplateKey {x:Type sys:String}}" />
<DataTemplate DataType="{x:Type sys:String}" />
The main practical difference between these two is that DataTemplate with default key is treated as an implicit template2, whereas ItemContainerTemplate is not. In fact, you need to manually reference it, e.g.:
<ListBox ItemTemplate="{StaticResource {ItemContainerTemplate {x:Type sys:String}}}" />
I'm not sure about the intentions behind creating ItemContainerTemplate class. I guess it gives you a clearer overview of the code, where you know that such a template is specifically intended to be used in an ItemsControl (or a derived control). Also, I guess it would prove to be pretty simple to write a strongly reusable DataTemplateSelector that would take advantage of this class.
1 They're not equivalent in the sense that created objects are of different types, but functionally they're equivalent.
2 Implicit templates are applied to all objects of corresponding type within the scope, unless a template is set explicitly.
The ItemContainerTemplate describes the world around your Item. For example in a ListBox the selection rectangle around your ListBoxItem. The DataTemplate describes how you ListBoxItem apears and of which elements it consists.
Dr. WPF did a good example:
http://drwpf.com/blog/category/item-containers/
You can put an ItemContainerTemplate in a ResourceDictionary, and it will automatically use the DataType as its key.
That's the only difference.
ItemContainerTemplate is useful/necessary when you need different Item containers for an ItemsControl.
Usually the XAML infracstructure decides which item container is used for a given ItemsControl:
ListBox uses ListBoxItem
DataGrid uses DataRow
ComboBox uses ComboBoxItem
Menu uses MenuItem
As for a Menu you would sometimes want a Separator (which is not a MenuItem technically)
That's where ItemContainerTemplate, ItemContainerTemplateSelector and ItemContainerTemplatekey come into play.
Based on the viewmodel/datacontext type or one/many of its property values you can switch between a Separator in an ItemContainerTemplate and a MenuItem in another
ItemContainerTemplate.
You may use triggers or a ItemContainerTemplateSelector to achieve this.
Actually and honestly I am myself just now about to understand what the ItemContainerTemplateKey is for.
I think to have understood that it's an easy way to map an ItemContainerTemplate to a data type without the need for a Selector or code behind or triggers.
If you're fine with the default ItemContainerTemplate you simply don't need to deal with it in your XAML. Manipulating the ItemsContainer style can be achieved within the ItemContainerTemplate. Having a custom DataTemplate to bind your data (and also to style it) is done within the ItemTemplate.
Usage of ItemContainerTemplate is rarely needed. But sometimes very handy.
You could check that Link to see the difference between controltemplate and datatemplate and hierarchicaldatatemplate itemspaneltemplate:
http://nirajrules.wordpress.com/2009/03/08/controltemplate-vs-datatemplate-vs-hierarchicaldatatemplate-vs-itemspaneltemplate/
According to all of the documentation, when you're creating a non-lookless control, you're supposed to subclass UserControl. However, UserControl is a simple subclass of ContentControl but it doesn't appear to add anything to it, interface-wise. As such, you can take that designer-generated code and change the base class to ContentControl and it appears to still work exactly the same.
So what's the point of UserControl over ContentControl?
Update:
For those who keep answering Visual Studio treats them differently, I'd argue that isn't the case. Try it! Create a new UserControl in Visual Studio, then in the resulting XAML file, change the root tag to ContentControl. Then in the associated class file, change the base class to ContentControl or simply delete it as I have done here (see the note) and you'll see it appears to work exactly the same, including full WYSIWYG designer support.
Note: You can delete the base class from the code-behind because it's actually a partial class with the other 'part' of the class being created by the XAML designer via code-generation. As such, the base class will always be defined as the root element of the XAML file, so you can simply omit it in the code-behind as it's redundant.
Here's the updated XAML...
<ContentControl x:Class="Playground.ComboTest.InlineTextEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock Text="Success" />
</ContentControl>
...and the associated class file...
namespace Playground.ComboTest {
public partial class InlineTextEditor {
public InlineTextEditor()
=> InitializeComponent();
}
}
UserControls are a good fit for aggregating existing controls when you don't need to provide the consumer a ControlTemplate. This means that UserControls are not lookless. Why not just use ContentControl as it can have coupled XAML like a UserControl and the implementation looks similar to UserControl? Well, there are several important technical differences you must know:
UserControls set themselves as the source to RoutedEvents raised by elements within them. This means that when an element outside the UserControl receives a bubbled event, the Source is the UserControl, not the thing you interacted within the UserControl. In the philosophical sense of what you often hear about UserControls, "It's for aggregating existing controls", this makes sense as you want the parent container element to think of your UserControl as a single unit. For example, your UserControl contains a button that the user clicks and the Grid that contains your UserControl instance receives the MouseLeftButtonUp event but the Button is not the Source of the event, your UserControl is.
UserControl sets Focusable and IsTabStop to false. You can see the philosophy demonstrating itself again here as we don't want a grouping of existing controls to be Focusable.
UserControl sets HorizontalAlignment and VerticalAlignment to Stretch. A ContentControl would automatically be set to Left and Top.
UserControl's own AutomationPeer implementation allows you to change VisualStates via VisualStateManager.GoToState(). ContentControl requires the VisualStateGroups to be at the top-level and you must call them with VisualStateManager.GoToElementState().
UserControl's own ControlTemplate wraps your content in a Border. This again makes sense when thinking of the philosophical use case for UserControl.
UserControl's own ControlTemplate provides more TemplateBindings than ContentControl. This is kind of a recapitulation of some above items but explains how they are possible. Recall that UserControl provides a Border so that relates to some of these free TemplateBindings you see below. This enables respect for BorderBrush, BorderThickness, Background and Padding properties on your control that would otherwise not work with just a ContentControl. For example, if you just derive your control from ContentControl and set the Background property on the root ContentControl element it will not work because the ControlTemplate of ContentControl has no TemplateBinding for Background. Of course you could set the Background property on the child content element that wraps your desired elements, like a Grid, but that isn't ideal IMO.
ContentControl's ControlTemplate
<ControlTemplate TargetType="ContentControl">
<ContentPresenter
Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
</ControlTemplate>
UserControl's ControlTemplate
<ControlTemplate TargetType="UserControl">
<Border BorderBrush="{TemplateBinding Border.BorderBrush}"
BorderThickness="{TemplateBinding Border.BorderThickness}"
Background="{TemplateBinding Panel.Background}"
Padding="{TemplateBinding Control.Padding}"
SnapToDevicePixels="True">
<ContentPresenter
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"
Content="{TemplateBinding ContentControl.Content}" />
</Border>
</ControlTemplate>
Basically, the UserControl class is there for convenience. It enables us to build little parts of the UI from already existing controls, whereas ContentControls are really for creating new controls, generally with a single purpose and/or functionality.
I read a book that had a good explanation of this and by good luck, someone has 'put a copy of it online'. From the linked book:
The UserControl class is a container class that acts as a “black box” container for a collection
of related controls. If you need a set of three controls to always appear together and
be allowed to easily talk to each other, then a likely candidate for making that happen is
the UserControl class.
Then relating to whether to create a CustomControl:
The following is a summary of the decision process:
Use the framework as much as possible. WPF provides a variety of extensible
controls, so make sure that the functionality you want doesn’t already exist in a
WPF control.
In many cases, the data structure you’re working with requires different visual representation.
Using ControlTemplates and DataTemplates can often get you the functionality
you need.
Look at ValueConverters to see whether they can help bridge the gap between the
stock functionality and what you need.
Finally, see whether you can’t extend existing behavior with attached properties.
Take a look for an in depth answer to your question:
WPF Control Development Unleashed
UPDATE >>>
#MarqueIV, to answer your question more directly: The UserControl class is provided to us for convenience. That's it. If you add a WPF CustomControl into your project, you will see that it has no XAML file. This means that you have to design you control markup in a file called Generic.xaml in the Themes folder. The UserControl class gives us a XAML file so that it is easier to create them... so it is more convenient... that's it. That's the reason.
One thing that is different from ContentControl is that UserControl overrides the OnCreateAutomationPeer method, you might look for that. Maybe it has some different UI-behaviors than the ContentControl.
This method creates an UserControlAutomationPeer-instance.
ContentControl
A ContentControl directly derives from Control class.
It hosts a single element which can be a container (eg Grid, Stackpanel, ...) hosting itself several elements (eg StackPanel with TextBlock and Image children).
Its appearance can be modified through a DataTemplate.
See MSDN Remarks section.
UserControl
A UserControl derives from ContentControl.
It does NOT support templates, thus no customization.
It does not catch focus automatically like a Window would.
Still in the MSDN Remarks section.
UserControl is a composite control. It has similar concept with UserControl in ASP.NET Webforms. This means it's a control that is composed from many controls. In WPF, creating user control has supports for designer in Visual Studio 2008 and above.
ContentControl is a control that is intended to have a single control as its content.
For more information:
http://msdn.microsoft.com/en-us/library/system.windows.controls.contentcontrol.aspx
UserControl and ContentControl maybe the same implementation but the use case are not the same.
we need to answer two questions when to use UserControl or CustomControl?
and when to use ContentControl?.
so when to use a UserControl or CustomControl?
Whenever I want to have a reusable piece of UI
for example if I want to have a FileDialogBrowser meaning a button with a TextBlock next to it so whenever i press the button and a user chooses a file i will show the chosen file in the TextBlock.
same but not exactly goes for customControl however here we want to do something more sophisticated, anyway this is not the issue.
so when to use ContentControl?
this is a little tricky to say but let's say we want to have progressBar with a message
so we can inherit from BusyIndicator or Border, however if we use a ContentControl we have control which can Control the content inside it. we can have it wrapping around other xaml elements.
hope this helps
I've got a ContentControl which has a style containing a border and other visual decorations. I want these decorations to disappear when the content is collapsed, so I figured I have to set the visibility of the ContentControl to collapsed in this case. I got this style for my ContentControl decoration:
<Style x:Key="DecoratedItem1" TargetType="{x:Type c:DecoratedItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:DecoratedItem}">
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="2" CornerRadius="2">
<StackPanel Orientation="Horizontal">
<Image Source="/Images/file.png"/>
<ContentPresenter Name="wContent"/>
</StackPanel>
</Border>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=wContent, Path=Content.Visibility}" Value="Collapsed">
<DataTrigger.Setters>
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The DecoratedItem class is just a subclass of ContentControl with additional DependencyProperties which are not relevant to this issue, I just wanted to note that I already have a subclass to which I could add code, if necessary.
This works when the content of the ContentControl is a UIElement, however if the content is generated by a DataTemplate it complains about not being able to find the Visibility property.
<!-- works -->
<c:DecoratedItem Style="{StaticResource DecoratedItem1}">
<TextBlock Text="ABC" Visibility="Collapsed"/>
</c:DecoratedItem>
<!-- doesn't work -->
<c:DecoratedItem Style="{StaticResource DecoratedItem1}" Content="ABC">
<c:DecoratedItem.Resources>
<DataTemplate DataType="{x:Type clr:String}">
<TextBlock Text="{Binding}" Visibility="Collapsed"/>
</DataTemplate>
</c:DecoratedItem.Resources>
</c:DecoratedItem>
The error for the second case diplayed in the debug output window is:
System.Windows.Data Error: 40 : BindingExpression path error:
'Visibility' property not found on 'object' ''String' (HashCode=-885832486)'.
BindingExpression:Path=Content.Visibility;
DataItem='ContentPresenter' (Name='wContent');
target element is 'DecoratedItem' (Name='');
target property is 'NoTarget' (type 'Object')
I understand why this happens, but don't know how to fix my style to work as I want it. I don't mind adding helper code to the DecoratedItem subclass if necessary. Any idea how to fix this?
[edit 1]
Some more explanation in regard to the proposed answer:
I can't enforce that the Content is always an UIElement. This is a model-view design after all, and of course I simplified the example a lot. In the real project the content is a model selected from the DataContext, which can be of several different types, and the DataTemplate builds a presentation for that model. Some of the DataTemplates decide (depending on model-state) that there is nothing to present and switch Visibility to Collapsed. I would like to propagate that information to the decorating container. The example above really just presents the problem and not the motivation, sorry.
[edit 2]
Not sure how knowing more about the model would help the problem, but here we go. The data in the Content field doesn't have much in common since it can be a lot of things, this DecoratedItem is supposed to be reusable to give a common visual style to items shown on some forms. Content can be stuff like work items whose DataTemplate collapses them if they are disabled; other kinds of Content can be incomplete and get collapsed. Of course other kinds never may get collapsed.
But note that the data model doesn't really have much to do with the question, which still is how to bind against the Visibility of the expanded content element (after possibly exposing it through the subclass in a bindable way).
There are a couple of ways of describing what's wrong. In the first, working example:
<c:DecoratedItem Style="{StaticResource DecoratedItem1}">
<TextBlock Text="ABC" Visibility="Collapsed"/>
</c:DecoratedItem>
the Content property of the ContentControl is set to be a TextBlock, which is a UIElement with a Visibility property. (This assumes that you have not changed the ContentPropertyAttribute of your derived class DecoratedItem to be something other than Content). Thus, your DataTrigger binding can correctly evaluate:
<DataTrigger Binding="{Binding ElementName=wContent, Path=Content.Visibility}" Value="Collapsed">
Contrast the working case with the failing one:
<c:DecoratedItem Style="{StaticResource DecoratedItem1}" Content="ABC">
in which the Content property is set to an instance of a String, which does not have a Visibility property.
The other way to describe what's wrong is to note that, even though you supply a DataTemplate for the case of Content being a String, Content is still a String and still does not have a Visibility property. In other words, the statement that the Content is generated by the DataTemplate is incorrect -- your DataTemplate just told the control how to display Content of type String.
The general answer to your question is that, if you want the DataTrigger in DecoratedItem1 bound with a Path of Content.Visibility, you need to make sure the content you put in it is always a UIElement. Conversely, if you want to be able to put any sort of Content into the control, you need to trigger off of something else.
The specific answer to your question, strictly, relies on your broader intent (in particular, on how the Visibility of the Content of your control will be set/modified). A couple of possibilities:
if you really want your DataTrigger binding of the form, "Content.Visibility", make sure that the Content is always a UIElement. For instance, use the working form of the style and then bind the Text of TextBlock to something appropriate. However, this doesn't fit so well with the idea of your derived control as a ContentControl, so...
your DataTrigger could probably bind to something else. It seems like, from the way the question is formed, that there is some other property or code-behind that will control whether the various content entities are Visible or not.
finally, you could add an additional DataTrigger to the TextBlock. This DataTrigger would set the visibility of its parent based on its own visibility. Then, bind the DataTrigger in style DecoratedItem1 with Path "Visibility" instead of "Content.Visibility", essentially chaining together Visibilities manually.
Edit
Based on what you've described about how you want to use this, it sounds like you need to consider the visual tree. You might augment your DecoratedItem control to have the following functionality: if all its visual children that are UIElments have a visibility of Collapsed (or if it has no visual children), it is also Collapsed (or, whatever logic makes sense for the desired functionality in terms of the Visibility of its visual children). You'd need to use the VisualTreeHelper class from code -- in particular, the GetChildrenCount and GetChild methods. You'd also, in your DecoratedItem class, override OnVisualChildrenChanged (while still calling the base class method) so that you can get UIElement.IsVisibleChanged events for the visible children.
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.
I have a user control that applies a style to button, with the style containing a ControlTemplate section. Within the ControlTemplate, there are various UI elements such as an Ellipse and a Path.
If I give those elements -- the Ellipse and Path -- a name with x:Name, can I access them from code behind?
It appears the style's Ellipse and Path are not visible because I get a compile error (C#).
Am I going about this the wrong way?
Because a template can be instantiated multiple times, it's not possible to bind a generated member via x:Name. Instead, you have to find the named element within the template applied to a control.
Given simplified XAML:
<ControlTemplate x:Key="MyTemplate">
<Ellipse x:Name="MyEllipse" />
</ControlTemplate>
You would do something like this:
var template = (ControlTemplate)FindResource("MyTemplate");
template.FindName("MyEllipse", myControl);
Or even more simply:
var ellipse = (Ellipse)myControl.Template.FindName("MyEllipse", myControl);
You can read about FrameworkTemplate.FindName.
Some examples and discussion here, here and here.