masking in WPF template (opacitymask reversed) - wpf

I am currently redoing a groupBox's template.
I would like the header to have rounded corner on top and "inverted" rounded corner at the bottom:
I managed the above template quite easily by rounding the top corners of the content part and putting the content on top of a "background" container having the darkest color as background.
BUT...
my specifications require that the content's background (lightgray on the picture) might be transparent (i.e.: it should be possible to see right through the content part without having to loose the header's background color). And this is where I'm stuck...
I could easily split this control into two rows of a grid, one for the header, the other for the content and have pretty rounded corner at the top, but I would not be able to get those "inverted" rounded corner at the header's bottom, would I?
so I would be really glad if somebody could either:
point me to a solution (involving whatever dirty trick)
confirm that what I want to do is impossible
thanks in advance

You could use a Path to obtain what you want, a Path that describes the whole dark gray area of your header. You can look at the examples here and figure out the data for your Path.

I figured it out, using the path option submitted by Andrei.
for those interested, here is the final template (using a cornerRadius of 3):
<Border x:Name="Border"
CornerRadius="3"
Background="{TemplateBinding local:MyGroupBox.ContentBackground}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="3" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3"/>
<ColumnDefinition/>
<ColumnDefinition Width="3"/>
</Grid.ColumnDefinitions>
<Border x:Name="HeaderBorder"
Grid.Row="0"
Grid.ColumnSpan="3"
CornerRadius="3,3,0,0"
Background="{TemplateBinding Background}" />
<ContentPresenter Grid.Row="0"
Grid.ColumnSpan="3"
Margin="6"
ContentSource="Header"
RecognizesAccessKey="True"
HorizontalAlignment="Center"/>
<Path x:Name="LeftCorner"
Grid.Row="1"
Grid.Column="0"
Stretch="Fill"
Data="M0,0 L3,0 C1.3431457,0 0,1.3431457 0,3 L0,0 z"
Fill="{TemplateBinding Background}"/>
<Path x:Name="RightCorner"
Grid.Row="1"
Grid.Column="2"
Stretch="Fill"
Data="M0,0 L3,0 3,3 C3,1.3431457 1.6568543,0 0,0 z"
Fill="{TemplateBinding Background}"/>
</Grid>
</Border>

Perhaps that top area contains two borders?
<Border Background="Gray"
Height="{TemplateBinding CornerRadius,
Converter={StaticResource ExtractHeight}}">
<Border Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius,
Converter={StaticResource UseTop}}" />
</Border>
Your lower area would have something similar in a Border with just the bottom corners in the CornerRadius property.

Related

How to get TabPanel ActualWidth from TabControl

I have a tabcontrol and tab strip placement is on left. So it is a simple navigation menu type tab control.
There is a TabPanel inside TabControl's control template.
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid x:Name="Grid" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<Border x:Name="ContentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local" Padding="{TemplateBinding Padding}" Margin="0,-1,0,0">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ScrollViewer x:Name="scroll" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" Template="{StaticResource ScrollViewerTab}">
<TabPanel x:Name="HeaderPanel" IsItemsHost="true" Margin="0,2,0,0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1" VerticalAlignment="Bottom" />
</ScrollViewer>
</Grid>
</ControlTemplate>
I need TabPanel's (tab headers) ActualWidth outside tabcontrol for alignment purposes.
I tried following but it did not work.
<TextBlock Text="{Binding Path=Actualwidth, ElementName=HeaderPanel}" />
<TabControl />
I also tried to bind the ActualWidth of header panel to Tag property of TabControl. Even that did not work.
<TabControl Tag="{Binding Path=ActualWidth, ElementName=HeaderPanel}" />
TabControls.Items collection gives the list of ViewModel objects. So I cannot get TabItem instances.
I ended up using visual tree helper in the codebehind file
var tabPanel = VisualTreeHelpers.FindChild<TabPanel>(TabControl, "MenuPanel");

Resize container without resizing text controls

I have a Custom Control containing a Canvas placed inside a Viewbox. This allows me to resize the control and everything inside the Canvas scales beautifully.
Much to my annoyance a requirement to stop any scaling of text within the control has reared its head. All other controls (mainly graphics) should scale, but not text. Text should move to the correct place upon resize, but the font should remain the same.
Any ideas how to do this?
After much head scratching I found a relatively simple workaround.
As I'm not going to need to click on any text in the control I've put all graphics inside one canvas with zOrder 1 and all text in another with zorder 0. Then I put them inside a grid so they overlap:
<ControlTemplate TargetType="{x:Type local:ContourPlot}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Viewbox>
<Border BorderBrush="Black" BorderThickness="1">
<Canvas x:Name="cvGraph" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" >
<Rectangle Canvas.Left="40" Canvas.Top="31" Width="48" Height="41" Fill="AliceBlue"/>
</Canvas>
</Border>
</Viewbox>
<Canvas Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" >
<Label x:Name="lblTest" Canvas.Left="0" Canvas.Top="10" Content="Label" FontSize="12" />
</Canvas>
</Grid>
</Border>
</ControlTemplate>

Pinned item inside silverlight scrollviewer

Is there a way to have a control inside silverlight scrollviewer that does not scroll? For example I would like the similar behavior as you get when pinning a column in a datagrid so that I can have a footer to my listbox but still have it fit inside the scroll bars (looks better that way). here is a screen shot of what I have. The control I want inside is at the bottom.
Per request here is my control template for the listbox. I have a template for scrollviewer to but could not find a way to tell it to leave the border at bottom when scrolling everything else. If you look at the template I have the border outside of the scrollviewer to keep it from being scrolled but would like it to fit inside the scrollbar because I think it looks better.
<ControlTemplate TargetType="telerik:RadListBox">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border BorderThickness="0" Background="{StaticResource ListBoxBackground}" Grid.RowSpan="2">
</Border>
<ScrollViewer x:Name="PART_ScrollViewer"
Margin="0"
IsTabStop="False"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
TabNavigation="{TemplateBinding TabNavigation}"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Style="{StaticResource ScrollViewerStyle}">
<ItemsPresenter/>
</ScrollViewer>
<Border Grid.Row="1" Background="Transparent" BorderBrush="Tan" BorderThickness="1" Height="50">
<TextBlock Text="Dont scroll but keep in scrollviewer" TextWrapping="Wrap"/>
</Border>
<ContentPresenter x:Name="dragVisualPlaceholder" Visibility="Collapsed" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</Grid>
</ControlTemplate>

Round a arbitrary content control

I have an Esri ArcGis map control which I want to round around the edges. I am also using Prism4.0/MEF and SL4.
I tried to place it in a border, but that doesn't work (the Esri control is loaded into the MapRegion, in another module):
<Border Grid.Row="2"
Margin="2"
CornerRadius="25">
<ContentControl
prism:RegionManager.RegionName="MapRegion"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch">
</ContentControl>
</Border>
UPDATE: Looks like this is not possible. This isn't really a bug with the Map itself, but it kind of is. The Map uses a Canvas inside the Grid "RootElement". This Canvas holds the images for the map. When using a Canvas, it doesn't respect the bounds it's been given. You can reproduce the bug with the following XAML
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="25">
<Grid>
<Grid>
<Canvas>
<Image Source="/Images/MyPicture.png"/>
</Canvas>
</Grid>
</Grid>
</Border>
The best approach is going to be to set an explicit style for the map. With this style any map that is used will have the rounded corners
<Style TargetType="esri:Map">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="esri:Map">
<Border CornerRadius="25" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<Grid>
<Grid x:Name="RootElement" Height="Auto" Width="Auto"/>
<Rectangle x:Name="ZoomBox" Fill="#55FFFFFF" Stroke="Red" StrokeThickness="2" Visibility="Collapsed"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

How do i make a WPF tab control's tab area smaller than the control?

I have a scenario where i have two buttons in the top right of my application, and i have a tab control that spans the entire application's screen. (Basically the two buttons are on the same horizontal line as the tabs in the tab control). The problem is that when i have multiple tabs open, the buttons and the tabs overlap. I don't want to have to specify the grid row/column numbers so that the buttons are above the tabs.
Is there any way to specify to the Tab control a certain area that it has to open tab controls before it automatically starts a second row of tabs?
In other words, if my tab control has a width of X, how can i tell the area that displays the actual tabs that it should only occupy say, x-15 amount of space.
Thanks!
You'll have to supply the ControlTemplate for the TabControl to do that. Here is an example where 100 pixels is reserved to the right of the header panel:
<Grid>
<TabControl>
<TabControl.Template>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local"
SnapsToDevicePixels="true"
ClipToBounds="true">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1"
Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0"
Height="Auto"/>
<RowDefinition x:Name="RowDefinition1"
Height="*"/>
</Grid.RowDefinitions>
<TabPanel x:Name="HeaderPanel"
Panel.ZIndex ="1"
KeyboardNavigation.TabIndex="1"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
IsItemsHost="true"/>
<Border x:Name="ContentPanel"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="2">
<ContentPresenter x:Name="PART_SelectedContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent"/>
</Border>
</Grid>
</ControlTemplate>
</TabControl.Template>
<TabItem Header="Item1"/>
<TabItem Header="Item2"/>
<TabItem Header="Item3"/>
<TabItem Header="Item4"/>
</TabControl>
</Grid>
Resize the window to see it in action.

Resources