wpf mouseover margin in stackpanel - wpf

I have a tab item, header having the following form: image_margin_textblock.
The trigger IsMouseOver is working properly when the mouse cursor is over the image, and also over the textblock. But, when the mouse cursor is over the margin between the Image and Textblock, the IsMouseOver trigger is not fired. This creates an annoying flickering effect.
Do you have any ideas how to achieve mouseover trigger over the margin?
Here is the code:
<TabItem.Header>
<ContentControl>
<ContentControl.Template>
<ControlTemplate>
<StackPanel x:Name="sp0" Orientation="Horizontal">
<StackPanel x:Name="sp1" Orientation="Horizontal" Background="Blue">
<Image VerticalAlignment="Center" HorizontalAlignment="Center" Source="tab1.png"/>
</StackPanel>
<TextBlock Margin="10,0,0,0" Text="Tab1" VerticalAlignment="Center"/>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}" Value="True">
<Setter TargetName="sp1" Property="StackPanel.Background" Value="Green"/>
</DataTrigger>
<Trigger SourceName="sp0" Property="IsMouseOver" Value="True">
<Setter TargetName="sp1" Property="StackPanel.Background" Value="Green"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
Thank you.

Set Background on outer StackPanel to Transparent so that margin also participates in HitTest (i.e. respond to mouse events).
Right now only image and TextBlock area responds to MouseOver event. Setting background to Transparent will work.
<StackPanel x:Name="sp0" Orientation="Horizontal" Background="Transparent">

Set background of your StackPanel to Transparent. This makes it visible to hit test.
<StackPanel x:Name="sp0" Orientation="Horizontal" Background="Transparent">
<StackPanel x:Name="sp1" Orientation="Horizontal" Background="Blue">
<Image VerticalAlignment="Center" HorizontalAlignment="Center" Source="tab1.png"/>
</StackPanel>
<TextBlock Margin="10,0,0,0" Text="Tab1" VerticalAlignment="Center"/>
</StackPanel>

Related

style textblock button change text

I want to change the TextBlock of a button programatically, but I still didn't get it right.
<Style x:Key="RoundedButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="50" Background="#463190">
<TextBlock Text="{Binding Tag}" Foreground="White"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="0,0,0,0"
FontSize="20"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Button controls has a property Content to show texts and no need to use Tag. It makes things are more complex.
If you change your Binding to the below, you will be able to use Button's Content property to change the Text inside the TextBlock.
Text = "{Binding Path=Content,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}"

WPF buttons with both image and text

Here is my control template for the button style.
<StackPanel Orientation="Horizontal">
<Image x:Name="EmailImage" Source="../Images/btn__icon_savedisk.png" Height="17" Width="17" Stretch="None" RenderOptions.BitmapScalingMode="NearestNeighbor" />
<TextBlock x:Name="EmailImageTxt" Margin="20,0,20,0" Foreground="White" Text="{x:Static res:Localize.SAVE}" Background="{x:Null}" />
</StackPanel>
My problem is when I get mouse pointer over the area with the space between image and text button(image and text) is not selecting. I need to have mouse focus on all over the buttons.
Thank you for your help.
Set Background="Transparent" to stackpanel and it works as expected.
An control with no background is usually called as non-hittable in XAML terms. So it is must to set a background to make the object respond to hits.
<Button VerticalAlignment="Center" HorizontalAlignment="Center">
<Button.Template>
<ControlTemplate>
<StackPanel x:Name="stackpanel" Orientation="Horizontal">
<Image x:Name="EmailImage" Source="Black.jpg" Height="17" Width="17" Stretch="None" RenderOptions.BitmapScalingMode="NearestNeighbor" />
<TextBlock x:Name="EmailImageTxt" Margin="20,0,20,0" Foreground="Black" Text="gdfgdg}" Background="{x:Null}" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red" TargetName="stackpanel"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>

Setting background of content control

<UserControl .....>
<DataTemplate DataType="{x:Type vm:AViewModel}">
<vw:AView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:BViewModel}">
<vw:BView />
</DataTemplate>
<ContentControl x:Name="chartScreen" Content="{Binding Screen}" Background="Yellow" />
</UserControl>
As you can see from above code, ContentControl is setting its content through binding to ViewModel's Screen property. Screen property will return an instance of AViewModel or BViewModel, depending on some condition. The problem is, when UserControl is loaded on screen, Screen property is null, so there is no content set yet. At this point, I would like to set some background for the ContentControl, but I cannot find a way how to do this? Background="Yellow" does nothing...
Any ideas how to set the background of ContentControl? This backgound should be applied always, even when content is displaying AView or Biew, or null.
Just wrap your ContentControl in a Border
<Border Background="Yellow">
<ContentControl x:Name="chartScreen"
Content="{Binding Screen}" />
</Border>
If all you have in your UserControl is your ContentControl, just set the Background on the UserControl itself. That would remove the extra Border as well.
try something like this :
<ContentControl x:Name="chartScreen" Content="{Binding Screen}" Background="Yellow">
<ContentControl.Triggers>
<Trigger Property="Content" Value="{x:Null}">
<Trigger.Value>
<Border Background="Yellow"/>
</Trigger.Value>
</Trigger>
</ContentControl.Triggers>
</ContentControl>
try something like this in WPF:
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Content}" Value="{x:Null}">
<Setter Property="Content">
<Setter.Value>
<Rectangle Width="100" Height="100" Fill="Blue" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>

Change DataTemplate style in ListBoxItem if the item is selected

I have a listbox with an expander inside the ItemTemplate. I managed to bind the expander's IsExpanded property to the ListBoxItem's IsSelected property ok. Now I want to apply a style to the ListBoxItem's content also bound to the IsSelected property.
<ListBox.ItemTemplate>
<DataTemplate>
<Border Name="myBorder">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Description}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Date:"/>
<TextBlock Text="{Binding Date}"/>
</StackPanel>
<dx:DXExpander Name="expanderDetails"
IsExpanded="{Binding Mode=TwoWay, Path=IsSelected,
RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Count:"/>
<TextBlock Text="{Binding Count}"/>
</StackPanel>
</dx:DXExpander>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
What I want to do is somehow set the style of the "myBorder" Border to "NotSelectedBorderStyle" for unselected ListBoxItems, and to "SelectedBorderStyle" for the SelectedItem (ListBox with single selection).
FYI, the styles define background, border and that kind of stuff, just to make the clear which item is selected, nothing fancy in these styles.
I tried the accepted answer here but if I completely switch styles I loose the nice expanding animation my DXExpander has.
I guess there must be some solution using triggers, but I can't just hit the right spot.
Finally I got it, I'm posting it here in the hope that this will save someone else time and pain :-P
This code does some additional things: the EventSetter and the corresponding Handler method are there to capture clicks to the elements inside the DataTemplate, in order to select the ListBoxItem which contains the element (if you don't you might type text inside an item, while a different one is selected).
The inner Border ("myBorder") is just a container for the stackpanels, I had to wrap everything inside another border ("backgroundBorder") which gets the style changed when the ListBoxItem gets selected.
<Style x:Key="FocusedContainer" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="LightGray"/>
<EventSetter Event="GotKeyboardFocus" Handler="OnListBoxItemContainerFocused" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="backgroundBorder" Width="Auto" Style="{StaticResource NotSelectedBorderStyle}">
<ContentPresenter Content="{TemplateBinding Content}">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Border Name="myBorder">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Description}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Date:"/>
<TextBlock Text="{Binding Date}"/>
</StackPanel>
<dx:DXExpander Name="expanderDetails"
IsExpanded="{Binding Mode=TwoWay, Path=IsSelected,
RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Count:"/>
<TextBlock Text="{Binding Count}"/>
</StackPanel>
</dx:DXExpander>
</StackPanel>
</Border>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="backgroundBorder" Property="Style" Value="{StaticResource SelectedBorderStyle}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then I set the ItemContainerStyle in my ListBox to the above style:
<ListBox Background="#7FFFFFFF" HorizontalContentAlignment="Stretch"
ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True"
ItemContainerStyle="{StaticResource FocusedContainer}"/>
To finish, the code behind for the GotKeyBoardFocus handler:
private void OnListBoxItemContainerFocused(object sender, RoutedEventArgs e)
{
(sender as ListBoxItem).IsSelected = true;
}
A mess in code, but pretty neat in the UI. Hope this helps someone!

WPF: When is a WrapPanel full

I'm using a WrapPanel to display variable height items in columns. The wrappanel has a constrained size.
Is there an way to determine when the WrapPanel is 'full'? I will then page to another panel with an animation.
I've looked at the ArrangeOverride of the items that are the panels children, but they always seem to be getting all the space they want. I need a way to determine when they begin getting clipped.
Here's an example using a ScrollViewer with a trigger to determine whether it would display using ScrollableHeight. Right now it just changes some text but you could do other things. Removing one of the Rectangles will fire the trigger:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="100" Height="50">
<ScrollViewer x:Name="scrollViewer" VerticalScrollBarVisibility="Hidden">
<WrapPanel>
<Rectangle Width="50" Height="20" Fill="Red"/>
<Rectangle Width="50" Height="20" Fill="Blue"/>
<Rectangle Width="50" Height="20" Fill="Green"/>
<Rectangle Width="50" Height="20" Fill="Yellow"/>
<Rectangle Width="50" Height="20" Fill="Orange"/>
</WrapPanel>
</ScrollViewer>
<TextBlock IsHitTestVisible="False">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="Clipped"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=scrollViewer, Path=ScrollableHeight}" Value="0">
<Setter Property="Text" Value="Not Clipped"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
You could also trigger based on ScrollViewer.ComputedVerticalScrollBarVisibility, but that requires the ScrollBar to actually be visible, whereas when you trigger based on ScrollableHeight, the ScrollBar can be hidden.
Actually using a WrapPanel for what you are trying to achieve doesn't seem like a good idea.
"[...] I will then page to another panel with an animation."
This would be layout to layout animation what isn't to easy, too.
You should write your own panel class: see here or (animated) here

Resources