Issue with WPF styles - wpf

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.

Related

WPF DataGrid top-left button set/view Content?

Trying to set the Content property of a WPF DataGrid top-left button at run time. I get the button object using the VisualTreeHelper of the DataGrid object and then I successfully set its Content property, as verified using Snoop while running the application. However, the button text is not visible. I suspect this is because there are UI elements on top of the button that use non-transparent background brushes. Upon reading the docs I see a grid that uses storyboards and a rectangle that uses gradient brushes.
Other than editing the WPF DataGrid top-left button style template, what are my options for making the button Content (text) visible?
You could entirely replace the template.
I'd prefer to bind the text of the textblock to a property in a viewmodel personally.
This could get you started.
Put this in scope of the datagrid like in your window resources or a resource dictionary merged in app.xaml.
<Style x:Key="{ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="Red">
<TextBlock Text="X" Foreground="White"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Needs more work, but it shows up on a datagrid sample I have.
( This is something which has dozens of styles from experiments answering questions elsewhere in it ).
(Something funny happens, I am now posting under the same user name as before but SO insists that I must recreate my profile every time I log in. Go figure.)
Thank you Andy for the solution you proposed. For the technical reason above, I am unable to mark this question "Solved".
Now I think a quick way around this is to place a transparent label right on top of the DataGrid button, with IsHitTestVisible = false. I noticed a couple interesting things though:
It seems the button can have a Grid or a string for Content but not both;
The button is already created when the DataGrid Loaded event handler runs, e.g., a Click event handler can be added to it; however, setting the Content to a string at this time doesn't change anything. I guess Content changes when the DataGrid is populated.

Any examples of a rich visual behaviour WPF button?

I am looking for an example of a customized WPF button.
Ideally in a liked Blend/VS2013 configuration, i.e. a VS2013 test solution that includes a button project that can be edited in Blend for VS2013.
The button should have a visual appearance that makes it clear what state it is in, i.e.
Normal = default
MouseOver = inner glow
Pressed = smaller size / smaller shadow
ToggledOn = outer glow
Disabled = grayed out
Given such an example I could then just tweak the visual appearance of the states using Blend.
And on the application side I want to just instantiate the button, associate the style, and set properties for BackgroundColor, image/icon, text label, width, height.
I seems that using a ControlTemplate style is the recommended way of doing this, rather than sub-classing, see MSDN.
The three key issues seem to be:
how to setup the VS2013/blend project structure to use both interchangeably on a single set of source files
how to compute relative sizes in the ControlTemplate, i.e. what is the syntax for
Width = Button.Width x 1.1 to set a glow extend relative to the actual button size that is not in the template, but to be defined on the client application UI design.
how to compute relative colors from the base color of the button, i.e. what is the WPF XAML syntax for GradientStop Color = Button.BackgroundColor x 80% + White x 20%
This should be a very common need, but Google was not helpful in finding anything like the above.
Any help with any one of the three key issues would be greatly appreciated.
Your requirements do not require defining a new ControlTemplate and can be achieved with a Style with Triggers, e.g.:
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="0.75" ScaleY="0.75"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Button Content="Click Me!" />
</Grid>
The Style can be accessible anywhere in the application if defined in your App.xaml Resources and given an x:Key
Using ScaleTransform's ScaleX and ScaleY are relative values.
You will need your own IValueConverter then bind the target color to a source color using your converter.

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.

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

WPF Style Trigger

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.

Resources