Click on WPF Element with lower ZOrder and without using IsHitTestVisible - c#

To simplify the problem, I have the following XAML Windows :
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{StaticResource MainViewModel}">
<Grid>
<Button Click="Button_Click">Click Me</Button>
<ListBox ItemsSource="{Binding Items}" Background="{x:Null}">
</ListBox>
</Grid>
</Window>
When it runs it look like this :
What I would expect is:
I'm able to select Items ( WpfApplication1.BusinessObject )
AND also able click on the button ( as I marked Background as {x:Null} )
But I can't click on the button, only select items in the listbox.
Note: If I use IsHitTestVisible, I can click on the button, but no more on the items, which makes sense. I also absolutely need to have both listbox and button taking the whole windows. Having on top the listbox and on bottom the button is not a solution.
Is there a good way to have this working ?
Thanks for your help !

you can try putting your listbox and button inside a StackPanel
also, it does not seem like your button is bound to any actions, could that be the reason : )?

Another option if you want the listbox sitting on top of and partially covering your button is to set `VerticalAlignment="Top" on your listbox. This will allow the listbox to grow vertically and only the height of the listbox will cover the button. However, if the listbox has a lot of items then it will completely cover the button and you will not be able to click the button.
You could always add PreviewMouseDown handler on the listbox and trigger the button click or command. That way if the listbox fills the entire window, you'll still be able to trigger the button handler.

Have a look at this thread: Canvas in ScrollViewer (Preview)MouseButtonDown event order
The problem is the ScrollViewer that handles mouse events. You could implement your custom ListBox template and use a custom ScrollViewer derived class with the code changes shown in the mentioned thread.

Thanks dotsven, setting handle=false didn't work for me, but I found this question that helped me: Click through ListBox to underlying Control. Taking inspiration from this I removed the ScrollViewer by setting a new style to my listbox and it worked :
<Style x:Key="{x:Type ListBox}" TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I also had to keep the background to {x:Null} :
<ListBox ItemsSource="{Binding Items}" Background="{x:Null}" Height="65" Width="242"/>
Thanks all for your help !

Related

How can I disable WPF button highlight when mouse over or when clicked in runtime?

I've this code in C# to create a button as a child of a StackPanel:
`
Button myButton = new Button();
//All button stuff (Background, text...).
myStackPanel.Children.add(myButton);
`
But, as every button, it highlights every time the mouse is over or when I click it. Is there any way to change that in an easy code (I'm still new to C#) can remove that highlight.
I don't know how to do this. I haven't seen anything explaining this and the only codes I could find were in XAML, and I didn't understand them so couldn't translate them to C#.
The problem is all the code I find is about retemplating the XAML code. What I need is to do what I mentioned in C#, as the control is created from scratch in C#.
I took a look at a few of the answers for this and didn't see any I liked much.
WPF controls are lookless, meaning they have fixed behaviour but not specific look to them. You can re template a wpf control to pretty much anything you can describe in xaml. Many wpf controls have quite complicated templates.
Here's one way to template a button as described.
I've put this style in my window's resources. Usually such styles are in resource dictionaries which are merged in app.xaml.
<Window.Resources>
<Style x:Key="NoMouseOverButtonStyle" TargetType="{x:Type Button}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="BorderBrush" Value="LightGray"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" >
<ContentPresenter Margin="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Style="{StaticResource NoMouseOverButtonStyle}"
Content="This is my Button"
Click="Button_Click"
HorizontalAlignment="Left"
VerticalAlignment="Top"
/>
</Grid>
</Window>
The button references the style as a resource.
That style sets some defaults so the button has a border you can see but over ride.
The contentpresenter is critical because this is where whatever you make content of your button will appear.
If I set an actual value on a button then that will over ride the style.
Hence
<Button Style="{StaticResource NoMouseOverButtonStyle}"
Content="This is my Button"
Click="Button_Click"
HorizontalAlignment="Left"
VerticalAlignment="Top"
BorderBrush="Red"
/>
Gives me a red border on my button.
A lightgray border is rather simpler than a button has by default.
You could reproduce that. Maybe that'd be an interesting learning exercise.
Lookup the button template on msdn.
Google: "wpf button template msdn"
Take a look at that. Brace yourself - it is complicated.
See the button border brush is hard coded in the template?
Change the style above so it does the same.
Clue:
<Setter.Value>

Disable WPF Button Hover Effect

The standard WPF Button control has a mouseover highlight that changes the colour of the control. I'm trying to disable this effect. Whilst I see lots of questions and answers to this on SO that involve changes to XAML, I'm trying to do this 100% programmatically, and I haven't been able to find any solution to this problem.
I don't have any XAML, as my WPF app dynamically adds a list of (derived) button controls to the form. The derived button class has no XAML whatsoever.
var button = new CustomButton(); // Inherits System.Windows.Controls.Button
button.Content = textBlock; // System.Windows.Controls.TextBlock
// other various property changes to the custom button removed
button.Focusable = false;
grid.Children.Add(button);
The button works perfectly with my custom code, but I just can't remove the mouseover effect. This is particularly bad, as it's being used on a touch screen, so the last touched button retains the mouseover effect until another button is pressed or the user clicks an unused area of the window.
Thanks to Kostas in the comments, I now know I need to create a global style and apply this to my custom control, however as per my original question, all the examples of doing this involve XAML, so my question really ought to be more specifically, how can I create a global style without a mouse over trigger, entirely programmatically?
The piece of the puzzle I was missing, was that the XAML for the style doesn't need to go in the button class, it instead goes into the parent window XAML. The Style is then added to the button class as it's added to the Window:
<Window x:Class="SomeOrg.SomeApp.WpfInterface.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfInterface"
mc:Ignorable="d"
Title="SomeApp" Height="450" Width="800"
Background="#ffffffe1">
<Window.Resources>
<Style x:Key="MyButton" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border" BorderThickness="0" BorderBrush="Black" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid x:Name="grid" HorizontalAlignment="Stretch" Height="Auto" Margin="10,10,10,10" VerticalAlignment="Stretch"
Width="Auto" Background="#ffffffe1"/>
</Grid>
</Window>
In the parent window's code, where the custom button is added to the window, the above style (MyButton), is applied as follows:
var button = new CustomButton();
customButton.Style = (Style) this.Resources["MyButton"];
grid.Children.Add(button);

Implementing style for Button in WPF

I have a quick question. I have implemented style for button. And it basically works. Here is the code (complete example: you can copy and paste, it will work):
<Window x:Class="TestWPFApplication.Window5"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWPFApplication"
mc:Ignorable="d"
Title="Window5" Height="300" Width="300">
<Window.Resources>
<Style TargetType="{x:Type Button}" x:Key="AButton">
<Setter Property="Width" Value="120"></Setter>
<Setter Property="Height" Value="50"></Setter>
<Setter Property="Background" Value="DarkGreen" />
<Setter Property="Foreground" Value="LightGreen" />
<Setter Property="FontSize" Value="22" />
<Setter Property="FontWeight" Value="Light" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border CornerRadius="15" Background="{TemplateBinding Background}">
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}"
HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button x:Name="QuitButton" Style="{StaticResource AButton}" Content="Quit" />
</Grid>
</Window>
It's all fine. Button looks like this:
BUT
If I move slightly the button, and the Margin property will be auto-generated, to this...
<Button x:Name="QuitButton" Style="{StaticResource AButton}" Content="Quit" Margin="88,114,87,108.5" />
.. button will look like this:
Right and bottom side of the button has been cut off. Don't know why:/
The question is: Can anyone explain me this? Thanks in advance!
You've set the top margin to 114 and the bottom margin to 108.5 for a total of 225.5, but you've also set the total Window height to 300. That leaves just 77.5 pixels for the caption bar, the top and bottom window borders and the button (which you've set to 120 pixels high). The only way for WPF to make everything fit is to crop the button. (The same thing is happening in the X axis).
Set WindowStyle="None" and ResizeMode="NoResize" on your main window and you'll see the button now has enough room to draw fine. Better yet, set the right and bottom margin values to 0 and you can now set left and top to whatever you want.

Wpf, style is not being applied

I've written a user control with popup, who's content is being set outside the control. The ControlTemplate of that control looks like the following:
<ControlTemplate TargetType="local:InfoIcon">
<Grid>
<ToggleButton x:Name="HelpButton" Style="{StaticResource HelpButton}" />
<Popup PlacementTarget="{Binding ElementName=HelpButton}" Placement="Bottom"
IsOpen="{Binding ElementName=HelpButton, Path=IsChecked, Mode=TwoWay}" StaysOpen="False">
<Border BorderBrush="#767676" BorderThickness="1"
Background="#f1f2f7">
<Border.Resources>
<!-- Important -->
<Style TargetType="Label">
<Setter Property="Foreground" Value="#575757" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#575757" />
</Style>
<!-- /Important -->
</Border.Resources>
<ContentPresenter Content="{TemplateBinding HelpContent}" />
</Border>
</Popup>
</Grid>
</ControlTemplate>
The Important part - I want to assign custom styles to items, which are being put inside the popup (it serves as a clickable hint)
I'm using my control in the following way:
<local:MyControl>
<local:MyControl.HelpContent>
<TextBlock>Ala ma kota</TextBlock>
</local:MyControl.HelpContent>
</local:MyControl>
But despite styles in the Border, TextBlock's text's color always inherit the value from its parent (checked using Snoop) - resulting in white text on white background.
You can downlad the small PoC application, which demonstrates the problem.
My observations:
The styling does work for Label. It only doesn't work for TextBlock.
When I add TextBlock.Foreground="Red" to the Border, TextBlock becomes red, still ignoring style (but now using color from Border).
Snoop informs, that this TextBlock actually has the Style resolved correctly. But despite it shouldn't, it uses the inherited value instead of one specified in the style.
How can I solve this problem and why does it occur?
I received answer on Microsoft forums; I'll leave it here in case someone encounters the same problem.
The difference is that a TextBlock is not a control, i.e. it doesn't have any ControlTemplate and because of this the implicit style doesn't get applied to it when it is located inside the StackPanel. Please see the following page for more information: http://blogs.msdn.com/b/wpfsdk/archive/2009/08/27/implicit-styles-templates-controls-and-frameworkelements.aspx
You could use Label elements or set the style for the TextBlock elements explicitly.
-- Magnus (MM8)
Edit2
I've set the Foreground of the UserControl to something else. This behavior is because the child TextBlock controls of the UserControl inherit the Foreground-Settings somehow. This has nothing to do with the popup or some other approaches we tried yet.
I've stumbled upon another question with a similar problems here: Cannot override controls foreground colour in wpf
I suggest to accept this strange behavior and just set a Foreground Color of the UserControl instead:
<Style TargetType="{x:Type local:InfoIcon}">
<Setter Property="Foreground" Value="Red"/>
</Style>
previous Edit
You had my curiousity with this weird behavior, but after looking at your PoC it was rather obvious :) The Popup has some attached Properties TextElement.* where you can style the text elements in the popup. This was new to me, too and I will reseach a bit more afterwards. Nevertheless: Workaround for your Problem is to not style the TextBlock but the Popup instead. your code could look something like following :
<ControlTemplate TargetType="{x:Type local:InfoIcon}">
<ControlTemplate.Resources>
<Style TargetType="Popup">
<Setter Property="TextElement.Foreground" Value="Red"/>
</Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red" />
</Style>
</ControlTemplate.Resources>
<Grid>
<ToggleButton x:Name="TB" Width="16" Height="16"/>
<Popup Placement="Bottom" PlacementTarget="{Binding ElementName=TB}" IsOpen="{Binding ElementName=TB, Path=IsChecked}" StaysOpen="False">
<ContentPresenter Content="{TemplateBinding InfoContent}"/>
</Popup>
</Grid>
</ControlTemplate>
I changed the styles to be outside of the controls, of course you can just use the attached properties of the popup directly. But initially you wanted to know how it works with the styles attached at the border, it does not matter now where you add the styles. You can use a ResourceDictionary for example.
As a suggestion, shouldn't this:
TargetType="local:InfoIcon"
be like this?
TargetType="{x:Type local:InfoIcon}"
Maybe you have some TextBlock style defining that it shouldd take the parent's control foreground.
Did you try to add a BasedOn property like this ?
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Foreground" Value="#575757" />
</Style>
I tried with your code example and this works :
<ContentPresenter Content="{TemplateBinding InfoContent}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="TextBlock.Foreground" Value="Red" />
</Style>
</ContentPresenter.Style>
<ContentPresenter.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Red" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
That's kind of odd because when I put the Foreground setter for the Label control inside the ContentPresenter.Style then this time it's Label wich doesn't work...I think it's because Label is a considered as a ContentControl whereas TextBlock is just a FrameworkElement.
Had a similar issue caused by another problem:
There is a strange bug in WPF that prevents styles, defined in merged dictionaries, from being applied to the first element:
https://www.engineeringsolutions.de/wpf-fix-style-is-only-applied-to-first-element/

MouseLeftButtonDown event isn't fired in child ContentControl

I have PortItem which derived from ContentControl, TextedStackPanel derived from StackPanel which contains PortItems. And in MainWindow i have 2 StackPanels which contains TextedStackPanels . In PortItem i have overridden MouseLeftButtonDown method. But when i do this on this method isn't fired. I searched here in the forum, and found that Background property of Grid/StackPanel must be set to Transparent. I applied this, but there is no changes. What to do ?
EDIT 1
I use partial classes. I have 2 classes: PortItem.cs and PortItem.cs.xaml. I modifiy any visual changes in this XAML file.
EDIT 2
Also any mouse events aren't fired. Triggers which i use IsMouseOver are also dont work when i keep mouse on PortItem
XAML
<ContentControl x:Class="**.PortItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UI="clr-namespace:**.UIData" Width="17" Height="17" Margin="3" SnapsToDevicePixels="True" >
<Grid Background="Transparent" Name="mainGrid">
<!-- transparent extra space makes connector easier to hit -->
<Rectangle Fill="Transparent" Margin="-2"/>
<Border BorderBrush="Green" x:Name="border" BorderThickness="2">
<Border.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}, Path=IsMouseOver}" Value="True">
<Setter Property="Border.BorderBrush" Value="Blue"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Border.BorderBrush" Value="Blue"/>
</DataTrigger>
<!--<DataTrigger Binding="{Binding ContactPort}" Value="{x:Null}">
<Setter TargetName="border" Property="Border.BorderBrush" Value="Green"/>
</DataTrigger>-->
</Style.Triggers>
</Style>
</Border.Style>
<Image Source="/**;component/Resources/1337238611_port.png">
</Image>
</Border>
</Grid>
Make sure that you haven't set 'IsHitTestVisible' to false on your PortItem. Also, make sure no other controls are on top of it. If they are, set their 'IsHitTestVisible' property to false and then your PortItem control will get the mouse right click event. To make sure nothing is on top, declare your put your PortItem as the last thing added to your TextedStackPanel. To double check that nothing else is on top, change the background color of other controls to something really noticeable (just for testing) to see if anything is covering your PortItem control. Also, change the color on your PortItem control to verify that it is really where you think it is. Then once you get it all working, change the colors back to their original colors.
If you could give us a code sample of your XAML, that might help. If you're adding the PortItems dynamically in code behind, supply that code too.
Edit: in light of the changes you've made to your code, try to add ClipToBounds="False" to the top of your user control declaration.
<ContentControl x:Class="**.PortItem" ClipToBounds="False"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UI="clr-namespace:**.UIData" Width="17" Height="17" Margin="3" SnapsToDevicePixels="True" >
Have you created a template for your PortItem? I created the follow class to replicate your PortItem and break point on the base.OnMouseButtonDown line and it fires, I think the reason your method is not executing is because there is no visual element for the mouse to actually interact with, try adding the style below to your app and you should see the method fire properly.
public class PortItem: ContentControl
{
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
}
}
then in XAML I created a style to give it something to render.
<local:PortItem Margin="44,36,156,95">
<local:PortItem.Style>
<Style TargetType="{x:Type local:PortItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PortItem}">
<Border Background="Transparent">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:PortItem.Style>
</local:PortItem>
The background being Transparent that you mention you can see in the border control, if you leave the background out you are correct, the event never fires.

Categories

Resources