Cannot show image as content of WPF ToggleButton - wpf

I'm working at this Point of time with VS 2015 and WPF.
Some weeks ago i had defined in the App.xaml of my project a templated style for the ToggleButton, so that users can see, if the button is checked or not.
Originally the Content of the togglebutton was a TextBlock with a text.
Now i want to replace that textblock by an Image.
Further i defined in the App.xaml some resources for the Images.
Here is the App.xaml:
<Application x:Class="WPFDesignerPrototyp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFDesignerPrototyp"
StartupUri="MainWindow.xaml">
<Application.Resources>
<!-- Style for togglebuttons-->
<Style x:Key="toggleButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border x:Name="outer"
BorderBrush="Black"
BorderThickness="1"
Margin="5,5,0,0"
Opacity=".9"
Background="Transparent">
<Border x:Name="inner"
Margin="5"
BorderThickness="0"
Background="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}">
<Grid x:Name="container">
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!--<TextBlock x:Name="display"
Grid.Row="1"
TextAlignment="Center"
Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"
Margin="5,2,5,2"/>-->
<Image x:Name="displayimage"
Grid.Row="1"
HorizontalAlignment="Center"
Source="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"
Margin="5,2,5,2"/>
</Grid>
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter TargetName="outer" Property="Background" Value="LightBlue"/>
<Setter TargetName="outer" Property="BorderBrush" Value="Transparent"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Image x:Key="imageHorizontalAlignmentLeft" Source="C:/Utilities/VS 2015 ImageLibrary/2015_VSIcon/AlignLeft/AlignLeft_16x.png"/>
<Image x:Key="imageHorizontalAlignmentCenter" Source="C:/Utilities/VS 2015 ImageLibrary/2015_VSIcon/AlignStretchHorizontal/AlignStretchHorizontal_16x.png"/>
<Image x:Key="imageHorizontalAlignmentRight" Source="C:/Utilities/VS 2015 ImageLibrary/2015_VSIcon/AlignRight/AlignRight_16x.png"/>
</Application.Resources>
</Application>
Then - in my WPF window - i implemented the new Content into one of my togglebuttons.
<ToggleButton x:Name="setAlignLeft" Grid.Row="1" Grid.Column="1" Content="{StaticResource imageHorizontalAlignmentLeft}" Style="{StaticResource toggleButtonStyle}" Click="setAlignLeft_Click"/>
But unfortunately nothing happens.
The Content of the button is blank.
So i tried it with another togglebutton, without the style toggleButtonStyle and there it worked.
I think there must be a mistake in my template but i don't know where.
Can anybody help?
Thanks in advance,
Patrick

You will need to bind with Content.Source of the parent as Content is already Image.
<Image x:Name="displayimage"
Grid.Row="1"
HorizontalAlignment="Center"
Source="{Binding Content.Source, RelativeSource={RelativeSource TemplatedParent}}"
Margin="5,2,5,2"/>

Related

MouseOver style trigger won't change background

New to WPF, coming from a web background.
My style trigger won't change button background. Style XAML:
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="GhostWhite" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#F48230"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
Button XAML (nothing relevant in Window or Grid attributes):
<Window...>
<Grid...>
<StackPanel Orientation="Horizontal" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center">
<Button Name="btnEdit" Cursor="Hand" Content="Edit Settings..." HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Margin="0,0,5,0" Click="btnEdit_Click"/>
<Button Name="btnExit" Cursor="Hand" Content="Exit" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Margin="5,0,0,0" Click="btnExit_Click" />
</StackPanel>
</Grid>
</Window>
The buttons do pick up the background style in the resources section, but not the trigger - mouseover results in default behaviour.
Supplementary question: Is there a way I can debug this? I looked in Live Visual Tree but couldn't figure out how to get the info I need.
WPF Controls have a Template property of type ControlTemplate. This property tells WPF how to draw the control on the screen. A WPF Button uses Windows Chrome in it's ControlTemplate which uses user selected system colors to allow for consistency between different applications. Leveraging the magic of WPF and XAML, you can create your own ControlTemplate to make the button look any way you see fit.
Create a style with a key so you can choose which buttons use the template:
<Window.Resources>
<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="GhostWhite" />
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<!-- a simple square button -->
<Border Name="wrapper"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Margin="{TemplateBinding Margin}"
Background="#01000000">
<!-- notice the wrapper has a background that is NEAR transparent. This is important. It'll ensure the button raises the click event -->
<Border Name=inner
Background="{TemplateBinding Background}">
<ContentPresenter Margin="{TemplateBinding Padding}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FFF48230"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" Value="#FF99501B"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
And then to use the template on a button:
<Window...>
<Grid...>
<StackPanel Orientation="Horizontal" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center">
<Button Style="{StaticResource MyButtonStyle}" Name="btnEdit" Cursor="Hand" Content="Edit Settings..." HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Margin="0,0,5,0" Click="btnEdit_Click"/>
<Button Style="{StaticResource MyButtonStyle}" Name="btnExit" Cursor="Hand" Content="Exit" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Margin="5,0,0,0" Click="btnExit_Click" />
</StackPanel>
</Grid>
</Window>

How to use WPF ContentControl 's content with DataTemplate

I copied some resource about custom button using Content control. And I changed something to be <TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentControl},Path=Content}"> For the dataTempalte
<DataTemplate x:Key="PriceDataTemplate" DataType="m:ClickTradeViewModel">
<Button Command="{Binding ExecuteCommand}" Cursor="Hand">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="DarkGray" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#FF345C8B" />
</Trigger>
<DataTrigger Binding="{Binding IsExecuting}" Value="True">
<Setter Property="Background" Value="DimGray" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<UserControl>
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentControl},Path=Content}"></TextBlock>
</ControlTemplate>
</UserControl.Template>
</UserControl>
</Button>
</DataTemplate>
And for the actual Button, it used
<ContentControl x:Name="AskContentControl" Grid.Column="2"
Margin="5,0,0,0"
Content="{Binding QQ.Bid}"
ContentTemplate="{StaticResource PriceDataTemplate}"/>
I expect the Content will use double Bid's tostring method to render the content, but it shows nothing inside (gray color). In the plot the left side shows the price did exists.
Update: I am not sure what's going on, but with some change <TextBlock Text="{Binding QQ.Ask}"></TextBlock> and set
<ContentControl x:Name="AskContentControl" Grid.Column="2"
Margin="5,0,0,0"
Content="{Binding}"
ContentTemplate="{StaticResource PriceDataTemplate}"/> makes it work.
The problem is then I had to explicitly set the PriceDataTemplate several times for different properties.
It does not work because you are using a Binding with RelativeSource finding a ContentControl but UserControl is also a ContentControl, so what it found is actually the UserControl, not the root ContentControl you thought. In this case you can specify some AncestorLevel as 2 (to find the second ContentControl):
<TextBlock Text="{Binding
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ContentControl, AncestorLevel=2},
Path=Content}"></TextBlock>
However it's not really safe and in this case the implicit DataContext is actually the Content you set for your ContentControl (this DataContext flows down from the DataTemplate through the UserControl's Template). So the Binding can be just simple like this:
<TextBlock Text="{Binding}"></TextBlock>
Note I supposed you keep setting the ContentControl's Content to {Binding QQ.Bid}.
This is a full working solution... I'm late but perhaps it could help others?
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ParametricStudyAnalysis.ScopeSelection.Special"
xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid" x:Class="ParametricStudyAnalysis.ScopeSelection.Special.UserControlAddSpecialSignal"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:UserControlAddSpecialSignalModel></local:UserControlAddSpecialSignalModel>
</UserControl.DataContext>
<UserControl.Resources>
<DataTemplate DataType="{x:Type local:UserControlSpecialSignalTtrModel}">
<local:UserControlSpecialSignalTtr/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<GroupBox Header="Signal type" Grid.Row="0" Padding="5">
<xcdg:DataGridControl Name="DataGrid" SelectionMode="Single" ItemsSource="{Binding SpecialSignalEntries}"
SelectedItem="{Binding SpecialSignalEntrySelected}" Height="200">
<xcdg:DataGridControl.Columns>
<xcdg:Column FieldName="Name" Title="Type of special signal" ReadOnly="True"></xcdg:Column>
</xcdg:DataGridControl.Columns>
</xcdg:DataGridControl>
</GroupBox>
<GroupBox Header="Parameters" Grid.Row="1" Margin="0,3,0,0" Padding="5">
<ContentControl Name="MyContentControl"
DataContext="{Binding SpecialSignalEntrySelected, Mode=OneWay}"
Content="{Binding SignalProviderSpecial}">
</ContentControl>
</GroupBox>
</Grid>
</UserControl>

Binding View Model property in Style in user Control in WPF

I have an exceed data grid and I want to bind context menu property of column header.
Here is my code
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid" x:Class="Sedan.Ux.Sheets.Views.SheetView"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="VisibilityConverter"/>
<Style TargetType="{x:Type xcdg:ColumnManagerCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type xcdg:ColumnManagerCell}">
<Grid VerticalAlignment="Center" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{TemplateBinding Content}" HorizontalAlignment="Stretch"/>
<TextBlock Grid.Column="1" Text=" ▼" HorizontalAlignment="Stretch" >
</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding Menus}" ItemsSource="{Binding Menus}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="ItemsSource" Value="{Binding SubMenus}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="CommandParameter" Value="{Binding Name}"></Setter>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<xcdg:DataGridControl Grid.Row="0" x:Name="Sheet" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="3" AutoCreateColumns="True" ReadOnly="{Binding ReadOnly}" ItemsSource="{Binding Data}" SelectionMode="Single" SelectedItem="{Binding SelectedItem}">
</xcdg:DataGridControl>
</Grid>
</UserControl>
I am not able to bind Menus property when I am using Setter in style.
If I remove the binding and hard code menu items ,I am able to see the context menu.
I have done research on how to get it done and I found various suggestions on stackoverflow and other forums regarding using 'Relative Source Self' in binding but that didnt work for me.
If I bind Menus to context menu of any button in user control (not via style) , then I am able to see menu.
Please help.

Docked control tabs not rendering correclty

I'm working on customizing a user control containing a tab control child element. The user control docks to the side of the main window. I want the tab items to orient based on the docking position, eg. left docked tab items rotate 90 degrees to lay along the side. I have the tabs aligning correctly by binding to the attached property DockPanel.Dock. The problem is that when the tabs are rendered they are disconnected by a line from the tab control. I can manually set the (DockPanel.Dock) property for the outer user control object and the tabs docked along that direction draw correctly but the other tabs are separated by a line.
Here is the TabDockControl XML:
<UserControl x:Class="AnimationMotionCaptureStudio.TabDockControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" x:Name="Root" DockPanel.Dock="Left">
<Grid>
<TabControl Height="Auto" HorizontalAlignment="Stretch" Name="tabControl1" VerticalAlignment="Stretch" Width="Auto" TabStripPlacement="{Binding Path=(DockPanel.Dock),ElementName=Root}">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Padding" Value="2" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<ContentPresenter Content="{TemplateBinding Content}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(DockPanel.Dock), ElementName=Root}" Value="Left">
<Setter Property="Padding" Value="2" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<ContentPresenter Content="{TemplateBinding Content}">
<ContentPresenter.LayoutTransform>
<RotateTransform Angle="90" />
</ContentPresenter.LayoutTransform>
</ContentPresenter>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=(DockPanel.Dock), ElementName=Root}" Value="Right">
<Setter Property="Padding" Value="5" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<ContentPresenter Content="{TemplateBinding Content}">
<ContentPresenter.LayoutTransform>
<RotateTransform Angle="-90" />
</ContentPresenter.LayoutTransform>
</ContentPresenter>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Resources>
<TabItem Header="{Binding Path=(DockPanel.Dock), ElementName=Root}" Name="tabItem1">
<Grid>
<Border BorderBrush="Silver" BorderThickness="1" Height="Auto" HorizontalAlignment="Stretch" Name="border1" VerticalAlignment="Stretch" Width="Auto" />
</Grid>
</TabItem>
</TabControl>
</Grid>
The code behind file has no additional code added beyond the constructor generated by VS2010.
And the MainWindow XML usage of the control:
<DockPanel Grid.Row="1" Height="Auto" HorizontalAlignment="Stretch" Name="MainDockPanel" VerticalAlignment="Stretch" Width="Auto" >
<DockPanel.Children>
<StatusBar Height="23" Name="statusBar1" Width="Auto" DockPanel.Dock="Bottom" Background="#FF00476D" />
<AMCS:TabDockControl DockPanel.Dock="Left">
</AMCS:TabDockControl>
<AMCS:TabDockControl DockPanel.Dock="Right">
</AMCS:TabDockControl>
<AMCS:TabDockControl DockPanel.Dock="Bottom">
</AMCS:TabDockControl>
<Border BorderBrush="Silver" BorderThickness="2" Height="Auto" Name="border1" Width="Auto" DockPanel.Dock="Top" Background="#FF898888">
<Image Height="Auto" HorizontalAlignment="Stretch" Name="D3DImage" Stretch="Fill" VerticalAlignment="Stretch" Width="Auto" MouseLeftButtonDown="D3DImage_MouseLeftButtonDown" MouseLeftButtonUp="D3DImage_MouseLeftButtonUp" MouseMove="D3DImage_MouseMove" Grid.RowSpan="1" Grid.Row="1" MouseWheel="D3DImage_MouseWheel">
<Image.Source>
<i:D3DImage x:Name="D3DImageSource"/>
</Image.Source>
</Image>
</Border>
</DockPanel.Children>
</DockPanel>
Any suggestions for how to set this up to render the tabs correctly would be greatly appreciated.

WPF Empty TabControl Content

I am using a TabControl to host workspaces, with the method outlined in This amazing article by John Smith. I was wondering if there is a way to add content, like an image, to the tab control when it has no tabs. Sort of a default or empty behavior. I would like to have the application logo, or maybe some helpful arrows a-la Chrome's first time use tab.
Edit: This might be a little more complicated. I tried Chad's solution below on a standard tabcontrol at it displayed. However, the tabcontrol I am using for the workspaces is being rendered by a content control using a datatemplate, and I couldn't get his solution working with it. HB's solution worked with some changes.
<DataTemplate x:Key="WorkspacesTemplate">
<Grid>
<Image Name="image1" Stretch="Uniform" Source="/Affinity;component/Images/affinity_logo.png" Margin="20"/>
<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}" Margin="4">
<TabControl.Style>
<Style TargetType="TabControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}"
Value="0">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Style>
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden" >
<StackPanel x:Name="HeaderPanel"
Orientation="Horizontal"
Panel.ZIndex ="1"
KeyboardNavigation.TabIndex="1"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
IsItemsHost="true"/>
</ScrollViewer>
<ContentPresenter x:Name="PART_SelectedContentHost" Grid.Row="1"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent"/>
</Grid>
</ControlTemplate>
</TabControl.Template>
</TabControl>
</Grid>
</DataTemplate>
You could overlay the TabControl on your Image and hide it if there are no items in it, e.g.
<Grid>
<Image />
<TabControl>
<TabControl.Style>
<Style TargetType="TabControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}"
Value="0">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Style>
</TabControl>
</Grid>
Or you could swap the content of a ContentControl, also using Triggers, as in the above method both contols affect the layout. e.g.
<ContentControl>
<ContentControl.Resources>
<Image x:Key="Image"/>
<TabControl x:Key="TabControl" ItemsSource="{Binding Data}" />
</ContentControl.Resources>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{StaticResource TabControl}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Data.Count}"
Value="0">
<Setter Property="Content" Value="{StaticResource Image}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Note that here the DataTrigger should bind directly to the same collection used in the TabControl. This is because if you bind to the TabControl.Items.Count this binding will break the moment the trigger fires as the TabControl will be unloaded.
You can add a background to the tab control. The XAML looks something like this:
<TabControl Height="290" HorizontalAlignment="Left" Margin="12,350,0,0" Name="TabControl" VerticalAlignment="Top" Width="481" TabIndex="200">
<TabControl.Background>
<ImageBrush ImageSource="/EffectsViewer;component/res/someimage.png" />
</TabControl.Background>
</TabControl>
Just be sure the image you use is in your resources. Its not to hard to add using the GUI in visual studio. As for adding controls, that might be a little more complicated. I think to do that you would need to either hide the control behind something while its empty, OR derive from it and implement it yourself.

Resources