What exactly does Panel.IsItemsHost do? - wpf

What is the Panel.IstItemsHost attached property used for?
I see plenty of examples of people setting it on the ItemsContainer template for an ItemsControl, but the un-documentation over at MSDN does not explain why or what advantages setting property confers.

Say I have an ItemsControl. I want to use a custom panel that swoops items in and out as you scroll; its called a SwoopPanel. Now, how do I tell the ItemsControl to use my SwoopPanel to contain the templates it creates?
The quick way is to set the ItemsPanel on the ItemsControl:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<lol:SwoopPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
However, sometimes that doesn't work for you. Maybe you wish to customize how the SwoopPanel is presented in the UI, and the only way to get around this is to change the control template of the ItemsControl. Now you can add your SwoopPanel directly to the control template and, using the property, mark it as the ItemsHost that the ItemsControl will put all the templated items it creates.
<Style TargetType="ItemsControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<Border CornerRadius="5">
<ScrollViewer VerticalScrollBarVisibility="Hidden">
<lol:SwoopPanel IsItemsHost="True"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Do you have to do it one way or the other? No. Is one more advantageous than the other? Well, the second way allows you more control of the UI, the first way is easier. Take your pick, really. I've never personally done it the second way, but I think there might be a couple of places where it might be useful.

More Explanation, Please!
While all of the above answers are technically correct, I feel they don't illustrate how IsItemsPanel correlates to the ControlTemplate and the presence (or absence) of an ItemsPresenter and the corresponding ItemsPanel property which it uses. This answer will attempt to shed light on those things and hopefully clarify when you should, or shouldn't use each.
ItemsControls, Panels and IsItemsHost, Oh my!
An ItemsControl is simply a control that displays a collection of items. It does this by first generating individual containers* to represent the items visually, then it hands those containers over to a specific panel to be laid out for display on screen. As items are added or removed, the ItemsControl adds or removes the corresponding containers from the panel as needed.
* Note: If an item is already an instance of the container type (as determined by the result of the IsItemItsOwnContainer override of the ItemsControl)--i.e. you add a ListBoxItem instance to the Items collection of a ListBox--that item is simply passed through as-is directly to the panel, acting as its own container.
The specific panel used for hosting and laying out the containers is the first one found in the ItemControl's control template that has its IsItemsHost property set to 'True'.
There are two ways to specify which panel that is:
By inserting an ItemsPresenter into the ControlTemplate to act as a placeholder for the panel specified by the ItemsPanel property. (This is the most common way.)
By inserting a Panel directly into the ControlTemplate and explicitly setting its IsItemsHost property to True.
But which do you use and why? Read on to find out!
ItemsPresenter - "Have It Your Way!"
In a typical ControlTemplate for an ItemsControl such as a ListBox, the template specifies an ItemsPresenter somewhere inside of it. Here's a simplified excerpt showing how it's used:
<Border x:Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<ItemsPresenter />
</ScrollViewer>
</Border>
As you can see, there is an ItemsPresenter specified inside of a ScrollViewer in the middle of the template. What you don't see however is an actual panel to lay out the items.
So if there's no panel defined in the template, where does it come from? That's where the ItemsPanel property comes in. As its name suggests, this property defines which panel will be used to host and lay out the items. It doesn't however say where that panel appears in the ControlTemplate.
That brings us back to the ItemsPresenter. In short, it's a placeholder that essentially says "When the ItemsPanel property is set, I'll insert that panel here and set its IsItemsHost to True automatically."
The advantage of using an ItemsPresenter in the template for your ItemsControl is that you're making it very easy for consumers of your control to replace the panel without having to completely re-template your entire control.
IsItemsHost - "My Way or the Highway!"
However, what if you don't want someone to be able to change out your panel because your control depends on some custom panel implementation and anything else will break the functionality? In that case, you don't use an ItemsPresenter in your template. You instead need to specify the exact panel you want to use.
This is where IsItemsHost property comes into play. When set on a panel in the ControlTemplate, it tells that ItemsControl to use that specific panel to host the generated containers, regardless of what ItemsPanel is set to. The ItemsPanel property is essentially ignored.
Another benefit of specifying the panel directly in the template is you can then name it and access it just like any other template part.
Here's the same example as above, but rather than an ItemsPresenter, it hard-codes a SpecializedPanel to lay out the items. We indicate that's the panel we want to use to host the items by setting its IsItemsHost property to True and finally, we give it a name so we can access it directly from code.
<Border x:Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<SpecializedPanel name="PART_MainPanel" IsItemsHost="True" />
</ScrollViewer>
</Border>
In this case, because the template doesn't use an ItemsPresenter and instead directly includes a panel with its IsItemsHost set to True, there is no way for the user to change out that panel short of completely replacing the entire ControlTemplate. (As mentioned before, the ItemsPanel property is ignored.)
Bringing it all home...
To recap, if you're a control author and want to give consumers of your control the flexibility to swap out the panel used to lay out your items, then define your template for your ItemsControl using an ItemsPresenter. Make sure to also set the ItemsPanel property in the template to specify a default panel.
If however, want to 'lock' which panel your control uses, then do not use an ItemsPresenter in the ControlTemplate. Instead, specify the specific panel you want to use directly in the template, then set its IsItemsHost property to True.
Note: There's technically a third scenario, which is arguably more common: You're not a control author creating something to be consumed by other users, but rather are simply re-templating an ItemsControl (like say a ListBox) for some specialized use in your own application.
In that case, since you are the ultimate consumer of the control, you most likely won't have to worry about other consumers downstream needing to change out the panel, so it's completely fine to simply specify the panel directly in your template (again, setting its IsItemsHost true) and not worry about using an ItemsPresenter and its associated ItemsPanel property as the latter, while valid, would just add unnecessary complexity without any actual benefit.
Hope this clarifies exactly what's going on.

See http://msdn.microsoft.com/en-us/library/system.windows.controls.panel.isitemshost(v=vs.90).aspx
Essentially, what this post says is that if you are replacing the ControlTemplate of a ListBox and want a new layout, set IsItemsHost=true on some panel, e.g. a StackPanel. Then any items in the ListBox will be automatically added as children of the StackPanel. If the orientation of the ListBox is Horizontal, then the ListBox will be horizontal.
The other way is to set the ItemsPanel property of the ListBox to an ItemsTemplate and in that template you have a StackPanel. In this case the ListBox items will be added to the StackPanel children just as in the first case. However, you do not need to set IsItemsHost = true, it will have absolutely no effect. This is done for you by the fact that you are setting the ItemsPanel property.

Related

WPF borders and the controls within them

This post is about the controls contained within a WPF Border control. It's also about having a border that can appear and disappear without affecting the contained controls.
For the record, I'm using C# and WPF and most of the view stuff is using XAML. I also use MVVM although I'm not sure that's going to be related.
What I had planned for was a border around a control that I could make appear and disappear, for the effect of a highlight or something like that. But when I change certain properties of the Border, for example the Opacity or Visiblity, they impact on the contained controls. I have also tried changing the Background property to Transparent and that has not made a difference.
I do know that some controls have a Border property, but that's not really the case for my situation.
How can I do this?
Thanks
Try this:
<Grid>
<Border BorderThickness="2">
<YourControl />
</Border>
<Border Opacity="0.5" BorderBrush="Red" BorderThickness="2" />
</Grid>
This way you can change the opacity of the second border without affecting your control. The trick is that Grid ensures that both elements inside it have the same dimensions.
Also notice how your control is wrapped in another border with the same thickness but with no brush. This is to keep the second border from obscuring your control.

What's the difference betwen a UserControl and a ContentControl?

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

Collapse ContentControl if Content is Collapsed

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.

Moving Keyboard Focus Away from ListBox

I'm developing a program in WPF (VB) that relies on keyboard navigation only.
In my program, I have a listbox that displays up to 20000 items.
What I want is that when the listbox has keyboard focus, and I move to the bottom item that is visible (using ArrowDown), I want the focus to move to the next item outside the listbox. I'm using PgUp and PgDown to scroll the listbox contents, and text search to jump to items.
Is there a way to detect if the focused/selected item is the last/first visible item in the listbox?
If so, I could just use:
ListBox1.MoveFocus(New TraversalRequest(FocusNavigationDirection.Down))
I'd suggest that you don't do this, the user interface should behave consitesntly with other user interfaces in the operating system.
Your users would be better off if you come up with an alternate user interface that's consistent with how user interfaces behave on your target operating system.
It is a little clear from your explanation but either your looking for:
navigation to move outside of the listbox when the last item is selected.
when navigation is attempted beyond the last item in the list that the navigation pops out of the listbox.
If (1) is your objective there is probably a reasonable solution using triggers and or some custom code handling for events based on the selected item and selected item changed. I would have to agree with Tom if this is the case though and suggest that you do not implement it this way since the last item will never be selectable without focus being programatically removed.
If your instead looking to do (2) then it is my experience that the natural behavior of a ListBox is to move to the next control when the Tab key is pressed and I've tested this for the down arrow key as well and it works the same. When I get to the last item in the list focus pops out of the listbox and to the next control according to it's parent.
UPDATE: I have to withdraw my original comments as the behavior I described above do not describe the default behavior in WPF for a ListBox however it is the behavior you will see the behavior I described above (which I believe is the behavior your looking for) when implementing an ItemsControl and specifying an ItemTemplate. Take a look at the following example.
<ItemsControl ItemsSource="{Binding ElementName=TheWindow, Path=ListOStrings}">
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border BorderBrush="Magenta"
BorderThickness="1">
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
By chance, this just happens to have the behavior you described since each item in the list behaves almost like a control placed directly as a peer to all the other controls.
I use an easy trick to move the focus outside Listbox:
I disable the listbox so the focus moves automatically to the next control, then i enable the listbox again :)
Lst.IsEnabled = False
Lst.MoveFocus(New TraversalRequest(FocusNavigationDirection.Next))
Lst.IsEnabled = True

Resizing XAML properties

Is there a way to have XAML properties scale along with the size of the uielements they belong to?
In essence, I have a control template that I have created too large for it's use/ mainly because I want to use the same control with different sizes. The problem is that I can set the control size to Auto (in the ControlTemplate), however the properties of the intrisic template elements aren't resized: eg StrokeThickness remains at 10 while it should become 1.
It works fine when I apply a ScaleTransform on the template, but that results in a control that's too small when it's actually used: the width/height=Auto resizes the control to the proper size and then the scaletransform is applied. So I'm stuff with a sort of nonscalable control.
I'm a bit new to WPF, so there might be a straightforward way to do this...
Your description is a bit vague, but it sounds like you'll want to have a ViewBox with the Stretch set to Uniform as the root element of your control.
You could try to bind the width and height of the control inside the template to the width and height respectively of the templated control at runtime. Something like:
<Button>
<Button.Template>
<ControlTemplate TargetType={x:Type Button}>
<Border Width="{TemplateBinding Property=ActualWidth}"
Height="{TemplateBinding Property=ActualHeight}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
Note that the binding sources are the FrameworkElement.ActualWidth and FrameworkElement.ActualHeight properties of the templated control. These properties contain the width and height of a control after it has been rendered, which can be different from what has been specified in the Width and Height properties. This is because the values are ultimately calculated at runtime taking into account the size of any parent controls in the visual tree.
Too bad you can't create a control template for a StackPanel, DockPanel, Grid, or any other container.

Resources