Button foreground doesn't change when content is StackPanel - wpf

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):

Related

Editing the property of an element within the ControlTemplate in WPF

I have some radio buttons that I'm building a custom control template for. Image of the buttons:
In the control template, each radio button will have a textblock with its name and another textblock below it to indicate if it's unavailable.
I want the "Unavailable" text to be visible ONLY when the button is NOT enabled. When the radio button is ENABLED, the "Unavailable" textblock should be collapsed.
Here is the simplified view.xaml for the buttons:
<RadioButton Name="one"
IsEnabled="{Binding One_isAvailable}"
Style="{StaticResource RadioButtonTheme}" />
<RadioButton Name="two"
IsEnabled="{Binding Two_isAvailable}"
Style="{StaticResource RadioButtonTheme}" />
<RadioButton Name="three"
IsEnabled="{Binding Three_isAvailable}"
Style="{StaticResource RadioButtonTheme}"/>
Here is the simplified version of the styling I have so far (RadioButtonTheme.xaml):
<ResourceDictionary>
<Style BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="{x:Type RadioButton}"
x:Key="RadioButtonTheme">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="7">
<StackPanel VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center"
Text="{TemplateBinding Property=Name}"
Foreground="{TemplateBinding Property=Foreground}">
</TextBlock>
<TextBlock Name="UnavailableTextBlock"
HorizontalAlignment="Center"
Text="Unavailable"
FontSize="14"
FontStyle="Italic"
Foreground="{TemplateBinding Property=Foreground}">
</TextBlock>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
So I've tried setting a couple things:
I set a visiblitity property on the radio button on the view.xaml. I then binded that visibility to the "UnavailableTextBlock" in the radiobuttontheme.xaml and set the rest of the template visiblity to "Visible." I thought that I can leave the template visible except for one element of it. I now don't think that's possible.
I tried directly binding the "UnavailableTextBlock" to the IsEnabled property of the radiobutton, and ran it through a BoolToVisiblityConverter.
<TextBlock Name="UnavailableTextBlock"
Visibility="{TemplateBinding Property=IsEnabled, Converter={StaticResource BoolToVisConverter}}">
However, I can't seem to get my converter to work inside of the ResourceDictionary. The program will crash with the error: "Cannot find resource named 'BoolToVisConverter'. Resource names are case sensitive"
I have this converter working across my other xaml files since I added it to my <Application.Resources> in the app.xaml. Do I need to link my Resource dictionary to the converter? How do I do that? <ResourceDictionary.Resources> didn't seem to work for me.
I tried adding a datatrigger to the "UnavailableTextBlock" as below:
<TextBlock Name="UnavailableTextBlock"....>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{TemplateBinding Property=IsEnabled}" Value="false">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
However, I get an error saying: '"IsEnabled" member is not valid because it does not have a qualifying type name.'
I'm guessing that it's referencing the IsEnabled property of the TextBlock and not of the radio button? Although I'm not too sure. I'm still learning WPF.
Thanks for all your help in advance!
If I understand correctly what you want to implement, then you need to use the control template trigger.
<Style BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="{x:Type RadioButton}"
x:Key="RadioButtonTheme">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border CornerRadius="7">
<StackPanel VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center"
Text="{TemplateBinding Property=Name}"
Foreground="{TemplateBinding Property=Foreground}">
</TextBlock>
<TextBlock Name="UnavailableTextBlock"
HorizontalAlignment="Center"
Text="Unavailable"
FontSize="14"
FontStyle="Italic"
Foreground="{TemplateBinding Property=Foreground}">
</TextBlock>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="UnavailableTextBlock" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Add name to specific style control wpf

I have a custom contextmenu:
<Window.Resources>
<ContextMenu x:Key="RowMenu" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<ContextMenu.Style>
<Style TargetType="ContextMenu">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="Transparent">
<Border Background="#1c1c1c" Height="70" Width="170" CornerRadius="10">
<StackPanel Orientation="Vertical">
<Button x:Name="openinBrowser" Click="Button_Click_1">
<Grid Width="170">
<materialDesign:PackIcon Kind="OpenInApp" VerticalAlignment="Center" Foreground="{StaticResource PrimaryHueMidBrush}" HorizontalAlignment="Left"/>
<Label FontFamily="Champagne & Limousines" Content="Action 1" FontSize="7" HorizontalAlignment="Center" Foreground="LightGray" VerticalAlignment="Center"/>
</Grid>
<Button.Style>
<Style BasedOn="{StaticResource MaterialDesignRaisedAccentButton}" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{StaticResource PrimaryHueMidBrush}"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ContextMenu.Style>
</ContextMenu>
</Window.Resources>
How would I be able to add a name to the Button so I can enable and disable it in my c# (without using binding), I have tried putting x:Name="" but it doesn't work, but if I add a button click it works? I am quite confused, any help would be appreciated!
I still say you should be doing this properly with data-binding, but if you insist...there are a couple of different ways to go about this.
Context menus aren't part of the regular visual tree, so you have to access them directly. Give your context menu a name, and then find the button by traversing its template's visual tree:
// button has to be templated in order for this to work,
// so don't try it in the parent window's constructor
// (add a this.contextMenu.Loaded handler instead if you have to)
var button = this.contextMenu.Template.FindName("openinBrowser", this.contextMenu) as Button;
If your visual tree is particularly complex then a faster option would be to create a boolean resource in your window's resources block:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
<Window.Resources>
<sys:Boolean x:Key="ButtonEnabled">True</sys:Boolean>
</Window.Resources>
...and then bind your button to that dynamically:
<Button x:Name="openinBrowser" IsEnabled="{DynamicResource ButtonEnabled}">
This breaks your "no binding" rule though, which is why I was asking why you're so adamant about not using data-binding...you can still use it, even if you're not binding to the data context. In this scenario you set the value of that resource in your code instead:
this.Resources["ButtonEnabled"] = false;

Bind header textblock isEnabled to parent Groupbox isEnabled

Referencing to this question: WPF Databinding: How do I access the "parent" data context?
I wanna do something similiar, but for the header of a Groupbox (because the header does not concern with the Box is being disabled and thus is always black while the rest is light gray. This looks a bit strange to me if all the content of the box is gray, the above is gray, but the box title itself stays black.
So I tried to use the approach mentioned in the linked question by flq to simply bind the isEnabled property of the header textblock to the isEnabled property of the groupbox but it seems that my binding at some point fails and I don't know where and why exactly.
heres my current code:
<GroupBox Header="Change Steps" Grid.Row="2" Grid.ColumnSpan="3" Name="gbChangeSteps">
<GroupBox.Style>
<Style TargetType="GroupBox">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" FontWeight="Bold" Height="19" Foreground="Black" IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}, Path=isEnabled}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupBox.Style>
<!-- ... (some non relevant Content)-->
</GroupBox>
after additional research I found the post Disable groupBox including the groupBox name in WPF
that lead me, in combination with Properties->Create Databinding->Binding type->UIElement to the solution that fixed both problems, the one this question was about and the original one that lead to entire restyling, which was that letters like the small g got messed up in the header.
This is the code that fixed the issue:
<GroupBox.Style>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" FontWeight="Bold" Height="19" IsEnabled="{Binding IsEnabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UIElement}}}">
<TextBlock.Style>
<Style>
<Style.Triggers>
<Trigger Property="Control.IsEnabled" Value="False">
<Setter Property="Control.Foreground" Value ="#FF6D6D6D" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupBox.Style>

Focus image does not work for first time on Textbox

i want to show a focus image around a text box when it got focus. so i create following style
<Style x:Key="TextBoxFocusVisualStyle">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Image Source="/WPFApp;component/Resources/txtFocus.png" Stretch="Fill" Margin="-8,-6,-8,-6"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and in window xaml file i used this style as following
<TextBox Grid.Column="1" Height="34" Margin="186,48,0,0" Name="txtEmailId" VerticalAlignment="Top" KeyboardNavigation.TabIndex="0" MaxWidth="293" HorizontalAlignment="Left" Width="293" Text="" FocusVisualStyle="{DynamicResource TextBoxFocusVisualStyle}"/>
but problem is that it does not work during loading. When window load then initially focus is on that textbox and at that time it does not show the image .However when i navigate to other textbox (and other control) then it show focus image. and finally when i focus return to that textbox then it display the focus image
so problem is that it does not show focus image first time on when window loaded. Please suggest that where i am wrong.
Consider that FocusVisualStyle applies to a control only when focused by keyboard (TAB key).
This is different from the logical focus that you obtain for example using
Control.SetFocus()
For an overview on Focus have a look at
http://msdn.microsoft.com/en-us/library/aa969768.aspx
A possible workaround for your problem is work with DependencyProperty IsFocused an use Style instead of FocusVisualStyle
<Style x:Key="TextBoxStyle" TargetType="{x:Type Control}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Image Stretch="Fill" Margin="-8,-6,-8,-6" Source="/WPFApp;component/Resources/txtFocus.png" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
And then in the main Window
<TextBox Grid.Column="1" Height="34" Margin="186,48,0,0" Name="txtEmailId"
VerticalAlignment="Top" KeyboardNavigation.TabIndex="0" MaxWidth="293"
HorizontalAlignment="Left" Width="293" Text=""
Style="{DynamicResource TextBoxFocusVisualStyle}" Background="White" />
Hope this heps

Change child control (contentcontrol) property on mouseover of parent

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.

Resources