WPF DataTemplate property set at Content - wpf

New to WPF and have Tabs and in each tab the content is presented in a curved corner panel/window/whateveryouwannacallit. I wasn't sure how to do this ( Style, ControlTemplate ) but decided to go the DataTemplate way.
So now I have this DataTemplate:
<DataTemplate x:Key="TabContentPresenter" >
<Border Margin="10"
BorderBrush="{StaticResource DarkColorBrush}"
CornerRadius="8"
BorderThickness="2"
Grid.Row="0"
Padding="5"
Background="{TemplateBinding Background}">
<ContentPresenter Content="{Binding}" />
</Border>
</DataTemplate>
As you can see with the the background property I wan't to set the background color at the content but Don't know how. Here I use it.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl ContentTemplate="{StaticResource TabContentPresenter}" Background="White">
<!-- Something Here -->
</ContentControl>
<ContentControl ContentTemplate="{StaticResource TabContentPresenter}" Grid.Row="1" Background="Blue">
<!-- Something Here -->
</ContentControl>
</Grid>
Is using DataTemplate wrong here or is there any other way?
I could probably set the background straight on the content and change from padding in mthe template to margin in the content but in some similiar situations that wouldn't work and it's nicer to only have to set it once.
EDIT:
As per advice I changed to ControlTemplate and also put it inside a style. This solves the Background problem but creates a bigger one. Now the content won't appear. I read on a blog here that putting a targetType solves this but it didn't solve my problem. The code looks like this now and also changed the ContentControl to use the style instead of Template.
<Style x:Key="TabContentPresenter" TargetType="ContentControl" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Border Margin="10"
BorderBrush="{StaticResource DarkColorBrush}"
CornerRadius="8"
BorderThickness="2"
Grid.Row="0"
Background="{TemplateBinding Background}">
<ContentPresenter Content="{Binding}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Use ControlTemplate instead DataTemplate
<ControlTemplate x:Key="TabContentPresenter">
<Border Margin="10"
CornerRadius="8"
BorderThickness="2"
Grid.Row="0"
Padding="5"
Background="{TemplateBinding Background}">
<ContentPresenter Content="{Binding}"/>
</Border>
</ControlTemplate>
Use Template instead of ContentTemplate
<ContentControl Background="Green" Template="{StaticResource TabContentPresenter}"/>

May be because TemplateBinding does not work with DataTemplate. Check this question for details.
Even if it works, all you need is a ControlTemplate and not a datatemplate.

Related

Binding to the UserControl Inside HierarchicalDataTemplate in Silverlight

I have customized TreeView in usercontrol.
In the HierarchicalDataTemplate I have an image with direction (arrow for the example).
When the application\UserControl flow direction change I need to flip the image.
So i tried binding the Image.FlowDirection (Which is flipping the image automatic when RightToLeft) to the UserControl.FlowDirection
<Image FlowDirection="{Binding Path=FlowDirection,
ElementName=MyUserControl}" ... />
But it is not working. I guess because he can't find the UserControl from inside the template.
I've tried this binding outside the template - and it's working fine.
Any solution ?
How can I get my UserControl from inside the template ?
--UPDATE--
There are two places of binding in this xaml. The first is in the style of the button, and it is working, and the second in the template of the TreeViewItem - and there it's not working.
<UserControl x:Class="MyTree"
...
d:DesignHeight="218" d:DesignWidth="284" x:Name="MyLayerListControl">
<UserControl.Resources>
<Style x:Key="CloseButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
...
</VisualStateManager.VisualStateGroups>
<Border x:Name="CloseButtonBorder" BorderThickness="0" Margin="3" CornerRadius="0" Background="Gray" >
<!-- THIS BINDING IS WORKING -->
<Image Source="{StaticResource back}" Margin="2"
FlowDirection="{Binding Path=FlowDirection, ElementName=MyLayerListControl}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="Transparent">
<Button Style="{StaticResource CloseButtonStyle}" />
<Grid>
<Grid.ColumnDefinitions></Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.Resources>
<sdk:HierarchicalDataTemplate x:Key="LayerListTemplate" ItemsSource="{Binding Path=Childrens}" >
<Grid>
<Border CornerRadius="2" BorderBrush="#FF6DBDD1" BorderThickness="1" Background="#FFBADDE9" Opacity="0.5" />
<StackPanel Orientation="Vertical">
<!-- The Item -->
<StackPanel Orientation="Horizontal" Margin="3">
<!-- THIS BINDING IS NOT WORKING -->
<Image x:Name="ArrowImage" Width="16" Height="16" Source="{StaticResource arrow}"
FlowDirection="{Binding Path=FlowDirection, ElementName=MyLayerListControl}"/>
</StackPanel>
</StackPanel>
</Grid>
</sdk:HierarchicalDataTemplate>
</Grid.Resources>
<sdk:TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource LayerListTemplate}" x:Name="MyTreeView" Grid.Row="1" />
</Grid>
</Grid>
</UserControl>
As #Chris says, add "DataContext" in front of you binding path, as you are binding to the Control itself, so if FlowDirection is a property of the DataContext, you need to have that.
Also, make sure you have Mode=TwoWay in your binding.
As an alternative, you can you use RelativeSource so you don't have to hard code an ElementName.
Check out: http://tonychampion.net/blog/index.php/2011/12/7th-day-of-silverlight-ancestor-relative-source-binding/

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>

WPF issuse binding from ControlTemplate to code behind

I have the following custom defined Button defined in my App.xaml file.
<Style x:Key="DispatchListCallButton" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="outerBorder" BorderThickness="1" BorderBrush="DimGray" CornerRadius="1" Background="{TemplateBinding Background}">
<Border Name="innerBorder" BorderThickness="1" BorderBrush="WhiteSmoke" CornerRadius="1" Background="{TemplateBinding Background}">
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="1*"></RowDefinition>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="1*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock>2600</TextBlock>
<TextBlock Margin="4,0,0,0">IPRJ</TextBlock>
</StackPanel>
<TextBlock Grid.Row="1" TextWrapping="Wrap">1234 Main St West Avenue</TextBlock>
<Rectangle Grid.Row="2" Height="1" Margin="2,0,2,0" Stroke="DarkGray" />
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock>1</TextBlock>
<TextBlock Margin="4,0,0,0">*</TextBlock>
</StackPanel>
<ContentPresenter HorizontalAlignment="Stretch" VerticalAlignment="Center" Name="content"/>
</Grid>
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have left off the triggers since I don't have issues with them.
This displays exactely what I want, accept that all of the values are currently hard coded. There are a total of 5 TextBlock's as part of this Button. I would like to be able to set up binding for each of these 5 text blocks so that I can set them dynamically in code behind. Ideally this is what I would like my code behind to look like.
DispatchListCallButton newButton = new DispatchListCallButton();
// Set the 5 TextBlock values
newButton.Id = "4444";
newButton.Code = "ABCD";
newButton.Address = "2000 Main";
newButton.Priority = 5;
newButton.Symbol = "*";
How can I do this and what would the binding expression in the ControlTemplate look like?
You can do this one of two ways.
First Method:
First you would need to create control type that inherited from Button since you cannot modify the button (or instantiate) by the style as you are attempting to now.
You would then define Id,Code,etc. as DependencyProperties of the control. If they are DependencyProperties then the code will automatically register to listen for changes.
Article:
http://msdn.microsoft.com/en-us/library/ms753358.aspx
Second Method:
You would define a ViewModel for the button that exposes those properties and implements INotifyPropertyChange. Each time the propertys are set, fire the event. You would then set newButton.DataContext to an instance of the ViewModel and modify the view model. Bindings would be like Text = {Binding Address}
Article:
http://msdn.microsoft.com/en-us/library/ms229614.aspx

WPF tabitem positioning

What is the proper way of positioning for example three tabitems at the very top left corner and one at the very top right corner of a tab control using WPF?
I have tried to move the fourth tabitem to the right by changing its margin but this doesn't produce a good result; first of all it is cut short and second of all it does not display correctly when selected.
The problem is that the TabPanel, which is used internally by the TabControl to lay out the tabs, does not seem to support what you want. A quick workaround would be to replace the TabPanel by something else, for example, a DockPanel:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<TabControl>
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border BorderThickness="0,0,1,1" BorderBrush="#D0CEBF" Grid.Row="1">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter ContentSource="SelectedContent"/>
</Border>
</Border>
</Border>
<DockPanel IsItemsHost="True" LastChildFill="False" Margin="2,2,2,0" />
</Grid>
</ControlTemplate>
</TabControl.Template>
<TabItem Header="Item 1" />
<TabItem Header="Item 2" />
<TabItem Header="Item 3" />
<TabItem Header="Item 4" DockPanel.Dock="Right" />
</TabControl>
</Window>
(Reference: This is a modified version of an MSDN example for styling a TabControl.)
The simple DockPanel doesn't work as smooth as the TabPanel -- the tabs "jump" a bit when switching between them, but this might get you started. Maybe subclassing the TabPanel and overriding the relevant parts would give you a more accurate result; I guess it depends on how much effort you want to put into this.
I found that by inserting an "invisible" tab I could adjust the spacing, (i.e. move the tabs down from the top)
For example:
TabItem Height="100" Visibility="Hidden" <br>
TabItem..... <br>
TabItem.... <br>
You would need to swap out the TabPanel within the TabControl to something custom which provided the desired behavior. None of the default panels are going to provide your desired behavior out of the box.
This will most likely need to involve overriding MeasureOverride and ArrangeOverride to provide the custom placement within the panel that is desired based on the number of items it contains.
This will involve a custom ControlTemplate for the TabControl. I tried an example using a DockPanel as the items host rather than the default TabPanel.
<Style TargetType="{x:Type TabControl}">
<Setter Property="OverridesDefaultStyle"
Value="True" />
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel Name="HeaderPanel"
LastChildFill="False"
Grid.Row="0"
Panel.ZIndex="1"
Margin="0,0,4,-1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent" />
<Border Name="Border"
Grid.Row="1"
Background="WhiteSmoke"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="2"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2">
<ContentPresenter Name="PART_SelectedContentHost"
Margin="4"
ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is that I don't know of a way of exposing the DockPanel.Dock property to the TabItems outside of the ControlTemplate E.G.
<TabControl Margin="10">
<TabItem Header="Tab One" DockPanel.Dock="Left"/>
<TabItem Header="Tab Two" DocKPanel.Dock="Left"/>
<TabItem Header="Tab Three" DocKPanel.Dock="Left"/>
<TabItem Header="Tab Four" DocKPanel.Dock="Right"/>
</TabControl>
// Note: This does not work!!
I guess you will need to write your own Panel to host the TabItems; Note that this will be quite a lot of work as you will need to handle things like overflow behaviour which is built into the TabPanel.
Even if you did try this I think you would have to write a custom TabControl if you wanted to expose this functionality outside of the ControlTemplate.
If you want to go down this road then see my answer in this post

WPF DropShadow Disappears

Wpf dropshadow disappears.
Here is how to reproduce.
Type the following in xaml pad.
<Page.Resources>
<DropShadowEffect x:Key="shadow"
Opacity="1"
ShadowDepth="1"
Color="Blue"
BlurRadius="30"/>
</Page.Resources>
<Grid>
<Button HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,0,0,0">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Bd"
BorderBrush="Black" BorderThickness="1"
Background="Yellow"
CornerRadius="8"
Effect="{StaticResource shadow}">
<TextBlock Text="Hello out there" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Grid>
You should see some text with a border abound it, and a drop shadow around the border.
Now change the Margin="0,0,0,0" to Margin="0,300,0,0", and size your xaml pad window so you can see the border. On my machine, the drop shadow is now gone.
Anyone else see this? Please help.
I wish I had a good explanation for you, but there were some weird things in your XAML that I played with and I think I have a solution for you.
If you use a Grid, most likely you want to lay out a specific number of rows and columns. You should specify those. This doesn't affect your problem, however.
Likewise, you should specify the Row and Column for your element because you'll eventually need to put this information in your XAML anyway. It's a good habit to start with IMO.
The problem that I can't explain is with the combination of HorizontalAlignment and VerticalAlignment. When you put the Button in the Grid, I would expect the Button to take up the entire space, but it doesn't. The only way you can make this work as far as I could figure out was to specify Height and Width. If you do this, the Effect will work. I found that the threshold in your original XML was a total Y margin of 239. For example, if you used 0,239,0,0, it would fail. If you used 0,139,0,100, it would also fail because the sum is 239. Weird stuff.
Here's my XAML that works:
<Page.Resources>
<DropShadowEffect x:Key="shadow"
Opacity="1"
ShadowDepth="2"
Color="Blue"
BlurRadius="30"/>
</Page.Resources>
<Grid Width="Auto" Height="Auto">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Width="90" Height="30" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,300,0,0" Grid.Row="0" Grid.Column="0">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Bd"
BorderBrush="Black" BorderThickness="1"
Background="Yellow"
CornerRadius="8"
Effect="{StaticResource shadow}">
<TextBlock Text="Hello out there" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
EDIT The OP does not want to specify a size for the Button because the Content of the Button can change dynamically. It turns out that if you set the MinHeight to something like 18 (I think this is reasonable for most content), the dropshadow effect will work again.
<Border x:Name="Bd" BorderBrush="Black" BorderThickness="1" Background="Yellow" CornerRadius="8" Effect="{StaticResource shadow}" MinHeight="18">
<StackPanel Orientation="Vertical">
<TextBlock>hi</TextBlock>
<TextBlock>there</TextBlock>
</StackPanel>
</Border>

Resources