Child control blocking parent's event handler - wpf

I have a user control which has a registered MouseDown eventhandler. The user control has a ScrollViewer with an ItemsControl and a TextBlock. The visibility of the ItemsControl and TextBlock is toggled by the MouseDown eventhandler so at a time only one of them is visible. The eventhandler is invoked correctly when I click the TextBlock but not in the ItemsControl.
If I set the "IsHitTestVisible" on the ItemsControl to false the view's eventhandler gets exposed but I'm not able to scroll.
Can someone suggest a way out please?
Code is somewhat like this:
<Grid>
<Border>
<ScrollViewer>
<TextBlock>
</ScrollViewer>
<Border>
<Border>
<Grid>
<ItemsControl/> <!-- Has a ScrollViewer in template to show scroll bar -->
</Grid>
<Border>
</Grid>

Your UI scenario sounds kinda strange to me, but if you still want to implement it then you can do the following. You are not getting event raised for the paren becaus ItemsControl marks it as handled, so it is not routed further. You can subscribe to PreviewMouseDown event instead of MouseDown in the parent. Then no matter what the parent will receive notification before it's children.
UPDATE
You can do the following
<ScrollViewer>
<Grid MouseDown="HandleMouseDown">
<TextBlock />
<ItemsControl IsHitTestVisible="false"/>
</Grid>
</ScrollViewer>
So you will have scroll for text block and for items. ItemsControl's own scroll bars will not be used.
UPDATE 2
If you want to have separate ScrollViewers, then another possibility can be the following (although I think some of borders and grids are really unneeded. Only if they are functional in your snippet)
<Grid>
<Border>
<ScrollViewer>
<TextBlock MouseDown="HandleMouseDown">
</ScrollViewer>
<Border>
<Border>
<Grid>
<ScrollViewer>
<Grid MouseDown="HandleMouseDown">
<ItemsControl IsHitTestVisible="false"/>
</Grid>
</ScrollViewer>
</Grid>
<Border>
</Grid>

Related

Why does the Background property affect the region where clicks are registered?

Apparently, setting a UniformGrid's Background property affects the control's boundaries. See the following example:
<StackPanel>
<UniformGrid Rows="0" Columns="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="32" Background="Aqua" PreviewMouseUp="UIElement_OnPreviewMouseUp">
<!--Clicking on this UniformGrid fires the event-->
</UniformGrid>
<Border BorderBrush="Black" BorderThickness="1"></Border>
<UniformGrid Rows="0" Columns="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="32" PreviewMouseUp="UIElement_OnPreviewMouseUp">
<!--Clicking on this UniformGrid DOESN'T fire the event-->
</UniformGrid>
<Border BorderBrush="Black" BorderThickness="1"></Border>
</StackPanel>
Both UniformGrids take up space of the same size when rendered. However, clicking inside the second UniformGrid's area doesn't raise the PreviewMouseUp event. The only difference between the two controls is the Background property.
Why does the Background property affect the region where clicks (and presumably other input events) are registered?
Edit:
If I declare the Background to be Transparent, the event is raised properly. The interaction is ignored only when there's no Background declaration at all.

Keyboard navigation in WPF Grids

Suppose you have a StackPanel, which contains a ScrollViewer which contains another StackPanel with an ItemsControl with a bound ItemsSource. This ItemsSource is bound to a collection of Grids created at runtime. Each Grid contains a label and a textbox/combobox/a few checkboxes that all have a unique TabIndex value within the StackPanel.
Here is the xaml:
<ScrollViewer Name="scrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Name="stackPanel" MinWidth="500" Width="Auto">
<ItemsControl Name="itemsControl" ItemsSource="{Binding ElementName=SomeWindow, Path=GridsCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</ScrollViewer>
I want to simply tab from one control to the next, but only within the controls in the grids in the grids collection. So far I've tried different KeyboardNavigation.TabNavigation settings but without any luck. What is the best way to do this?
Set TabNavigation to KeyboardNavigationMode.Cycle for each container you want to behave like that, so the focus won't escape it as long as you use Tab and Shift+Tab:
KeyboardNavigation.SetTabNavigation(grid1, KeyboardNavigationMode.Cycle);
If you want to change Ctrl+Tab behaviour, use KeyboardNavigation.SetControlTabNavigation.
You can apply an implicit style that disables tabbing for every Control, then you re-enable it for just what you want to be tab-able:
<ScrollViewer Name="scrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<Style TargetType="Control">
<Setter Property="IsTabStop" Value="False" />
</Style>
</ScrollViewer.Resources>
<StackPanel Name="stackPanel" MinWidth="500" Width="Auto">
<ItemsControl Name="itemsControl"
ItemsSource="{Binding ElementName=SomeWindow, Path=GridsCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</ScrollViewer>
Don't forget to set IsTabStop on your dynamically generated Grids to True

Minimum resizing

Inside a grid have a ContentControl inside a Grid where I load a UserControl.
I want the user to resize the window, but how can I prevent resizing-down the window so it will be less the the user control?
In other words, user control should be always visible on the window.
<Grid>
<Border>
<ContentControl Content="{Binding Path=THeModel}">
</ContentControl>
</Border>
</Grid>
Use the MinWidth and MinHeight properties of the Window to set a minimum width and height.
<Window MinWidth="200" MinHeight="200" ... > ... </Window>
If it depends on its content, you can try binding these properties to the ActualWidth/ActualHeight of another control:
<Window MinWidth="{Binding ElementName=MyControl, Path=ActualWidth}" ... > ... </Window>
But this will only work well if MyControl has a fixed size - if it grows with the window, then the results will not be ideal.

Prevent WPF control from expanding beyond viewable area

I have an ItemsControl in my user control with a scroll viewer around it for when it gets too big (Too big being content is larger than the viewable area of the UserControl). The problem is that the grid that it is all in just keeps expanding so that the scroll viewer never kicks in (unless I specify an exact height for the grid). See code below and thanks in advance.
<UserControl x:Class="BusinessObjectCreationWizard.View.TableSelectionPageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<GroupBox FontWeight="Bold" Height="300px"
Header="Tables"
Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal"
ItemsSource="{Binding Path=AvailableTables}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Path=DisplayName}"
IsChecked="{Binding Path=IsSelected}"
Margin="2,3.5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</UserControl>
This user control is loaded here
<Border Background="White" Grid.Column="1" Grid.Row="0">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"
Header="{Binding Path=CurrentPage.DisplayName}" />
</Border>
I would like to not specify the height.
If you remove the Height from your GroupBox (which, as far as I understand, is what you want to do), then it will fill its container, unless there's a panel upstream that imposes its own sizing rules.
I used this simplified version of your XAML. I removed the template and the binding, and hard-coded some items, to make this stand alone; those changes won't affect the way layout is done.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<GroupBox FontWeight="Bold" Header="Tables" Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal">
<TextBlock>Foo</TextBlock>
<TextBlock>Bar</TextBlock>
<TextBlock>Baz</TextBlock>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</Window>
Run it, and you'll see that the content does indeed size to fit the window, and the scrollbar only enables when the window gets too small to see all three items. I believe this is what you want.
So the problem is most likely one of the parent panels, one you're not showing in your sample XAML. The problem you describe could occur if your GroupBox appears inside a StackPanel:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<StackPanel>
<GroupBox FontWeight="Bold" Header="Tables" Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal">
<TextBlock>Foo</TextBlock>
<TextBlock>Bar</TextBlock>
<TextBlock>Baz</TextBlock>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Window>
Now the GroupBox appears at the top of the Window, sized to exactly fit its contents. If you shrink the Window enough, the GroupBox will be cut off -- because it's sized to fit its content, not its container. This sounds like the problem you're describing.
The reason is that StackPanel asks its children what their ideal height is (based on their content), and uses that height. Without StackPanel (or something similar), the default is to respect the control's VerticalAlignment, and if that's set to the default value of Stretch, then the control is stretched to fill its parent. This means it won't be taller than its parent, which sounds like what you want.
Solution: remove the StackPanel (or whatever else is causing you problems) and use something else. Depending on what you're trying to accomplish, you might have better luck with a DockPanel or a Grid. Hard to tell without knowing more about your layout.
Edit: Okay, it looks like the problem is indeed the HeaderedContentControl parent -- but not directly. HeaderedContentControl isn't a panel, so it doesn't do any layout of its own (and its descendant, GroupBox, doesn't have this same problem). The problem is its default template -- which includes a StackPanel. The good news is, you're free to use a different template, let's say one with a DockPanel instead:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<HeaderedContentControl>
<HeaderedContentControl.Style>
<Style TargetType="{x:Type HeaderedContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedContentControl}">
<DockPanel>
<ContentPresenter ContentSource="Header" DockPanel.Dock="Top"/>
<ContentPresenter/>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</HeaderedContentControl.Style>
<GroupBox FontWeight="Bold" Header="Tables" Padding="2">
<ScrollViewer>
<ItemsControl FontWeight="Normal">
<TextBlock>Foo</TextBlock>
<TextBlock>Bar</TextBlock>
<TextBlock>Baz</TextBlock>
</ItemsControl>
</ScrollViewer>
</GroupBox>
</HeaderedContentControl>
</Window>
If you leave off the <HeaderedContentControl.Style> part, this reproduces your problem; but with the style in place, it allows the GroupBox to fill its container, so the ScrollViewer will get a scrollbar when you want it to.
If the previous answer doesn't fix the problem, you could also try binding the Width, Height of your grid to the ActualWidth, ActualHeight of your parent UserControl. Something like:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication.UserControl1"
x:Name="UserControl">
<Grid Height="{Binding ElementName=UserControl, Path=ActualHeight}"
Width="{Binding ElementName=UserControl, Path=ActualWidth}" />
In this case you aren't setting an explicit width and height but you are limiting the Grids width/height to the constraints of the UserControl it sits in.
I had the same issue, after reading this response I replaced all StackPanels with Grids in UserControl. It resolved the Scrollbar issue.
Try removing the grid entirely and setting the HorizontalAlignment and VerticalAlignment directly on the GroupBox. If a layoutpanel has only one child, it's often redundant... this migth be true in your case.
If that doesn't work... what's the parent of your grid control?
Why not just use a listbox instead of an itemscontrol, that has a built in scrollviewer.
They are different. If you do not want to have the items selectable, then don't use a ListBox. It is going to be heavier, and will also have the deselect a selection everytime the user clicks on an entry. Just put the ItemsControl in a ScrollViewer
I had the same problema with ListBox, it wasn't expanding and the scroll viewer didn't appear. I solved it as follows:
<UserControl x:Class="TesteView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid MaxHeight="710">
....
....
<StackPanel>
<ListBox MaxHeight="515"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding Path=Teste,Mode=TwoWay}">
....
....
</ListBox>
</StackPanel>
</Grid>
</UserControl>

Making a control "transparent" to click events

I have a ListBox displaying some items, and in certain modes I "stamp" a kind of watermark across the top of it. I've done this with a Border containing a TextBlock with an Opacity of 0.5. All this works nicely.
However, I still want the user to be able to click on the items in the ListBox but if I click on the "stamp" it obviously eats the click events and they're not seen by the ListBox.
What do I have to do to prevent this? (i.e. allow the ListBox to see the Click event)
Thanks,
Craig
You can do this with the IsHitTestVisible property:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ListBox>
<ListBoxItem>a</ListBoxItem>
<ListBoxItem>b</ListBoxItem>
<ListBoxItem>c</ListBoxItem>
</ListBox>
<Border Opacity="0.2" Background="Cyan" BorderBrush="Black" BorderThickness="5" IsHitTestVisible="False" >
<TextBlock Text="EXAMPLE" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>

Resources