Setting ContentControl's background color in XAML - wpf

For the life of me I cant seem to figure out this simple task of setting the ContentControl's background color:
<ContentControl x:Name="Content03"
Width="130"
Height="130"
Canvas.Top="50"
Canvas.Left="400"
Background="Yellow">
<Ellipse Fill="YellowGreen" IsHitTestVisible="True">
</Ellipse>
</ContentControl>
Also tried doing this using styles but still doesnt work ;(

A ContentControl has no visual prescence in itself, but is a container for a child control. Setting some properties on this control (like fontsize etc) is usually only a way of having those properties propagate down the visual tree, so they van be picked up by child controls (those that support it).
The best thing to do is this:
<ContentControl x:Name="Content03"
Width="130"
Height="130"
Canvas.Top="50"
Canvas.Left="400">
<Grid Background="Yellow">
<Ellipse Fill="YellowGreen" IsHitTestVisible="True">
</Ellipse>
</Grid>
</ContentControl>

If you don't have to stick with ContentControl I suggest using Border instead.
When I've had the same problem, Border had the same Child property that I needed to only have one child and to easily switch it via code with a different object. Border uses Properties like Background correctly. Those Properties also work, if Child is null.
<Border x:Name = "Content03"
Width = "130"
Height = "130"
Canvas.Top = "50"
Canvas.Left = "400"
Background = "Yellow">
<Ellipse
Fill = "YellowGreen"
IsHitTestVisible = "True">
</Ellipse>
</Border>

I know this is old but you could also change the template of the ContentControl in the style. That can be overkill for some things, but in this case it's really just wrapping a ContentPresenter in a Border and some template binding:
<Style TargetType="ContentControl" x:Key="StPortal">
<Setter Property="Background" Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This gives you the ability to set background, etc. properties that borders have, AND gives the ability to set things like font family and size that ContentControl has and Border doesn't...

Related

How to get the content width of the TextBox except for width of the VerticalScrollBar?

I created HighlightTextBox that derived TextBox. And the code is as shown below.
<Style TargetType="{x:Type host:HighlightTextBox}">
<Setter Property="AcceptsReturn" Value="True" />
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="TextWrapping" Value="NoWrap"/>
<Setter Property="Foreground" Value="#00000000"/>
<Setter Property="FontSize" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=FontSize, Mode=TwoWay}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Name="textArea" TargetType="{x:Type host:HighlightTextBox}">
<Border BorderThickness="{Binding BorderTickness}"
BorderBrush="{Binding BorderBrush}"
Background="{Binding BackGround}">
<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
<host:TextCanvas x:Name="PART_RenderCanvas" ClipToBounds="True"
TextOptions.TextRenderingMode="ClearType" TextOptions.TextFormattingMode="Display"
LineHeight="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=LineHeight}"/>
<ScrollViewer x:Name="PART_ContentHost" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The important point is ControlTemplate.
As you can see, the content of the HighlightTextBox consists of TextCanvas and ScrollViewer.
The HighlightTextBox highlights the currently selected line by painting on the TextCanvas but it invasion the VerticalScrollViewer section as below.
I want to display it by not invasion the VerticalScrollViewer.
I think that maybe the cause is TextCanvas occupy entire section of TextBox.
So I tried to move TextCanvas into the ScrollViewer but this way make facing the run-time error that "PART_ContentHost can't have child element.
I think that another way is to get the content width of the TextBox except for the width of the VerticalScrollBar and binding it to the width of the TextCanvas. But I don't know how to get it.
What I should do to solve this problem?
If you have a better way or another way to solve then please let me know.
Thank you for reading.
Search the visual tree for your ScrollViewer's content presenter, that will give you the width of the content area itself:
var scrollViewer = yourTextBox.Template.FindName("PART_ContentHost", yourTextBox) as ScrollViewer;
var contentPresenter = UIHelper.FindChild<ScrollContentPresenter>(scrollViewer, String.Empty);
var width = contentPresenter.ActualWidth;
UPDATE: You can bind to the ScrollContentPresenter's content control directly like this:
<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
<TextBlock Text="{Binding ElementName=PART_ContentHost, Path=Content.ActualWidth}" Background="CornflowerBlue" VerticalAlignment="Top"/>
<ScrollViewer x:Name="PART_ContentHost" />
</Grid>
Keep in mind though that this gives you the area of the ScrollViewers content width which, in the example I've given above, will a bit smaller than the TextBlock's width due to the fact that the ScrollViewers content is a TextBoxView with a 2,0,2,0 margin:
To compensate for this you'll probably want to bind your Canvas margin to the TextBoxView margin (which in my case is a TextBlock rather than a Canvas):
<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
<TextBlock Text="{Binding ElementName=PART_ContentHost, Path=Content.ActualWidth}"
Margin="{Binding ElementName=PART_ContentHost, Path=Content.Margin}"
Background="CornflowerBlue" VerticalAlignment="Top" />
<ScrollViewer x:Name="PART_ContentHost" Padding="0" Margin="0"/>
</Grid>
This will keep your Canvas's alignment in the parent Grid the same as for the TextBoxView so that everything lines up properly:
(You could also just remove the TextBoxView's margin, but that's probably not what you want).

ContentControl in ControlTemplate does not show Border nor Background

I have a Button with a custom control template and a Grid as sibling. The Button should use the TriangleUpPath when the Grid is visible, otherwise it should use the TriangleDownPath. So far so good.
But I want that the Path has a Border around. So I thought I could just set the BorderBrush of the ContentControl, but this does not work. I don't see the Border. Same goes for the Background of the ContentControl.
Here is my minimal not working example:
<Button x:Name="_hideButton"
Grid.Row="0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Click="ToggleResultsVisibility">
<Button.Style>
<Style TargetType="Button" >
<Style.Resources>
<Path x:Key="TriangleUpPath"
Data="M 0,5 L 7,0 L 14,5"
Margin="3,3,0,0"
Stroke="Black"
StrokeThickness="1"/>
<Path x:Key="TriangleDownPath"
Data="M 0,0 L 7,5 L 14,0"
Margin="3,3,0,0"
Stroke="Black"
StrokeThickness="1"/>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ContentControl Margin="0,5,5,0"
Width="20"
Height="11"
BorderBrush="Magenta"
BorderThickness="1"
Background="Cyan">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Background"
Value="HotPink"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Visibility, ElementName=_resultsGrid}"
Value="Collapsed">
<Setter Property="Content"
Value="{StaticResource TriangleDownPath}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Visibility, ElementName=_resultsGrid}"
Value="Visible">
<Setter Property="Content"
Value="{StaticResource TriangleUpPath}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
<Grid x:Name="_resultsGrid"/>
As you can see, I even tried to set the Background in the Style (to HotPink) to no avail.
It helps, when I have a Border around the ContentControl.
But the question is: why do the Background-Property and the BorderBrush-Property of the ContentControl have no influence?
The default style for ContentControl does not contain a way of rendering the Border and Background properties, it just has a ContentPresenter.
You need to add a custom template for the content control yourself, containing the additional elements you need:
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
<ContentPresenter
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Cursor="{TemplateBinding Cursor}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</ContentControl.Template>
It's because BorderThickness/Brush aren't some magic properties that make the border magically appear out of nowhere.
For making borders, you should be using a <Border> element somewhere.
By default, the Button has some Border inside. That's why setting BorderBrush/BorderThickness on a Button does something. Specifically, the default control template of a Button contains a Border as one of its topmost elements, and that Border's definition looks similar to:
// the DEFAULT Button's control template
<ControlTemplate>
...
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
.... >
...here goes other things like textblock/content..
</Border>
...
</ControlTemplate>
so, as you see, there's a border and it has its settings bound to the owning control, so bound to the Button. The TemplateBinding binds BBrush of Border to BBrush of the Button that owns it.
Now, as you have completely rewritten/overridden the Button's control template, it now does not contain any Border, so there is nothing that would draw the border.
Try:
<Border BorderBrush=... BorderThickness=...>
<ContentControl ....
and it should be like you wanted.
Afterthought: If I understood you wrong and if you meant your setting the BorderBrush/Thickness on the ContentControl inside the buttons' template that it didn't add border around your paths, then there's a surprise.
The properties are available on the CC only because they are commonly used, they came from base Control class. But the ContentControl does not use them at all. They are ignored. ContentControl does not draw any borders, texts, etc. ContentControl shows its content, and the content is just a Path which draws itself only. So, either add a Border around the ContentControl as above, or change your resources and wrap each Path into a Border and use that as a content resource.
<Style.Resources>
<Border x:Key="TriangleUpPath"
BorderBrush=... BorderThickness=...
<Path Data="M 0,5 L 7,0 L 14,5"
.... />
</Border>
<Border x:Key="TriangleDownPath"
BorderBrush=... BorderThickness=...
<Path Data="M 0,0 L 7,5 L 14,0"
.... />
</Border>
</Style.Resources>

PRISM Scrolling in region

I use regions with Prism. In the main window I have defined regions and in the region LeftNavigationRegion I inject a module which consist of basically a treeview. When the main region resizes I want a scrollbar from the treeview, but I instead get the scrollbar from the content control. This means that the header control in the treeview disappears. Does anyone know how to show the scrollbar of the treeview
XAML in main window...
<ContentControl x:Name="ActionContent"
cal:RegionManager.RegionName="{x:Static inf:RegionNames.LeftNavigationRegion}"
VerticalAlignment="Stretch">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<ContentPresenter Content="{TemplateBinding Content}" />
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="false">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
You need to limit the size of the TreeView in some way. By default, it is probably being allowed to stretch to whatever size it needs, so the TreeView doesn't think it ever needs to scroll so don't show it's ScrollBars
You can do this by binding the Height/Width of the TreeView to your ContentControl's Height/Width. (If your outer ScrollViewer is inside the ContentControl, then bind to the it's Height/Width instead)
<TreeView Height="{Binding ElementName=ActionContent, Path=ActualHeight}"
Width="{Binding ElementName=ActionContent, Path=ActualWidth}">
Think i solved it, the problem was related to how i renderede the treeview columns, i was using a static Grid inside the style template of the treeview. When i realized that if i changed my custom treeview to a standard listview the scrolling worked. So i extracted the styles for the standard listview and made some changes to the treeview style and it worked, now the header for the treeview stays on top and the scrollbar for the treeview is shown instead of the scrollbar for the content control
In
<Style TargetType="{x:Type TreeView}".....
is added
<Border BorderThickness="{TemplateBinding Border.BorderThickness}" BorderBrush="{TemplateBinding Border.BorderBrush}" Name="Bd" SnapsToDevicePixels="True">
<ScrollViewer Style="{DynamicResource
{x:Static GridView.GridViewScrollViewerStyleKey}}">
<ItemsPresenter />
</ScrollViewer>
</Border>
for the control template. And in
<Style x:Key="{x:Static GridView.GridViewScrollViewerStyleKey}" TargetType="ScrollViewer">
I added
<ScrollViewer DockPanel.Dock="Top"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
Focusable="false">
<GridViewHeaderRowPresenter Name="hrp" Columns="{StaticResource gvcc}"
ColumnHeaderContainerStyle=
"{StaticResource MyHeaderStyle}" />
</ScrollViewer>
So i can define my columns outside from style as treeview does not have .view property
Hope this helps someone and thanks Rachel for your efforts

WPF clipping even when no clipping is desired - how to turn it off?

I need to float out some content out of the ListBox as specified in a DataTemplate for an ListBox.ItemTemplate. I am using RenderTransform but the content gets clipped on ListBox boundaries. ClipToBounds is False for the entire visual tree.
I have read somewhere that WPF internally performs some clipping even when none is specified with dedicated clipping properties. I have also found out that using Canvas can sometimes cure the clipping problem but it does not help here.
How can I overcome this problem? Here is some XAML that I want to fix. Please note the entire left part of rectangle is missing.
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Fill="Red" Stroke="Green" StrokeThickness="4" Width="100" Height="50">
<Rectangle.RenderTransform>
<TranslateTransform X="-50" />
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ListBox.ItemTemplate>
42
</ListBox>
The ListBoxItem's are getting clipped by the ScrollViewer in the ListBox Template. To work around this I think you'll need to remove the ScrollViewer from the Template and if you need scrolling you can wrap the ListBox in a ScrollViewer
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ListBox Margin="100,10,0,0">
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Fill="Red" Stroke="Green" StrokeThickness="4" Width="100" Height="50">
<Rectangle.RenderTransform>
<TranslateTransform X="-50" />
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ListBox.ItemTemplate> 42
</ListBox>
</ScrollViewer>
Update
The ScrollViewer in the Template will generate a ScrollContentPresenter which in turn has the following GetLayoutClip
protected override Geometry GetLayoutClip(Size layoutSlotSize)
{
return new RectangleGeometry(new Rect(base.RenderSize));
}
This class is Sealed so you can't derive from it to override this method. You would have to implement your own ScrollContentPresenter (e.g MyScrollContentPresenter) and probably your own ScrollViewer that uses MyScrollContentPresenter as well to make this work (and if you return null in this method I think that some items below the bounds of the ListBox could become visible as well)
I stumbled upon a solution to this problem by accident while working around it. If you change the ScrollViewer's HorizontalScrollMode and VerticalScrollMode to "Disabled" within the style template, it will stop clipping in each direction respectively.
Edit: May not work for WPF. I tested with a UWP app. The fields in question are:
ScrollViewer.HorizontalScrollMode="Disabled"
ScrollViewer.VerticalScrollMode="Disabled"

ItemContainerStyle in Custom Control Derived From ListBox

Please bear with me Silverlight Designer Gurus, this is compicated (to me).
I'm creating a custom control which derives form the Silverlight 3.0 ListBox. In an effort not to show tons of code (initially), let me describe the setup.
I have a class library containing a class for my control logic. Then I have a Themes/generic.xaml that holds the styling details. In generic.xaml, I have a style that defines the default layout and look for the ListBox where I'm setting a values for the Template, ItemsPanel and ItemTemplate.
In my test app, I add my control on to MainPage.xaml and run it and it works great. I dynamically bind data to my control and that works fine.
Now I want to set the ItemContainerStyle for my derived control. If I create a style in the MainPage.xaml file and set the ItemContainerStyle property to that control as in:
<dti:myControl x:Name="MyControl1" ItemContainerStyle="{StaticResource MyListBoxItem}"
Height="500"
Width="200"
Margin="10"
Background="AliceBlue"
/>
It works as expected.
However, I'd like to do this in the class library or, more specifically, in generic.xaml. I tried to this Setter to my current Style:
<Setter Property="ItemContainerStyle">
<Setter.Value>
<ControlTemplate>
<Grid Background="Red" Margin="3">
<ContentPresenter x:Name="contentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="Stretch" Margin="3"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
And it fails miserably with:
"System.ArgumentException: 'System.Windows.Controls.ControlTemplate' is not a valid value for property 'ItemContainerStyle'."
Note: This is not my actual style I'd like to use for ItemContainerStyle. I'm actually looking to plug in some VSM here for the various selected/unselected states of the a ListBoxItem (for a dynamically bound control).
So, to the question is how do I apply the ItemContainterStyle to my custom control when it's defined using generic.xaml? I do not want that property set when I actually use the control later on.
Thanks,
Beaudetious
You missed to put Style tag inside your Setter.Value. ItemContainerstyle explects a Style to ListBoxItem(Unless you subclassed ListBoxItem to your own derived version.)
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType=”{x:Type ListBoxItem}“ >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="Red" Margin="3">
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="Stretch" Margin="3"/>
</Grid>
</ControlTemplate>
<Setter.Value>
</Style>
</Setter.Value>

Resources