Change child control (contentcontrol) property on mouseover of parent - wpf

I am new to WPF and am not able to figure out how to change the property of the child ContentControl of the Button control on mouse over. My code looks something like this:
<Button x:Name="btnAddItem" Height="25" Width="25" Margin="5,0,0,0"
Style="{DynamicResource btnStyle}" ToolTip="Add Item">
<ContentControl Content="ContentControl" Height="20" Width="20"
Template="{DynamicResource contentTemplate}" />
</Button>
Now, when in the MouseOver event of the Button, I would like to change the size of the Button as well as the size of the child ContentControl. The ContentControl actually contains a vector image for the Button. Please help.

Your Button will automatically stretch to fit the size of it's contents, so get rid of it's Height and Width properties. If you want to maintain the space between the edge of the Button and the ContentControl, use the ContentControl's Margin property.
Then, use a DataTrigger in your ContentControl's Style to change the Height/Width when the mouse is over it. Be sure you set Height/Width in your style instead of in your <ContentControl> tag, because if you set it in the tag it will take precedence over the triggered value so will never change.
<Style x:Key="MyContentControlStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Height" Value="20" />
<Setter Property="Width" Value="20" />
<Setter Property="Margin" Value="5" />
<Setter Property="Content" Value="ContentControl" />
<Setter Property="Template" Value="{DynamicResource contentTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=btnAddItem, Path=IsMouseOver}" Value="True">
<Setter Property="Height" Value="20" />
<Setter Property="Width" Value="20" />
</DataTrigger >
</Style.Triggers>
</Style>
<Button x:Name="btnAddItem" Height="25" Width="25" Margin="5,0,0,0"
Style="{DynamicResource btnStyle}" ToolTip="Add Item">
<ContentControl Style="{StaticResource MyContentControlStyle}" />
</Button>

In order to achieve what I wanted, I used Rachel's advice as well as Samuel Slade's. I did it something like this:
<Button x:Name="btnEditItem" Style="{DynamicResource btnStyle}" Margin="5,0,0,0" ToolTip="Edit Item" Click="btnEditItem_Click">
<ContentControl x:Uid="ContentControl_5" Content="ContentControl" Template=" {DynamicResource contentTemplate}" Margin="2.5"/>
</Button>
And I set the height and width of the button through btnStyle via Setter property and change the height and width of the button through the triggers.
This got me working perfectly. I appreciate all your help suggestions. I am not sure if I could have reached to this conclusion as I was thinking on a different route of child controls property. Thanks again.

Related

Button foreground doesn't change when content is StackPanel

I have a button style defined where the template has a ContentPresenter with a name of "contentPresenter". It then has a trigger set up like this:
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="#FF838383" />
</Trigger>
</ControlTemplate.Triggers>
This trigger simply changes the foreground color of a button to gray when the button is disabled. However, I have one button in my application which does not have simple text as its content. The button looks like this:
<Button Grid.Column="3" Grid.Row="3" Grid.ColumnSpan="2"
Margin="120 20 30 10"
Command="{Binding SomeCommand}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding MyImage}" Margin="0 0 2 0"
Visibility="{Binding ShowMyImage, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Text="{Binding ButtonText}" />
</StackPanel>
</Button>
The foreground of this button is always the same whether the button is disabled or not. I'm thinking it's because the text for the button is in a textblock within the stackpanel in the content presenter, but I don't know how to get the foreground color changes to be applied to the inner textblock.
I tried changing the Property in the trigger to use "TextBlock.Foreground" instead of "TextElement.Foreground". I also tried binding the Foreground of the inner textblock to the foreground color of the closest ancestor of type FrameworkElement like this:
<TextBlock Text="{Binding ButtonText}" Foreground="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(FrameworkElement.Foreground)}" />
None of this worked. What do I need to do to get the foreground color to apply to the TextBlock inside the StackPanel?
Your trigger should be in the Style, and should be setting Foreground on the Button itself, rather than on the ContentPresenter. Then it'll Just Work, with a custom template or with the default template. No need for any extra bindings.
<Style TargetType="Button" x:Key="MyButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="Beige" BorderBrush="Black" BorderThickness="1" Padding="6,2">
<ContentPresenter
/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="#FF838383" />
</Trigger>
</Style.Triggers>
</Style>
Usage:
<Button
IsEnabled="False"
Style="{StaticResource MyButtonStyle}"
>
<StackPanel>
<TextBlock
>Blah Blah</TextBlock>
<Button>X</Button>
</StackPanel>
</Button>
<Button Style="{StaticResource MyButtonStyle}" IsEnabled="False">Testing</Button>
Screenshot (never mind my ugly template):

WPF Textblock Tooltip does not wrap

I am trying to create a tooltip that wraps automatically (and also has an advanced mode that takes normal content, but that's later). Anyway, I'm setting the content as a string and making the content just a textblock with wrapping. However I can't figure out why this isn't working. Here is the style I'm working on:
<Style x:Key="StHelpLinkBase" TargetType="{x:Type graphicElements:MyHelpLink}">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="{StaticResource BrHelpLinkBackground}" />
<Setter Property="Width" Value="12" />
<Setter Property="Height" Value="12" />
<Setter Property="Margin" Value="5" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type graphicElements:MyHelpLink}">
<Grid x:Name="templateRoot">
<Image Source="Images/Icon_16_Help.png" Stretch="UniformToFill" MaxHeight="16" MaxWidth="16"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
x:Name="PART_Image">
<Image.ToolTip>
<ToolTip Background="{TemplateBinding Background}" BorderThickness="0"
DataContext="{Binding DataContext, ElementName=PART_Image}"
TextElement.Foreground="{TemplateBinding Foreground}"
ContentTemplate="{StaticResource DtTooltipAdvanced}"
MaxWidth="150"
x:Name="PART_Tooltip">
<ContentPresenter />
</ToolTip>
</Image.ToolTip>
</Image>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is the basic template referenced:
<DataTemplate x:Key="DtTooltipBasic">
<Grid>
<TextBlock Text="{Binding Content, RelativeSource={RelativeSource AncestorType=ToolTip}}"
TextWrapping="Wrap"
Foreground="White"
Margin="15"
FontFamily="Resources/#Artifakt Element"
FontSize="9pt" />
</Grid>
</DataTemplate>
And here is the usage (MyHelpLink inherits from ContentControl):
<graphicElements:MyHelpLink Content="This is some help text that is long and is just set as straight string in content but it should wrap I hope." />
I've tried setting the MaxWidth on the tooltip as I have it now, I've tried setting it on the Grid that is in the DataTemplate, and I've tried setting it on the textblock itself and all just cut off the text. I also tried setting the Width property of the textblock directly and same thing...
So why doesn't this wrap?
Ok well I still don't know why this didn't work but I ended up with another solution. Through some experimenting I found that if I put the textblock directly inside the control template instead of a data template it worked and wrapped correctly. However in order to switch it I couldn't use it that way.
So what I did was make two control templates; one with a wrapping textblock for generic content and one with ContentPresenter for non-string content. I then made the style with a trigger on the content type (I made a custom readonly dependency property in my class denoting to trigger the change if the content is anything except a string). The trigger changes the template from the wrapping textblock to the content presenter depending on the type of content set.
If anyone knows why it doesn't work inside a DataTemplate I would love to know and will mark as the answer...

WPF toolbar's style not working

I'm using a resource dictionary in my wpf proyect.
The dictionary has these 2 styles:
<Style x:Key="MyMenu" TargetType="Menu">
<Setter Property="Background" Value="Black"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style x:Key="MyToolbar" TargetType="ToolBar">
<Setter Property="Background" Value="Black"/>
</Style>
And in my XAML file, I use these 2 styles as it follows:
For the menu:
<Menu Name="menuMainBar" Style="{DynamicResource MyMenu}" IsMainMenu="True" VerticalAlignment="Top" Margin="0,10,0,0">
<MenuItem ...
</Menu>
For the toolbar:
<ToolBarTray Name="toolBarTrayRigth_wargames" Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" Width="50">
<ToolBar Name="toolBarRigth_wargames" Style="{StaticResource MyToolbar}" BorderThickness="0,0,1,0">
<Button ...
</ToolBar>
</ToolBarTray>
This is the result:
As you can notice, the menu takes the style correctly, but the toolbar isn't affected by the style at all.
I tried removing all the style elements from the toolbartray tag, as it follows:
<ToolBarTray Name="toolBarTrayRigth_wargames">
...
</ToolBarTray>
And now the toolbar works, but as you can see, now the toolbarTray is not how I want it to be (I want a vertical toolbar, not a horizontal toolbar):
I also tried setting the background of the ToolBar directly in the xaml file:
<ToolBarTray Name="toolBarTrayRigth_wargames" Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" Width="50">
<ToolBar Name="toolBarRigth_wargames" BorderThickness="0,0,1,0" Background="Black">
<Button ...
</ToolBar>
</ToolBarTray>
...and it works:
But I don't want to do that, I want to use a resource dictionary for my toolbar's style.
any ideas on how to achieve that?
I found, bizarrely, that if I set the Orientation of the parent ToolBarTray to Horizontal, that your ToolBar style was able to set the background on the ToolBar.
I also found that with the ToolBarTray orientation still being Vertical, this worked to set the ToolBar's background:
<Style x:Key="MyToolbar" TargetType="ToolBar">
<Style.Triggers>
<!--
This is intentional. A conventional setter was found not to set the background
when the parent ToolBarTray's Orientation was Vertical.
-->
<DataTrigger Binding="{Binding Source={x:Null}}" Value="{x:Null}">
<Setter Property="Background" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
I find this very strange.
as #EdPlunkett found, its a strange Setter in the default template in the Trigger for Vertical Orientation:
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="Margin" TargetName="Grid" Value="1,3,1,1"/>
<Setter Property="Style" TargetName="OverflowButton">
<Setter.Value>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#FFEEF5FD"/>
you can edit the template (in Document Outline panel, right click on the ToolBar element > Edit Template > Edit a Copy...), and remove this Setter.

How do I change a button image but still select the whole button?

I am trying to make a button in WPF that, when hovered by the mouse, lights up and gets the blue-ish selection around it. I managed the former, but by changing the button image, I apparently override the commands that highlights the button with a blue selection.
This is what I have:
<Button Command="DoSomething" Name="button">
<Button.Template>
<ControlTemplate>
<Image Height="32" Width="32" Stretch="Uniform" Name="buttonImage">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="/Project;component/Project/Bitmaps/Icon_colour.png" />
</Style>
</Image.Style>
</Image>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="false">
<Setter Property="Source" Value="/Project;component/Project/Bitmaps/Icon_grey.png" TargetName="buttonImage"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Is there a way to get the selection back again, while keeping the icon-light-up effect?
You have replaced the default Button ControlTemplate, so all you need to do is to 'replace' the missing part(s) from the original ControlTemplate. You can find that in the Button Styles and Templates page on MSDN.
UPDATE >>>
Alternatively, you can simply add an Image into the Button.Content property:
<Button Command="{Binding DoSomething, Mode=OneWay}">
<Image Source="/Project;component/Project/Bitmaps/Icon_colour.png" />
</Button>

TextBlock with vertical scrollbar only

I have a TextBlock which may contain a long text so I want to add a vertical scroll bar to it. My initial attempt was to wrap a ScrollViewer around it. That works but the problem is that when I zoom in, the width is zoomed also. I tried disabling the horizontal scroll bar like this:
<ScrollViewer IsTabStop="True" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
But it didn't solve the problem. I also tried binding the width:
Width="{Binding ElementName=Scroller, Path=ViewportWidth}"
It didn't help either.
So, my question is, how can I add vertical scrollbar to it but have a fixed width and wrapped text for the TextBlock inside? Here's my full code:
<ScrollViewer Grid.Row="1" IsTabStop="True" VerticalScrollBarVisibility="Auto">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Top" TextWrapping="Wrap" TextAlignment="Center"/>
</ScrollViewer>
There are two parts to this answer... the first is to simply use a TextBox:
<TextBox ScrollViewer.VerticalScrollBarVisibility="Visible" Text="Something really
really really really really really really really really long"
Style="{StaticResource TextBlockStyle}" />
The second part is to simply Style the TextBox so that it looks like a TextBlock:
<Style x:Key="TextBlockStyle" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="TextWrapping" Value="Wrap" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
Feel free to remove any of these properties if they do not suit your situation.
<TextBox HorizontalAlignment="Center"
VerticalAlignment="Top"
TextWrapping="Wrap"
TextAlignment="Center"
VerticalScrollBarVisibility="Auto" Width="300" Style="{StaticResource TextBlockStyle}"/>
You don't need a ScrollViewer wrapped in the TextBox, the TextBox control has its own ScrollViewer. And you need to define the width of the TextBox so that the scrollbar will know its fixed width and will wrap the text.
Then, you have to style the TextBox to look like a TextBlock
A good reason why this ScrollViewer won't work according to to Ifeanyi Echeruo from Microsoft, from MSDN
ScrollViewer first asks its content how large it would like to be in
the absence of constraints, if the content requires more space than
the Viewer has then its time to kick in some ScrollBars
In the absence of constraints TextBlock will always opt to return a
size where all text fits on a single line.
A ScrollViewer with ScrollBars will never get a TextBlock to wrap.
However you may be able to come up with a Measure\Arrange combination
for a panel of your own that is almost like ScrollViewer but I cant
think of any logic that can satify both constraints without explicit
knowlege of the behaviour of said children

Resources