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
Related
I have a WPF application where one of the main features is a 'layout' window. In the layout window, the user can place/position/resize LayoutElements.
The place/position/resize behavior of the icons is shared; they all act the same in terms of mouse-dragging and resizing. I've implemented this behavior and it's working as I intend.
An issue with my current approach is that I've both defined DataTemplates in my main Canvas window to tell the XAML how to render the various LayoutElement subclasses, and each LayoutElement subclass has its own XAML file to define how its appearance.
For example, in my main drawing/canvas XAML, I have:
<DataTemplate x:Key="trackTemplate">
<local:SizingOverlay MouseDown="IconMouseDown" MouseUp="IconMouseUp" MouseMove="IconMouseMove" MouseEnter="IconMouseEnter" MouseLeave="IconMouseExit">
<local:Track Width="128" Height="128" Canvas.Left="0" Canvas.Top="0" />
</local:SizingOverlay>
</DataTemplate>
The above is used by a template-lookup to say: when you have a Track LayoutElement, render it this way.
Additionally, I have Track.xaml which defines the look of the local:Track elements.
If I continue to add more LayoutElement types, I will have to keep adding more DataTemplates which basically say: for each element type, use the appropriate XAML file as the content for the SizingOverlay.
However, it seems like there should be a way to tell this DataTemplate that it contains LayoutElements, and it should use the contained concrete subclass of LayoutElement to get the XAML.
In other words: I have the abstract class LayoutElement and concrete representations of this class. How can I tell WPF to use the concrete subclass's XAML?
Here is what I've tried:
If I add an XAML layout to LayoutElement, then Track.xaml complains because it is a LayoutElement and it's conflicting with the LayoutElement.xaml definition.
If I don't have XAML for LayoutElement, then I can't do something like this:
<DataTemplate x:Key="trackTemplate">
<local:SizingOverlay MouseDown="IconMouseDown" MouseUp="IconMouseUp" MouseMove="IconMouseMove" MouseEnter="IconMouseEnter" MouseLeave="IconMouseExit">
<local:LayoutElement Width="128" Height="128" Canvas.Left="0" Canvas.Top="0" />
</local:SizingOverlay>
</DataTemplate>
Edit: Possible duplicate
I acknowledge this question is potentially a duplicate of the following:
How to create a common WPF base window style?
Abstract class on XAML-Window
Creating an abstract base class for windows in WPF
c# WPF XAML - Working with abstract UserControls
MyUserControl cannot be the root of a XAML file because it was defined using XAML
Inheriting from a UserControl in WPF
Using an Abstract Class as DataType in DataTemplates
However, I am not sure if the questions above contain complete/appropriate answers for my use-case. They contain quite a bit of hypothetical discussion with the answers saying "I think" this, "maybe" that.
I believe I've tried the solutions proposed in the other links (particularly the last one) and it compiled, but the concrete classes were not displayed as if it couldn't find the concrete class XAML and just gave up.
I will examine those answers, and if it's clear that they apply to this question, I will delete this question. Otherwise I will update this question with a new answer (or someone else can).
I was able to get it working by replacing my abstract-class XAML element with a ContentPresenter whose content is bound to the child class.
<DataTemplate x:Key="elementTemplate" >
<local:SizingOverlay MouseDown="IconMouseDown" MouseUp="IconMouseUp" MouseMove="IconMouseMove" MouseEnter="IconMouseEnter" MouseLeave="IconMouseExit">
<ContentPresenter Content="{Binding}"/>
</local:SizingOverlay>
</DataTemplate>
How can I use the properties of the controls that are inside a user control without having to use DependencyProperty?
Since, if for example I want to use all the properties of a button, I would have to declare all these?
And if there is another way without user control and it is the correct one, I would appreciate it if you answered it. (Google translator, sorry)
UserControl:
<UserControl x:Class="UserControls.UserControl01"
...
>
<Grid>
<Button x:Name="uc_btn" />
<TextBox x:Name="uc_txt" />
<DataGrid x:Name="uc_dtg" />
</Grid>
</UserControl>
Code using the UserControl:
<Window x:Class="UserControls.wnd02"
...
>
<Grid>
<local:UserControl01 uc_btn.Background="Red" uc_txt.Margin="10" uc_dtg.BorderThickness="5" Margin="90" />
<local:UserControl01 uc_btn.Background="Green" uc_txt.Margin="25" uc_dtg.BorderThickness="20" Margin="5" />
</Grid>
</Window>
It is not usual to do what you are asking.
Let's consider a usercontrol which is intended to work as if it is one single control. For example a time picker. This contains two sliders which increase/decrease hour and minute. You can also overtype in the hour and minute textboxes and there's a : between the two textboxes.
This usercontrol is all about the one property though. Time. You don't care what the minutes background is externally. If this changes it's internal to the usercontrol.
In this scenario you'd usually add a TimeSpan dependency property to the usercontrol and this is the only thing anything external to it uses.
Pretty much all commercial WPF development uses the MVVM pattern and that TimeSpan would be bound to a property in the parent view's viewmodel.
That's one scenario.
Another is where a usercontrol encapsulates a bunch of UI which is then re-usable.
Styling has scope so when you apply a style to say a Button in a window then that would apply to any Buttons in a usercontrol within it. Setting their properties.
There are also certain dependency properties marked as "inherits" whose values propogate down the visual tree.
One such is DataContext and it is this which most teams would use to deal with properties within a usercontrol.
Using MVVM there would be a MainWindowViewModel.
That would have (say ) a ChildUserControlViewModel property. That would be associated with usercontrol using a datatemplate specified datatype.
You'd then bind properties of whatever is in a usercontrol to properties of ChildUserControlViewModel or properties of MainWindowViewModel using RelativeSource binding.
ViewModel first is a common navigation and composition pattern for WPF. You should be able to find numerous blogs explain it better than I can in a SO post.
Here's one:
https://social.technet.microsoft.com/wiki/contents/articles/30898.simple-navigation-technique-in-wpf-using-mvvm.aspx
I'm using a control template to show validation errors on each of my controls using the built-in WPF's validation mechanism, and everything works fine. The controlTemplate looks like this:
<ControlTemplate x:Key="MyErrorTemplate" TargetType="{x:Type Control}">
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="3">
<AdornedElementPlaceholder Name="MyAdorner" />
</Border>
<Image Name="imgError"
Source="/MyAssembly;component/Images/ValidationIcon.png"
ToolTip="{Binding ElementName=MyAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
</StackPanel>
</ControlTemplate>
I have read that the validation mechanism wraps the validated control up with the control template (the default one or a custom one like above) whenever the control gets an error.
"When the WPF validation system detects an invalid control it creates
and adorner that holds a control (...), inserts a control into it and sets that control
template to the content of the Validation.ErrorTemplate attached
property.
It positions the adorner above the original control so that the
AdornedElementPlaceholder is exactly above the control and that let us
easily place the control template content relative to the original
control" (see more)
How can I perform this same behavior for another functionality? I mean use "MyErrorTemplate" without the WPF's validation system, is it possible?
so if I understand you correctly you want to have the same validation adorning thing without WPF's validation, right?
The approach then is actually to rebuild the components of WPF's validation system:
create a Dependency Property MyCustomErrorTemplate to hook up your template to the control
create a Dependency Property HasCustomError to enable showing the error
within MyCustomErrorTemplate_Changed hook up to the HasCustomError_Changed to enable showing/hiding of your adorner
create/copy the TemplatedAdorner Class that is then showing your Template
I recommend you use .NET Reflector or ILSpy to look at the following code to get some understanding of what's going on. This isn't actually very complex or hard to understand:
in PresentationFramework.dll:
System.Windows.Controls.Validation (especially the private static void ShowValidationAdornerHelper(DependencyObject targetElement, DependencyObject adornerSite, bool show, bool tryAgain)
MS.Internal.Controls.TemplatedAdorner (sadly this is internal, so you either have to copy it or use some Reflection on it)
Why not? You can have similar attached properties MyValidation.Errors and HasErrors and fill them with your custom logic. And you can have trigger that replaces ControlTemplate with ErrorTemplate when HasError is true. I think this simple approach will do that you need although i am not quite sure that i exactly understand that you need.
I want to create a custom control that extends a built-in control and then has a template that wraps that control with a container?
The C# class:
class ExtraBorderTextBox : TextBox {}
The Xaml:
<ControlTemplate>
<Border>
<TextBox/>
</Border>
</ControlTemplate>
That doesnt' work because the TextBox in the control template isn't my custom control, it is a second instance.
I need access to the properties and events on TextBox, having a different parent doens't make sense, I would have to replicate all of that in my class.
This is a simplified example; imagine Border being replaced with a ContentControl that has a 50 line control template for itself. I guess I want something like ContentPresenter (like I have in the ContentControl), but there isn't anything like a "ControlPresenter". Right? Am I missing something, or am I stuck with replicating my content control for the TextBox, or replicating the TextBox behaviour and presentation for my content control?
Thanks.
Update:
There is an answer here that does what I want, which is to copy the default template for System.Windows.Controls.TextBox. This will do what I want; I can insert my container into that. I was hoping that WPF provided a way that is more maintainable to do something like this, something like a adorner/decorator pattern.
Is there any way to make this better in some way? Would using something like Expression Blend make this so that I don't have to hand-edit the XAML pasted in from the webpage?
You could use the default control template as a base and modify it. The default control templates can be found here: http://msdn.microsoft.com/en-us/library/aa970773.aspx
If I understood you right, you want to inherit from TextBox, do some overriding, and use that new class in XAML.
If so:
1) declare the xmlns namespace at the top of your file:
<UserControl
...
xmlns:local="TheAssemblyWhereExtraBorderTextBoxResides"
...>
2) use your custom textbox:
<ControlTemplate>
<Border>
<local:ExtraBorderTextBox />
</Border>
</ControlTemplate>
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.