WPF issuse binding from ControlTemplate to code behind - wpf

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

Related

Nested ContentControls with template

I have a custom WindowStyle, the XAML looks like this:
<Style TargetType="{x:Type Window}"
x:Key="WindowStyle">
/** Some setters **/
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<AdornerDecorator>
<Grid Background="#88000000"
x:Name="WindowBackgroundGrid">
<Border x:Name="WindowContentBorder"
Background="{DynamicResource WindowBackground}"MaxHeight="{Binding Source={x:Static SystemParameters.FullPrimaryScreenHeight}}"
MaxWidth="{Binding Source={x:Static SystemParameters.FullPrimaryScreenWidth}}"
Margin="20">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header -->
<Border BorderBrush="{DynamicResource BorderBrushColor}"
Background="{DynamicResource PaneHeader_Background}"
Grid.Row="0">
<TextBlock Text="Title"Foreground="{DynamicResource DefaultForeground}"
FontSize="16"
FontWeight="Bold"
Margin="5,5,2,5" />
</Border>
<!-- Content -->
<ScrollViewer Grid.Row="1"
Margin="5">
<ContentPresenter Content="{TemplateBinding Content}" />
</ScrollViewer>
</Grid>
</Border>
</Grid>
</AdornerDecorator>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now I want the inner Grid in a seperate Style so that I can use it elsewhere.
<Style x:Key="WindowContentStyle"
TargetType="{x:Type ContentPresenter}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header -->
/** Border control **/
<!-- Content -->
<ScrollViewer Grid.Row="1"
Margin="5">
<ContentPresenter Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}" />
</ScrollViewer>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
And I use a ContenPresenter in my WindowStyle to present it:
<ContentPresenter>
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}"
BasedOn="{StaticResource WindowContentStyle}" />
</ContentPresenter.Style>
</ContentPresenter>
Problem
The edit above didn't give me any errors, but it doesn't present my WindowContentStyle.
When I set the Content property of the Window control and load the style
this.window.Content = view;
this.window.Style = (Style)Application.Current.TryFindResource("WindowStyle");
the content is shown in the ContentPresenter in the WindowStyle and not in WindowContentStyle. Because of this, the Template is not used and I don't have a header with a title.
How can I make my outer ContentPresenter pass on the Content to my inner ContentPresenter (the one in WindowContentStyle)?
Thanks in advance!
Greetings
Loetn
You should use a ContentControl to display your content, not a ContentPresenter. From the ContentPresenter Class page on MSDN:
You typically use the ContentPresenter in the ControlTemplate of a ContentControl to specify where the content is to be added.
From the ContentControl Class page on MSDN:
A ContentControl has a limited default style. If you want to enhance the appearance of the control, you can create a new DataTemplate.

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/

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 DataTemplate property set at Content

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.

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