WindowChrome button not hit-test visible when disabled - wpf

In WPF you can customize the caption bar using WindowChrome. Adding buttons in the non-client area is fairly simple as long as you remember to set WindowChrome.IsHitTestVisibleInChrome="True".
Now, there is a bug or unexpected/weird behavior when a disabled button is double-clicked. Nothing should happen, but instead the app is maximized.
Steps to reproduce
Create a new WPF project. Preferably targeting .NET 6.
Paste the code below in MainWindow.xaml.
Run the app and double-click the button.
Expected: Nothing happens
Actual: The app is maximized
<Window x:Class="WpfChromeTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" WindowStyle="None" Height="450" Width="800">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="20" />
</WindowChrome.WindowChrome>
<Window.Template>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Width="200" Height="28" VerticalAlignment="Top"
Content="I'm disabled! DOUBLE-CLICK ME!"
WindowChrome.IsHitTestVisibleInChrome="True"
IsEnabled="False" />
</Grid>
</ControlTemplate>
</Window.Template>
</Window>
What am I missing here? Is it a bug? And if so, is there a workaround?
Update
The accepted answer got it right. The reason why it works can be seen in WindowChromeWorker.cs(670) where the call to UIElement.InputHitTest will indeed skip any disabled element. However, on line 673 we find the magic that allows for the suggested solution:
When the parent element has WindowChrome.IsHitTestVisibleInChrome set to true, the callback will correctly return HTCLIENT, effectively swallowing our double click.
In the provided sample, we could simply replace <Grid> with the following, to get the desired behavior:
<Grid WindowChrome.IsHitTestVisibleInChrome="True">

Double clicking on the title bar causes the window to change its state,
when the button is enabled, the double click will be handled by the button and not passed to the title bar.
when the button is disabled, the double click will be passed to the title bar, this will happen if you replace the button with any disabled UIElement.
So the current behavior is normal.
Is there a workaround?
Yes, If you want to disable WindowState change regardless of the button's IsEnabled value, wrap <Button/> with another UI element that will prevent the double click from being passed to the window's title bar if the button is disabled.
<ContentControl
Grid.Row="0"
Width="200"
Height="28"
VerticalAlignment="Top"
WindowChrome.IsHitTestVisibleInChrome="True">
<Button
x:Name="MyButton"
Content="I'm disabled! DOUBLE-CLICK ME!"
IsEnabled="False" />
</ContentControl>

Related

Button Border clipped in WPF with 125% DPI Setting

When I use 125% DPI setting in Windows, sometimes a button border is not rendered. It seems to depend on the size and position, maybe also the parent element of a button:
In my app, I can also see in the WPF designer that a parent border seems to be too small in size, though the button itself is larger. Maybe the measure process is somehow wrong:
I tried changing UseLayoutRounding for the whole window, also changing SnapsToDevicePixels does not help.
DPI Awareness is set in the manifest.
Does anyone know how to fix this for the entire application?
Thats the code in a default Wpf Window Application (I can't reproduce the problem right now, but the problem persists in my application):
<Window x:Class="WpfApp3.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"
mc:Ignorable="d"
Title="MainWindow"
SizeToContent="WidthAndHeight">
<StackPanel>
<GroupBox>
<GroupBox.Header>
<Button Width="20"
Height="22"
Content="X" />
</GroupBox.Header>
</GroupBox>
<Button Width="20"
Height="22"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Content="X" />
</StackPanel>
</Window>

WPF: Textblock text in Popup goes outside of the main window

In my example wpf app I've added one button and one popup to the window. The button is in the bottom right corner and the popup has set "PlacementTarget" property to it and "Placement" set to top. The popup consists of one very long textblock.
What I expect this popup will behave is not to go outside of the window and therefore automatically set his "HorizontalOffset" to the appropriate value, but the popup behaves against my intentions.
Here's my xaml file:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Name="window" x:Class="WpfApplication1.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:Converters x:Key="Converters"/>
</Window.Resources>
<Grid>
<Button x:Name="button" Content="Button" VerticalAlignment="Bottom" Width="75" HorizontalAlignment="Right"/>
<Popup Placement="Top" PlacementTarget="{Binding ElementName=button, Mode=OneWay}" IsOpen="True">
<TextBlock TextWrapping="Wrap" Text="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" Background="White"/>
</Popup>
</Grid>
Do anyone know how to fix it?
I've read that this should be default popup behavior to take care of going out of the boundaries, but not in my case. Thanks in advance.
Have you tried to set the width of the Popup or Textblock ?
Sorry, I can't write this poor answer as a comment..

Drawing in a button_click event makes the button disappears?

I'm still new in WPF and still facing some problems in working with it. I want to draw some circles when I click a button. I put the instructions for drawing this circle in the method of button_click which means the circles should be drawn when I click the button. The circles are drawn correctly but all the buttons and tools I placed in the form disappear. I suppose this occurs because I'm drawing in the grid where the button is placed. So is there a way to draw the circles inside a certain border or something without anything disappears?
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="449" Width="677">
<Grid Height="297" Width="460">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="181*" />
<ColumnDefinition Width="279*" />
</Grid.ColumnDefinitions>
<Button Content="Button" Height="31" HorizontalAlignment="Left" Margin="185,-53,0,0" Name="button1" VerticalAlignment="Top" Width="94" Click="button1_Click" Grid.Column="1" />
</Grid>

WPF Dockpanel first child uses remaining space

In a window I have there is a list of DockPanels to specify a couple of files. Each DockPanel has a TextBox (for the path) and a button (to browse for a file).
I've recreated a simple WPF page to demostrate the problem here:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="150"
Height="22">
<DockPanel>
<TextBox HorizontalAlignment="Stretch"/> <!-- path to file -->
<Button Content="..." DockPanel.Dock="Right"/> <!-- button to browse for file -->
</DockPanel>
</Page>
The problem is that I want the button to the right of the textbox, but that causes the textbox to be really small since the LastChild of the DockPanel is the button it uses up the remaining space. Ive tried to change this by shuffling them around and setting LastChildFill="False" but that only causes the button to be small again, not making the TextBox wide (even with HorizontalAlignment="Stretch").
The reason I want the controls in that order is I want the user to reach the TextBox before the Button when using tab to navigate in the window. I looked into setting TabIndex but it feels hacky, favorite features of WPF is that tabindex are in the order the conrols are defined in the XAML. Not to mention that I probably would have to manually set TabIndex on everything in the Window.
To me, it seems that the setting of TextBox.HorizontalAlignment isn't respected. How can I make the first control use as much space as it can but still preserve tab order?
Make it like this:
<DockPanel LastChildFill="True">
<Button Content="..." DockPanel.Dock="Right"/> <!-- button to browse for file -->
<TextBox DockPanel.Dock="Left" HorizontalAlignment="Stretch"/> <!-- path to file -->
</DockPanel>
If you don't want the behavior of DockPanel, don't use a DockPanel.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox />
<Button Content="..." Grid.Column="1"/>
</Grid>

How to determine if a custom control is in a Toolbar?

i have created a UserControl to make an ImageButton:
<Button x:Class="myimagebutton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:imagebutton">
<Grid x:Name="grdButton">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
x:Name="btnImage"
HorizontalAlignment="Left"
VerticalAlignment="Center">
</Image>
<TextBlock Grid.Column="1"
TextWrapping="Wrap"
Text="{Binding Text}"
VerticalAlignment="Center"
Margin="2 0 2 0" />
</Grid>
</Button>
now i want to apply the default Toolbar Button Style to my Button if this Button is in a Toolbar. I have read this article link text and put this
If Me.Style Is Nothing AndAlso TypeOf Me.Parent Is ToolBar Then
Me.Style = DirectCast(FindResource(ToolBar.ButtonStyleKey), Style)
End If
in my Code behind.
After that as a test I put my Button both in a Toolbar and another out of the Toolbar to test it. But the Button always get the default style, not the style I am trying to set.
After debugging i find out that Me.Parent is always Nothing. So now is my question: how i get the information that my button is in a toolbar or not?
I'm having some difficulty understanding exactly what you are describing but after reading it through a few times I think I understand.
Am I right so far?
If so, you are wondering then why your button has an image
A few pointers about your description that threw me off and is probably the reason why you haven't seen anybody else post an answer for your question thus far.
i replaced the the UserControl Item with a Button
Essentially what you have done is created new control that likely inherits from Button. You might have started off with a UserControl but in order to replace the root item in XAML you would also have to make sure your type myimagebutton inherits from Button as well. This is just how XAML works and learning how to explain it this way will help people understand what you are doing.
Normally inheriting from Button is not how developers override the visual style of a button in WPF mainly because WPF doesn't support the concept of what is sometimes referred to as visual inheritance and also there are other suitable methods that can be used to solve the problem in a different way. Instead inheritance is mainly reserved for when behavioral modifications or additions need to be made to an existing control class. This being said there are ways to simulate visual inheritance through the use of content controls that work similar to content pages and master pages in ASP.NET but I think this is a bit outside of the scope of your example. Also if you are to pursue the inheritance model you will need to make sure that in your code behind that you are setting the correct default style in the static constructor so posting your code behind for your button would help too.
I believe the reason why your example isn't working is because the ToolBar specifically looks at the types of controls irrespective inheritance in order to to apply it's custom toolbar styles. In your case your control is of type myimagebutton and not Button so the style is not set by the ToolBar which normally directly sets the Style property based on the type of the control using the two potential types of calls.
element.SetResourceReference(FrameworkElement.StyleProperty, styleKey);
element.DefaultStyleKey = styleKey;
BTW, in your case I believe only the second line is performed by the ToolBar control and styleKey at that point is defined as null.
Now instead of inheriting from Button in the first place you would probably be better off just to create a new ControlTemplate or a DataTemplate for your button and assigning into the Template or ContentTemplate property respectively through the use of a style. This way you are still always dealing with a button and the style is what changes the visual properties.
<Window x:Class="HeaderedContentControlTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="252"
Width="372">
<Window.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Background"
Value="Orange" />
</Style>
<DataTemplate x:Key="ImageButtonDataTemplate">
<Grid x:Name="grdButton">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center">
</Image>
<TextBlock Grid.Column="1"
TextWrapping="Wrap"
Text="{Binding}"
VerticalAlignment="Center"
Margin="2 0 2 0"
Background="Pink" />
</Grid>
</DataTemplate>
<Style x:Key="ImageButtonStyle"
TargetType="{x:Type Button}">
<Setter Property="ContentTemplate"
Value="{StaticResource ImageButtonDataTemplate}" />
</Style>
</Window.Resources>
<Grid Margin="11">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ToolBar>
<Button Style="{StaticResource ImageButtonStyle}"
Content="Some Text" />
</ToolBar>
<Button Grid.Row="1"
Style="{StaticResource ImageButtonStyle}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Content="Some Text" />
</Grid>
</Window>
Using the ContentTemplate allows you to redefine the inner contents of the Button without loosing all of the special button state transitions and other niceties you would normally like to keep.
See this related post on MSDN Forums that also explains similar behavior when adding a StackPanel containing buttons to a ToolBar.

Resources