Rolling effect in ListBox - wpf

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

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.

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.

WPF: Applying multiple data templates?

Let's say I am displaying a data component, such as TreeView.
Let's say it is bound to a tree structure, of base type TreeViewItem.
TreeViewItem
TreeViewItem
TreeViewItem
TreeViewItem
and so on.
But some of those items are more specific implementations of TreeViewItem such as AnimalTreeViewItem and even more granular ZebraTreeViewItem
TreeViewItem
AnimalTreeViewItem
ZebraTreeViewItem
PlantTreeViewItem
Now, let's say i want these items to be rendered in a similar fashion, but there would be slight differences in rendering depending on underlying type.
One way i've gotten this to work, is using DataTemplate.
Problem is that i have to create a separate template for each type, with 100% of contents defined in the same way (minus small difference in layout / color etc)
Is there a way to define data templates, that share most of their contents together? meaning, w/out having to create 2 templates, that are almost identical in their markup, just to change the background color of some textbox etc..
You might be interested in the solution shown in this article. It works fine if the differences between are minor, e.g. a different color for some element, but it can also handle more complex scenarios through the use of triggers.
The answer to this question is DataTriggers
<DataTemplate x:Key="myTaskTemplate">
...
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=TaskType}">
<DataTrigger.Value>
<local:TaskType>Home</local:TaskType>
</DataTrigger.Value>
<Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
</DataTrigger>
</DataTemplate.Triggers>
...
</DataTemplate>
This data template will only be 'triggered' when the TaskType is Home.
http://msdn.microsoft.com/en-us/library/ms742521.aspx#adding_more_to_datatemplate

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