ContentPresenter issue - c#

I have a ContentControl on which I have a grid. Column 1 will be a TreeView, Column 2 is a splitter and I have set column 3 to be a ContentPresenter. Defined as such:
<ContentPresenter Grid.Column="2" Name="PlaceHolder" />
If I then add my ContentControl to a Page and add an item to the ContentControl it does not just appear in column 2 but instead spans the entire width of the ContentControl. This is how I add something to the ContentPresenter:
<MyNamespace:MyControlName>
<Button>Hello world!</Button>
</MyNamespace:MyControlName>
How can I get the button to only appear in column 2 and have the TreeView in column 0 appear as normal?
Thanks in advance.

You are setting your ContentControl.Content property in the ContentControl to your Grid, then when you use the control you are overwriting ContentControl.Content to set it as a Button instead
You need to set your Grid to be the ContentControl.ContentTemplate instead of the actual Content, and modify your ContentPresenter show the Content property
<ContentControl ...>
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid>
...
<ContentPresenter Grid.Column="2" Name="PlaceHolder"
Content="{TemplateBinding Content}" />
...
</Grid>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>

Related

How to change dependency property behavior in UserControl

I have a WPF user control which contains a TextBox and some buttons. Now I want to change the way in which the VerticalContentAlignment property of the user control is handled in order to change only the vertical content alignment of the contained text box and not of the user control itself. So how can I override the VerticalContentAlignment dependency property of the user control in order to achieve my desired behavior?
You could write a ControlTemplate for the UserControl that simply ignores the VerticalContentAlignment property. Then bind the TextBox's VerticalAlignment to the VerticalContentAlignment property of the UserControl, e.g. by a RelativeSource Binding.
<UserControl ...>
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</ControlTemplate>
</UserControl.Template>
<Grid Background="AliceBlue">
<TextBlock
Text="Hello"
VerticalAlignment="{Binding VerticalContentAlignment,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</Grid>
</UserControl>
Test case:
<Grid>
<local:MyUserControl
Height="100"
VerticalAlignment="Center"
VerticalContentAlignment="Bottom"/>
</Grid>

Accessing element attribute value from its template

I have a combobox defined in xaml:
<ComboBox Width="100"/>
This ComboBox, along with all other combobxes I have, is styled with a ControlTemplate which I copied and edited some colors and such in it.
<ControlTemplate TargetType="{x:Type ComboBox}">....
In this control template, how can I access the value of the Width attribute from the element above?
So for example:
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid Width="??{Binding WidthValue}??" >....
Where the {Binding WidthValue} is 100, from the Width="100" above.
<Grid Width="{TemplateBinding Width}">
P.S. you'll often see this used in the default control templates for controls for attributes like Padding, Margin, and SnapsToDevicePixels
You can use TemplatedParent binding
<Grid Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}">
but you shouldn't need to as Grid should stretch so if you limit ComboBox to 100 you should automatically limit Grid inside
EDIT
If you need to bind to width then I would suggest to bind to ActualWidth instead of Width
<Grid Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActualWidth}">
as Width does not need to be defined in all controls where this template is used

want to create Group Template user control in silverlight

I want to create multiple groups of control(s) within a rectangular border. where each group will be containing control within it, surrounded by rectangular border and a header (optional) is to be placed over each child group's top-left above its border.
So, I created a class GroupLayout, each child element within this have to create its own new group. I created Header as an attached property.
Syntax making use of template is as:-
<GroupLayout Orientation = "Vertical">
<DataGrid GroupLayout.Header= "Group 1" />
<Grid GroupLayout.Header= "Group 2" />
-------So On--------
</GroupLayout>
as above given, DataGrid and Grid both should form there own two groups with vertical orientation. each child element should create its own new group.
So, I tried this as User Control:-
<Style TargetType = "GroupLayout">
<Setter.Property>
<ControlTemplate TargetType="GroupLayout">
<StackPanel>
<Border x:Name="MainParentGroupBorder">
<StackPanel>
<ContentPresenter Content = "{TemplateBinding HeaderLabel}" />
<Border x:Name="ChildGroupBorder">
<ContentPresenter Content = "{TemplateBinding Content}" />
</Border>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Property>
</Style>
In code behind I'm driving from ItemsControl.
But, this is not working as required. Now after a lot of efforts, I think I have to implement ItemTemplate in Xaml here. but I'm not able to do so to get the required result. Please help me.
Thanks,
GK Prajapati
It looks to me like you're re-inventing the wheel.
each group will be containing control within it, surrounded by rectangular border and a header (optional) is to be placed over each child group's top-left above its border
This is perfectly covered by an existing control: HeaderedContentControl. All you have to do is provide an appropriate control template for it. I suggest something like this:
<ItemsControl>
<controls:HeaderedContentControl Header="Group 1">
<DataGrid />
</controls:HeaderedContentControl>
<controls:HeaderedContentControl Header="Group 2">
<Grid />
</controls:HeaderedContentControl>
</ItemsControl>
Now, give the HeaderedContentControl the appropriate template:
<Style TargetType="controls:HeaderedContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:HeaderedContentControl">
<StackPanel>
<Border x:Name="MainParentGroupBorder">
<StackPanel>
<ContentPresenter Content="{TemplateBinding Header}" />
<Border x:Name="ChildGroupBorder">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Edit
Expanding on the above -- if you need the specific syntax of using an attached property GroupLayout.Header, then I suggest having your GroupLayout class override the ItemsControl.GetContainerForItem method. Have it return an instance of HeaderedContentControl:
protected override DependencyObject GetContainerForItemOverride()
{
return new HeaderedContentControl();
}
Now, you can use another ItemsControl override -- PrepareContainerForItemOverride -- to pass along your attached property:
protected virtual void PrepareContainerForItemOverride(DependencyObject element, Object item)
{
// get the attached property from the ItemsControl item
string header = ((FrameworkElement)item).GetValue(GroupLayout.Header) as string;
// set the container's "Header"
((HeaderedContentControl)element).Header = header;
}
Now you can use the exact XAML syntax you need:
<GroupLayout Orientation = "Vertical">
<DataGrid GroupLayout.Header= "Group 1" />
<Grid GroupLayout.Header= "Group 2" />
</GroupLayout>

Difference between Templates

What is the difference between
ControlTemplate
DataTemplate
HierarchalDataTemplate
ItemTemplate
Control Template
A ControlTemplate specifies the visual structure and visual behavior of a control. You can customize the appearance of a control by giving it a new ControlTemplate. When you create a ControlTemplate, you replace the appearance of an existing control without changing its functionality. For example, you can make the buttons in your application round rather than the default square shape, but the button will still raise the Click event.
An Example of ControlTemplate would be
Creating a Button
<Button Style="{StaticResource newTemplate}"
Background="Navy"
Foreground="White"
FontSize="14"
Content="Button1"/>
ControlTemplate for Button
<Style TargetType="Button" x:Key="newTemplate">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="RootElement">
<!--Create the SolidColorBrush for the Background
as an object elemment and give it a name so
it can be referred to elsewhere in the control template.-->
<Border.Background>
<SolidColorBrush x:Name="BorderBrush" Color="Black"/>
</Border.Background>
<!--Create a border that has a different color by adding smaller grid.
The background of this grid is specificied by the button's Background
property.-->
<Grid Margin="4" Background="{TemplateBinding Background}">
<!--Use a ContentPresenter to display the Content of
the Button.-->
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="4,5,4,4" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
More about ControlTemplate
Data Templates
Data Template are a similar concept as Control Templates. They give you a very flexible and powerful solution to replace the visual appearance of a data item in a control like ListBox, ComboBox or ListView. WPF controls have built-in functionality to support the customization of data presentation.
An Example for the DataTemplate would be
<!-- Without DataTemplate -->
<ListBox ItemsSource="{Binding}" />
<!-- With DataTemplate -->
<ListBox ItemsSource="{Binding}" BorderBrush="Transparent"
Grid.IsSharedSizeScope="True"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Key" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBox Grid.Column="1" Text="{Binding Value }" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
More about DataTemplates and Triggers
Item Templates
You use the ItemTemplate to specify the visualization of the data objects. If your ItemsControl is bound to a collection object and you do not provide specific display instructions using a DataTemplate, the resulting UI of each item is a string representation of each object in the underlying collection.
An Example for Item Template would be
<ListBox Margin="10" Name="lvDataBinding">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text=", " />
<TextBlock Text="Age: " />
<TextBlock Text="{Binding Age}" FontWeight="Bold" />
<TextBlock Text=" (" />
<TextBlock Text="{Binding Mail}" TextDecorations="Underline" Foreground="Blue" Cursor="Hand" />
<TextBlock Text=")" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When you set an ItemTemplate on an ItemsControl, the UI is generated as follows (using the ListBox as an example):
During content generation, the ItemsPanel initiates a request for the ItemContainerGenerator to create a container for each data item. For ListBox, the container is a ListBoxItem. The generator calls back into the ItemsControl to prepare the container.
Part of the preparation involves the copying of the ItemTemplate of the ListBox to be the ContentTemplate of the ListBoxItem.
Similar to all ContentControl types, the ControlTemplate of a ListBoxItem contains a ContentPresenter. When the template is applied, it creates a ContentPresenter whose ContentTemplate is bound to the ContentTemplate of the ListBoxItem.
Finally, the ContentPresenter applies that ContentTemplate to itself, and that creates the UI.
If you have more than one DataTemplate defined and you want to supply logic to programmatically choose and apply a DataTemplate, use the ItemTemplateSelector property.
The ItemsControl provides great flexibility for visual customization and provides many styling and templating properties. Use the ItemContainerStyle property or the ItemContainerStyleSelector property to set a style to affect the appearance of the elements that contain the data items. For example, for ListBox, the generated containers are ListBoxItem controls; for ComboBox, they are ComboBoxItem controls. To affect the layout of the items, use the ItemsPanel property. If you are using grouping on your control, you can use the GroupStyle or GroupStyleSelector property.
For more information, see Data Templating Overview.
ControlTemplaes defines the "look" and the "behavour" of a control. A button is rectangular by default. A ListBox has a white background by default. These are all defineed by Control's ControlTemple.
A DataTemplae helps a Control with Layout of Data that it holds. If a list of Users are added to listbox and you would like UserName to show up before UserPassword then you will define this inside a DataTemples. DataTemples is assigned to the ItemTemplate (4) Property of the ListBox.
HierarchalDataTemplte is same as DataTemples except that it deal with Hierarchal Data Source. It is commonlly used with TreeView Control.

WPF Custom "Toolbox" Selector control

I am trying to create a custom control, which behaves a bit like one of the "rows" in the toolbox in Expression Blend.
When closed, it displays the first of its items, and when the user holds the mouse down over it for a second or so, it "expands" to reveal the other items in a popup excluding the item which is currently selected (the item which was clicked on still remains visible, but is not grouped with the others).
I have managed to make a control which inherits from ContentControl displays one item and then reveals the others when expanded, but the item which is displayed first does not change when one is clicked.
The template is as follows:
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter Content="{TemplateBinding MainItem}" /> // The item that is seen even when not expanded
<Popup Name="Popup" Placement="Right" IsOpen="{TemplateBinding IsExpanded}" AllowsTransparency="True" Focusable="False" PopupAnimation="Fade">
<Border Name="SubmenuBorder" SnapsToDevicePixels="True" BorderThickness="0" >
<StackPanel Orientation="Horizontal" IsItemsHost="True" /> // The items only seen when expanded
</Border>
</Popup>
</Grid>
At the moment, I have to manually set the MainItem in XAML and it is not a member of the Items property of the ContentControl.
Is there a way to achieve this functionality where all items are part of the Items collection and are automatically not shown when an IsSelected property or something is set, and are then shown where the current MainItem is shown when this is the case?
I tried changing it to a Selector and using a filter on the Items to achieve this and binding the first ContentPresenter to the SelectedItem but it didn't seem to work.
If possible, the order of the items only visible when the control is expanded should be the same as the order in which they are laid out in XAML, with the currently selected item missing.
Thanks
try using the ItemContainerStyle to set the visibility of the selected item to 'Hidden'. You'd need a BoolToVisibilityConverter (you can write one easily or get one from the WPFToolkit) to get the correct value
<ContentPresenter Content="{Binding ElementName=selector, Path=SelectedItem}" />
<ComboBox x:Name="selector">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Visibility" Value="{Binding Path=IsSelected, Converter={StaticResource boolToVisibilityConverter}}" />
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SomeProperty}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>ComboBoxItem
I managed to solve the problem by using a VisualBrush to create a preview of the item without having to move it within the visual tree.

Categories