I am developing a Silverlight 4 out of browser application using a standard ComboBox and I need to press the tab key twice to move over the control. The other controls (TextBox, RadioButton etc) in the data entry form are all behaving normally, i.e. only a single tab is required.
I created a simple sample application and found that the ComboBox was behaving correctly so there is something special about my real application that is causing the problem. I suspect the problem is due to the fact that I am using the AccentColor Theme. I've had a number of problems with these themes and have come to realise that they should be considered as samples of what is possible. Their quality is not good enough for use in a production application as this Introducing the new Silverlight 4 themes blog post suggests.
I am asking this question so that I can answer it myself to capture the solution for future reference.
The AccentColor Theme creates an implicit Style for a ComboBox which includes setting the Template property. This template uses a ToggleButton with a custom style that wraps the ContentPresenter inside a ContentControl for some reason (styling?) and by default the IsTabStop property is true. Explicitly setting this property to false restores the intuitive behaviour of a single tab to move over the control.
<ContentControl VerticalAlignment="Center" IsTabStop="False">
<ContentControl.Foreground>
<SolidColorBrush x:Name="ContentPresenterWrapperColor" Color="{StaticResource BaseColor1}" />
</ContentControl.Foreground>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ContentControl>
The standard ComboBox template does not include this ContentControl which explains the difference in behaviour. I am not familiar enough with the AccentColor theme to know if it would be better to remove the ContentControl altogether or if it is required for the custom visual styling.
FYI Silverlight Spy was a great help in tracking down the problem in the behaviour, even if it is a bit pricey imho :-)
Related
I have a control which template contains a generic ContentPresenter. This ContentPresenter will normally contain an AccessText (possibly amongst other things), whose AccessKey should focus the whole control. How do I do that?
Here is a specific but much simplified example to my case. (I know it could be implemented in several other ways (e.g. UserControl), but I need it this way for various reasons). I just made it up, there are minor omissions; the point is only to show the idea.
Imagine I want to add a header to the TextBox. I subclass it into a TextBoxEx and add two dependency properties Header and HeaderTemplate, similarly to HeaderedContentControl. (Code is trivial and omitted).
Then I assign a template to my TextBoxEx along these lines:
<ControlTemplate TargetType="TextBoxEx">
<StackPanel>
<ContentPresenter Name="PART_Header"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
RecognizesAccessKey="True" />
<TextBox ... /> <!--or some other components with similar functionality-->
</StackPanel>
</ControlTemplate>
When using this control, I might do something like this:
<TextBoxEx Template="{StaticResource TemplateAbove}" Header="_Test">
<TextBoxEx.HeaderTemplate>
<DataTemplate>
<AccessText Text="{Binding}" FontSize="14" />
</DataTemplate>
</TextBoxEx.HeaderTemplate>
</TextBoxEx>
(Of course, in reality it is done via styles and is more elaborate, but I tried to conflate it to a minimal code).
Now, this all renders fine. The only problem is that when I press Alt+T, nothing happens. (Except for underlining the T). What I want is for the control to get focus - which is the usual action OnAccessKey.
Typicaly, such things are done using a Label, which has the Target property, which can point to the control to be focused. But here at the ControlTemplate level, I don't know what header template the user will use; it may not have anything AccessText-related at all, and I don't want to restrict it in any way. Yet if such control is used, I want the access key to work. Supposedly, ContentPresenter.RecognizesAccessKey should help, but how do I tell it what exactly to do, i.e. what (sub-)control to focus?
Or is there another easier approach (but still, on a template level)?
P.S. The problem can be solved by replacing the ContentPresenter with Label as follows:
<Label Name="PART_Header"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
Target="{Binding RelativeSource={RelativeSource TemplatedParent}} />
But I feel this is not the 'right' way to do it and I'm still missing something fundamental, so I'll happily listen to any suggestions.
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 want to make a bigger checkbox in WPF.
I've discovered that I need to do a control template, one example of which is found here:
http://msdn.microsoft.com/en-us/library/ms752319.aspx
If I use that code the checkbox doesn't resemble the default look. All I want to do is change the Border Width & Height attributes.
I need a control template that looks exactly like the default, from there I will just change the Width and Height. Does anyone know where I can find one? Or a better approach?
How about this?
<CheckBox>
<CheckBox.LayoutTransform>
<ScaleTransform ScaleX="2" ScaleY="2" />
</CheckBox.LayoutTransform>
</CheckBox>
You can use double values for ScaleX and ScaleY if the integer values are not exactly what you want.
Here is a possible solution found on msdn:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/98cf8a65-f4ca-4ff5-9851-c2989b91a013
The default ControlTemplates can be found on MSDN (see Default WPF Themes link).
Make sure to add the respective themes namespace to your xaml file to reference the necessary theme controls.
<theme:BulletChrome Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
RenderMouseOver="{TemplateBinding IsMouseOver}"
RenderPressed="{TemplateBinding IsPressed}"
IsChecked="{TemplateBinding IsChecked}"/>
I am not sure if you can just specify it generically, you might need to add references too.
The problem here is that you cannot really specify a border size either since the control encapsulates it.
The best solution I found is to wrap it in the ViewBox:
<Viewbox Height="46" HorizontalAlignment="Left" >
<CheckBox Content="Some Content"/>
</Viewbox>
You can use visual tree of the checkbox and when the elements you want exist, change them at runtime by explicitly setting the Width and Height. Use Peter Blois' snoop or some equivalent to see if there are named elements you can access with FindName; if not you will have to guess (e.g., some styles might have two Border elements and you must pick one) and walk the visual tree explicitly.
Keep in mind that your code should do nothing if you don't find the elements you are looking for.
<Border BorderBrush="#C4C8CC" BorderThickness="0,0,0,1">
<TextBlock x:Name="SectionTitle" FontFamily="Trebuchet MS" FontSize="14" FontWeight="Bold" Foreground="#3D3D3D" />
</Border>
I have to use the same above format at many places in a single xaml page, so for this i created a usercontrol and defined the above code inside it.
So my question is,
What i am doing is it right approach?
Will it make the page to load slower then the above code used as it is without defining it in a new user control?
I doubt you would notice a difference. However a lighter and more flexiable approach would be to use a Templated Control instead of a UserControl. Its a little more technical but results in a tighter implementation.
How many is "many" anyhow?
I'm trying to use a StaticResource in a ControlTemplate for a custom object, and whenever that object is rendered, the application crashes. As you can see in the code below, I define both the template and the resource in App.XAML. I've been doing a bit of searching to see if/why this isn't allowed, but have had no luck so far.
<Color x:Key="PersonBackground">#FF003B00</Color>
<ControlTemplate x:Key="PersonTemplate" TargetType="this:Person">
<Border Background="{StaticResource PersonBackground}" BorderBrush="White"
BorderThickness="2" CornerRadius="10" MinHeight="70" MinWidth="120">
...
</ControlTemplate>
If anyone could explain why this isn't allowed or what I'm doing wrong (or, best yet, a better way to do custom theming in Silverlight), I would greatly appreciate it.
Edit: I feel like I should specify that I'm mostly just interested in being able to set the color scheme in one place; the rest of the theme won't need to change as much.
Instead of Color, can you try using a SolidColorBrush
<SolidColorBrush x:Key="PersonBackground" Color="#FF003B00"/>