WPF Style Trigger - wpf

I change the FontSize of Text in a Style trigger, this causes the Control containing the text to resize as well. How can I change the Fontsize without affecting the parent's size?

A nice trick to isolate an element from its parent layout wise is to place the element in a Canvas
In the markup below there are two copies of your element
The first is hidden and establishes the size of your control
The second is visible but wrapped in a Canvas so its layout size does not affect the parent.
<Parent>
<Grid>
<Element Visibility="Hidden"/>
<Canvas>
<Element />
</Canvas>
<Grid>
</Parent>

You can increase the Padding at the same time you decrease the FontSize - this will cause the calculated height of the Button to remain the same:
<StackPanel>
<Button Content="ABC">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="20"/>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="FontSize" Value="12"/>
<Setter Property="Padding" Value="5"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Margin="0,20" Content="123" FontSize="20"/>
<Button Content="Do Re Mi" FontSize="20"/>
</StackPanel>
You can do the reverse and set a negative Padding if the FontSize is increasing, as well.
You could also use a binding from FontSize to Padding to accomplish the same thing in a general way, but if you're only dealing with a fixed set of FontSizes it would be easier to just hardcode it as above.

There is absolutely NO need for hard-coded widths, crazy measure overrides, tricky bindings, or anything of that sort.
The solution is actually incredibly simple. Instead of changing the font size in a style trigger, create a simple control template for your button with a RenderTransform applied to the content presenter element. Add a ScaleTransform to the RenderTransform. Inside a IsPressed trigger definition set the vertical and horizontal scales on the ScaleTransform to a smaller ratio, say 0.8.
Using a RenderTransform will keep the layout of the pressed button the same with, so it won't influence the position of the other elements. By contrast, using a LayoutTransform would have actually caused the button container to shrink and the parent container's ArrangeOverride method would cause the adjacent buttons to move to fill the extra space.
I'm really busy right now so I'll leave the actual implementation up to you! ;-)
http://msdn.microsoft.com/en-us/library/system.windows.media.scaletransform.aspx

I am creating a ControlTemplate for a ButtonControl so it looks like a label (flat text, no borders) with triggers for IsKeyboardFocused, IsPressed, IsDefaulted etc.
The IsPressed is defined to drop the FontSize (from default of 30) down to 28. To give a pressed animation effect.
One use of these Buttons is a horizontal StackPanel of Button, separated by vertical separators. When the IsPressed trigger is fired on a button and it is resized, the entire row of buttons gets re adjusted, which is not a pleasing visual effect.
My preference is for a template based solution, to avoid introducing new controls in order to provide overrides. The only problem with the hard coded size approach is internationalisation, other languages will increase the orginal size.
The solution I am going with is to set the minWidth in C# after the button's DesiredSize has been calculated. Note that Width is NaN even after the Button is rendered hence the use/existence of DesiredSize. Later I will try and XAMLize the C#.

What kind of control are you using? If this is a HeaderedControl like a GroupBox or TabItem then you need to specifically set the HeaderTemplate like this:
<DataTemplate x:Key="MyHeaderTemplate">
<TextBlock Text="{Binding}" Fontsize="14" FontWeight="Bold" />
</DataTemplate>

I can think of a couple of things you could try:
You can override the Measure Pass of the control - when a control is rendered in WPF it undergoes two passes. The first is a 'measure pass', where the control comes up with what sizes that it wants to be. The second is the 'arrange pass', where it actually lays out the control. WPF provides a method called MeasureOverride. If you override this method you can provide custom behavior that can be used to adjust the size of the control.
Note - I believe that you will have to call the Measure method all of your controls children during this override in order to get your control to lay out properly.
Hard code the height and width on the control - this will override the control's DesiredSize with your values. While generally a not the greatest of ideas, it will work.

Related

Issue with WPF styles

I wonder if someone can help, I am designing a custom WPF window for an application i am working on and I have an issue with the Min, Max and Close buttons. I have designed a ControlTemplate for the 3 buttons and they are in a StackPanel with Vertical orientation. In my base style I have the following
<Style x:Key="BaseWindowButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value={Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}" />
<Setter Property="Background" Value="Transparent" />
...
</Style>
Have also tried setting the Foreground to a specific color such as #FF000000 and nothing displays
I have a style that inherits this style for the buttons but does not change the foreground or background.
My problem is that the button content does not display, the button displays and the IsMouseOver trigger fires which changes the background but the textual content never displays.
The Min button style is
<Button x:Name="PART_Min">
<Path Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
Data="F1M0,6L0,9 9,9 9,6 0,6z"
SnapsToDevicePixels=True" />
</Button>
I am at a loss as to why the content does not display so would appreciate your thoughts.
The Visual Tree is below, I have examined this and identified the Foreground values
Window (Foreground: #FF000000)
Grid
AdornerDecorator
Grid
ContentControl (Foreground: #FF000000)
StackPanel
Button (Foreground: #FF000000)
Grid
ContentControl (Foreground: #FF000000)
But like I said above I have removed the binding and specified a physical value and still do not get the content displaying
Use ContentPresenter instead of ContentControl in your button's template. (You should include the button's control template in a question like this... it's potentially highly relevant.)
As I too was a beginner with WPF, and trying to understand things was a bit of a learning curve, I would like to offer a few previous links posted out here.
First a simple style for creating simple label and having all labels as a default take on this format without explicit style assignments.
Another sample showing creation of a custom button. This is where I went step by step to create a custom class and apply a style to it to show/hide image on a button.
Maybe the button link and style declaration will help you find your button coloring issues too.

wpf combobox trigger for Validation.HasError

I've looked around, but can't specifically find my issue. I know that the default "Error" handling within WPF puts an "Adorner" around controls in case there are any errors based on IDataErrorInfo or Validataion rules failing a given control. That's all good and fine, however, with a tabbed page interface, if any controls are so flagged as invalid, they are properly adorned in red border. However, as soon as you go from tab page 1 to 2 and back to 1, all the adorners are gone (bad). This was already asked, and solution accepted, but was looking for a better alternative.
So, I went to my "Themes" declaration, and for the textbox control, I just said to set the entire background color of the control to red and not just the border. Without any fancy forced triggering via Notify on Property Changed, if I swap between pages, the red background of the entire textbox remains constant.
Now, on to the combobox control. For those who have customized their own, or even looked into the default MS version of the control, its actually a clustered mess of controls, grids, columns, buttons, etc to make the magic of combobox work. In brief...
ControlTemplate
Grid (two columns, one for text display of chosen, second column for the drop-down arrow)
Border spanning both columns
Path ( line drawing / glyph for the drop-down image for combobox )
ControlTemplate TargetType Textbox (as part of the entire combobox set)
Border specifically "PART_ContentHost"
ControlTemplate of combobox
Grid
Toggle Button
Dropdown exposed showing list
other triggers..
Finally, the main ComboBox declaration which is templated by above components.
Anyhow, I can't for the life of me get this. In the "Toggle Button" area of the combobox declaration, I have a trigger to change the background to an OBVIOUS off color for proof of testing the trigger working and in the right location within the ControlTemplate declarations.
So, knowing this is the correct place within the combobox declarations, I want to supersede the green background color with red if there's an error with the data. I KNOW the overall "Validation.HasError" is properly getting triggered as the native error handler shows. No matter how / where within the template I try to change the background color to red, it does NOT work. I've even tried doing DataTriggers, using converters, trying multiple properties, but it appears not to be cooperating.
Any suggestions? This is getting really annoying.
FINALLY, got it... and not as obvious as I would have guessed. Anyhow, here's what I've found. If you went with a sample from Microsoft's template of the combobox, they first provide the overall two-column "ToggleButton" declaration
<ControlTemplate TargetType="ToggleButton"
x:Key="baseComboBoxToggleButton" >
... blah blah...
</ControlTemplate>
Then, the declaration for the "Display Value" of the combobox
<ControlTemplate TargetType="TextBox" x:Key="ComboBoxTextBox" >
<Border x:Name="PART_ContentHost" Focusable="False"
Background="{TemplateBinding Background}" />
</ControlTemplate>
Then, tie them together as one Combobox "wrapper" declaration
<ControlTemplate TargetType="ComboBox" x:Key="ComboBoxGridControlTemplate" >
<Grid x:Name="GridComboWrapper">
<!-- This is the dropdown button that POINTS TO THE "baseComboBoxToggleButton at the top -->
<ToggleButton Name="ToggleButton"
Template="{StaticResource baseComboBoxToggleButton}"
Grid.Column="2" Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press" >
</ToggleButton>
...
rest of the content presenter,
EDIT(able) textbox area,
popup area of combobox when in drop-down mode
</Grid>
<ControlTemplate.Triggers>
<!-- PUT THE VALIDATION CHECK HERE -->
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
<!-- THIS IS THE CRITICAL COMPONENT... I HAD TO EXPLICITLY TELL
The TagetName as the "ToggleButton" and change ITs Background property
and it now works -->
<Setter TargetName="ToggleButton" Property="Background"
Value="{StaticResource BrushDataInvalidBorder}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
So, now it works as intended and doesn't loose any adorner just because the active page on a given form changes and clears it... its static to each individual control as expected... Wow... what a PITA this one was.
Hope it helps someone ELSE in the future from excessive head banging against a wall while learning this nested level of stuff.

TextBlock internal margin

I suppose when several TextBlocks are located in a row(i.e in StackPanel with the Horizontal orientation) they should look as if it was one TextBlock. But I found the following:
The first line is a TextBlock displaying "eeee". The second one is a StackPanel containing 4 TextBlocks and each TextBlock contains "e". As you can see from the picture above, the second line is a little bit longer than the first one. It looks like the TextBlocks have some internal margin. But, in my case, I'd like to prevent it.
So, do you know how to do it?
Try setting the Padding and Margin of the TextBlock to zero.
<TextBlock Padding="0" Margin="0" ... />
Since you have multiple textblocks that you are going to want to do this with, you should probable use a Style to do this.
<StackPanel Orientation="Horizontal">
<Style TargetType="TextBlock">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
</Style>
...
</StackPanel>
If this doesn't help, then you could start using negative margins:
Margin="-1,0,0,0"
However this introduces brittleness into your code, because the spacing between text depends on the font size and the text displayed. For example, if you change the font size to 13, you will no longer have this problem with five lower case 'e'. Changing the text to uppercase 'E' will result in the same problem, though.
You could try using the clipping properties on the TextBlock or even the rendering affects, but this seems like a lot of work just to get text to adjust a couple of pixels. May I ask why you are trying to get the two text samples to look the same?
Update:
I am doubtful that you can find a good way to make this work the way that you want to. May you should look into using a RichTextBox or creating a custom control.
Here is a good example use of a RichTextBox with text highlight (although it is foreground highlighting).

WPF: How to implement custom Grid with CellSpacing?

I miss the HTML-cellspacing in WPF. Im trying to implement something similar in WPF's Grid by making a custom Grid-class overriding from Grid and then modifying MeasureOverride and ArrangeOverride to get the behaviour I want. Which is that each cell in a grid should have a fixed spacing (not padding) to each other cell. How could this be done?
I found this blog post about creating a custom grid panel that handles spacing between cells: http://daniel-albuschat.blogspot.dk/2011/07/gridlayout-for-wpf-escape-margin-hell.html
You could probably write your own panel, or perhaps even a Grid descendant, that does your own layout with cell spacing. It would be a fair bit of work.
Here's what I usually do instead, to achieve the same thing. Suppose I want a cell spacing of 3 pixels. You can accomplish that by applying a 1.5 pixel margin to each cell (so the total space between a cell and its neighbor is 1.5 + 1.5 = 3 pixels), and then another 1.5 pixel margin around the entire Grid so the outer margin is correct (1.5 pixel margin around the cell + 1.5 margin around the Grid = 3 pixels). The XAML looks like this:
<Grid Margin="1.5">
...
<Label Grid.Row="0" Grid.Column="0" Margin="1.5">...</Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="1.5">...</TextBox>
...
</Grid>
It's ugly but it works.
If most of the controls in your Grid are of the same type (e.g. if it's all Labels, or all Labels and TextBoxes), then you can use styles, instead of declaring and re-declaring the Margin on every element in the grid:
<Grid Margin="1.5">
<Grid.Resources>
<Style TargetType="Label">
<Setter Property="Margin" Value="1.5"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Margin" Value="1.5"/>
</Style>
</Grid.Resources>
...
<Label Grid.Row="0" Grid.Column="0">...</Label>
<TextBox Grid.Row="0" Grid.Column="1">...</TextBox>
...
</Grid>
Actually, I've found in practice that I often want uneven margins -- for example, I may want a 3-pixel margin around the top, left, and right, but no margin on the bottom (because the controls below it already have a margin of their own). So I usually don't end up using 1.5 pixels all around; I usually end up with something more complex. So I can see why they didn't add a CellSpacing; it would make the simple cases simpler, but would be useless in more complicated layouts.
But half-margins-all-around is a quick way to achieve CellSpacing, and then you can tweak the margins if you need something fancier.
You may interested with my answer here, supports uniform border and cellspacing for each child in a custom grid:
How could I put a border on my grid control in WPF?
You're looking for the Margin property. You can style the DataGridCell class.
<Style TargetType="{x:Type toolkit:DataGridCell}">
<Setter Property="Margin" Value="3" />
</Style>

Rolling effect in ListBox

This is a follow-up to this question
Increase FontSize for hovered element in WPF ListBox
I have a ListBox where I want a "rolling" effect. From start all my items in the ListBox have a FontSize of 12, and when IsMouseOver="True" they get FontSize=18. Now I want the item below and above the IsMouseOvered item to get FontSize 16. I've got a feeling this is not so straight forward. Anyone got any idea?
This is my ListBox now
<ListBox Name="ListBox" ItemsSource="{Binding MyList}" DisplayMemberPath="Property1">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="TextBlock.FontSize" Value="18"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Thanks
This is going to be difficult to accomplish in straight XAML, because there's no property that you can examine that tells you if the mouse is currently over the next (or previous) element in the list.
The way I'd implement this: I'd create a wrapper class for the items in your list that exposes the properties IsMouseOver, FontSize, and Content. I'd implement a parent class that maintains some kind of indexable collection of these items (like an array or a list), and that registers for its items' PropertyChanged events so that it can know which item in the list the mouse is presently hovering over. The parent class would then be responsible for adjusting the FontSize on the child objects whenever the currently moused-over item changes. All the XAML does is bind to the FontSize on the child objects.
Note, by the way, that if you implement some kind of cool effect (like the one in the example Aaron linked to, which incidentally doesn't do what you've said you want) that, in resizing elements when IsMouseOver changes, moves them on the screen in such a way that the element that the mouse is over changes too, your users will hunt you down and kill you in your sleep.
It appears you are going for the fish eye effect. There are varying solutions out there for WPF which provide this behavior for you in the form of a reusable panel for instance. They generally focus on images however the concept is the same. This solution shows an example in pure XAML.
I wrote this user control that would mimic the dashboard of the Mac's (Fish eye effect).

Resources