ScrollViewer not working with MouseWheel on GroupBox - c#

I am trying to get a scrollviewer to work in a custom styled groupbox.
This is the style for the groupbox:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--Set default style of groupbox-->
<Style TargetType="GroupBox">
<Setter Property="Margin" Value="0, 10, 0, 0"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Border CornerRadius="4" BorderThickness="1" BorderBrush="{StaticResource BorderBrush}" Background="{StaticResource ContentBackgroundBrush}">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical" CanVerticallyScroll="True">
<Label Content="{TemplateBinding Header}" Margin="5,5,0,0" Style="{StaticResource SmallTitle}"></Label>
<ContentPresenter Margin="10, 5, 10, 10" RecognizesAccessKey="True" x:Name="CtlGroupboxPresenter" />
</StackPanel>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The scrollbar shows up, but I can't scroll using the mouse wheel. It works however when my mouse if over the vertical scrollbar. It seems like a tracking issue.
I saw some guys on SO that suggest adding some code to code behind to get it working, but as this is in a resource dictionary I have no place where I could put it...
Does anyone know what the issue is?
Here is an image of the wpf form:
XAML inside the groupbox:
<UserControl x:Class="Sun.Plasma.Controls.ViewNews"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
<DockPanel LastChildFill="True">
<Label DockPanel.Dock="Top" Style="{StaticResource LblTitle}" FontWeight="Bold" FontSize="24" >Latest SUN news & announcements</Label>
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical" Name="CtlLoadingNews">
<Label Style="{StaticResource LblContent}">Loading content from server...</Label>
<ProgressBar IsIndeterminate="True" Height="30" />
</StackPanel>
<ListView Background="Transparent" DockPanel.Dock="Bottom" ItemsSource="{Binding NewsFeeds}" BorderBrush="Transparent" Name="CtlNews" Visibility="Collapsed">
<!-- Defining these resources prevents the items from appearing as selectable -->
<ListView.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="0 0 0 20">
<Label Style="{StaticResource LblTitle}" FontWeight="Bold" Content="{Binding Title}" />
<StackPanel Orientation="Horizontal">
<Label Style="{StaticResource LblFooter}" Content="{Binding PublishDate}" />
<Label Style="{StaticResource LblFooter}">By</Label>
<Label Style="{StaticResource LblFooter}" Content="{Binding Authors[0]}" />
<Label Style="{StaticResource LblFooter}">
<Hyperlink RequestNavigate="Hyperlink_RequestNavigate" NavigateUri="{Binding Source}">Read entry</Hyperlink>
</Label>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DockPanel>

The problem is that the ListView in the contents of the GroupBox stops the MouseWheel event from bubbling up to the ScrollViewer. I found a hacky solution:
You handle the PreviewMouseWheel event on the inner ListView and raise the MouseWheel event directly on the scroll viewer.
private void ListView_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (!e.Handled)
{
e.Handled = true;
var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
eventArg.RoutedEvent = UIElement.MouseWheelEvent;
eventArg.Source = sender;
//navigate to the containing scrollbar and raise the MouseWheel event
(((sender as ListView).Parent as GroupBox).Content as ListView).RaiseEvent(eventArg);
}
}
Again, this is not a solution I particularly like, because it is dependent on the layout of the GroupBox.
A second, slightly better way is to add a style to the resources of the GroupBox in which you add a handler to the PreviewMouseWheel event:
<GroupBox Header="test">
<GroupBox.Resources>
<Style TargetType="ScrollViewer">
<EventSetter Event="PreviewMouseWheel" Handler="ScrollViewer_PreviewMouseWheel" />
</Style>
</GroupBox.Resources>
<!-- your contents -->
</GroupBox>
The event handler then does the scrolling:
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var scrollViewer = sender as ScrollViewer;
double change = e.Delta;
double currentPosition = scrollViewer.VerticalOffset;
scrollViewer.ScrollToVerticalOffset(currentPosition - change);
}

Related

Button binding not working which is in Expander, which is in a listview

I have a strange issue. I have a listview which contains expander. That expander contains button. When I try to click on it - I am getting an error stating "Cannot find source: Element name".
When I remove CommandParameter from the code and place it again during the runtime - it works. Any ideas why that is happening? All TextBlocks are bind correctly and there are no issue, except for those buttons
Listview:
<ListView x:Name="customMeetingsListView"
Grid.Row="2"
ItemsSource="{Binding CustomMeetingsList}"
Style="{StaticResource CtMeetingsListView}"/>
List view item template:
<Style x:Key="CtMeetingsListView" TargetType="{x:Type ListView}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Expander Background="Transparent">
<Expander.Header>
...
</Expander.Header>
...
<materialDesign:Badged Grid.Row="0"
HorizontalAlignment="Right"
Badge="{Binding RelatedFiles.Count}"
Padding="0 0 8 0">
<materialDesign:PopupBox
Background="Transparent"
StaysOpen="True">
<Button Background="{DynamicResource CardBackground}"
Margin="0 -8 0 -10"
BorderThickness="0"
Command="{Binding ElementName=customMeetingsListView, Path=DataContext.AddCaseFileCommand}"
CommandParameter="{Binding}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="FileDocumentEditOutline"
VerticalAlignment="Center"
Foreground="DarkSlateGray"
Width="12" Height="12"/>
<TextBlock Margin="10 0 0 0"
Text="Add new"
Foreground="DarkSlateGray"
VerticalAlignment="Center"
FontSize="10"/>
</StackPanel>
</Button>
</materialDesign:PopupBox>
</materialDesign:Badged>
DataContext is bind via Prism:
<UserControl
...
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
...
</UserControl>
View model:
private DelegateCommand<CustomMeetingEntity> _addCaseFileCommand = null;
public DelegateCommand<CustomMeetingEntity> AddCaseFileCommand => _addCaseFileCommand ?? (_addCaseFileCommand = new DelegateCommand<CustomMeetingEntity>(AddCaseFile));
Regards,
Eddie

Blur The Background Of A Custom Control

The title might not be clear but I will explain now. I have a custom control that represents a modal (inspired by SingltonSean), When I trigger a command, it shows this modal that covers the rest of the elements behind it, it somewhat acts like a popup. Now I want everything behind it to be blurred. How can I achieve that?
This is my modal custom control:
<Style TargetType="{x:Type viewModel:Modal}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type viewModel:Modal}">
<ControlTemplate.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</ControlTemplate.Resources>
<Grid Visibility="{TemplateBinding IsOpen, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid.Background>
<SolidColorBrush Color="Black" Opacity="0.025" />
</Grid.Background>
<Border HorizontalAlignment="Center" VerticalAlignment="Center" UseLayoutRounding="True" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=border}" />
</Grid.OpacityMask>
<Border x:Name="border" Background="White" CornerRadius="20" />
<Grid Width="500" Height="600">
<StackPanel Width="300" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Padding="10 5" Content="Close Modal" />
</StackPanel>
</Grid>
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You need to apply blur where content is:
<Grid>
<Grid>
<Grid.Effect>
<BlurEffect Radius="20"/>
</Grid.Effect>
<TextBlock Text="Some content I want to blur"/>
</Grid>
<TextBlock Text="Popup"/>
</Grid>
If you want to make a control, which is able to blur something before in visual tree, you still need to apply effect to it. One possibility is to use attached behavior:
public static class Behavior
{
public static UIElement GetBlurTarget(DependencyObject obj) => (UIElement)obj.GetValue(BlurTargetProperty);
public static void SetBlurTarget(DependencyObject obj, UIElement value) => obj.SetValue(BlurTargetProperty, value);
public static readonly DependencyProperty BlurTargetProperty =
DependencyProperty.RegisterAttached("BlurTarget", typeof(UIElement), typeof(Behavior), new PropertyMetadata((d, e) =>
{
if (e.NewValue is UIElement element)
element.Effect = new BlurEffect { Radius = 20 };
}));
}
Then layout should be like this:
<Grid>
<Grid x:Name="container">
<TextBlock Text="Some content I want to blur" />
</Grid>
<TextBlock Text="Popup" local:Behavior.BlurTarget="{Binding ElementName=container}"/>
</Grid>
Both cases will produce same result:

Button inside ListView not respond to ItemClick

I have three buttons inside ListView.
I tried to click each of the button, however the ItemClick function was not triggered.
There is the xaml file of the ListView
<UserControl.Resources>
<DataTemplate x:Name="DefaultWideSideItemTemplate" x:DataType="ui:AppMenuItem">
<Grid>
<ui:ButtonWithIcon IconContent="{x:Bind Icon}"
Content="{x:Bind Name}"
Style="{StaticResource TransparentFontButtonStyle}"
/>
</Grid>
</DataTemplate>
<others:SideMenuItemTemplateSelector DefaultTemplate="{StaticResource DefaultWideSideItemTemplate}" x:Key="WideSideItemTemplateSelector"/>
</UserControl.Resources>
<ListView Style="{StaticResource HorizontalCenterListView}"
IsItemClickEnabled="True"
ItemsSource="{x:Bind MenuItemCollection}"
VerticalAlignment="Top"
x:Name="SideMenuListView"
Loaded="SideMenuListView_Loaded"
ItemClick="SideMenuListView_ItemClick"
ItemTemplateSelector="{StaticResource WideSideItemTemplateSelector}">
</ListView>
There is the code where ItemClick was defined.
public event EventHandler<AppMenuItem> SideMenuItemClick;
private void SideMenuListView_ItemClick(object sender, ItemClickEventArgs e)
{
// DO SOMETHING HERE
}
There is the xaml file of the ui:ButtonWithIcon
<Style x:Key="TransparentFontButtonStyle" TargetType="ui:ButtonWithIcon">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="30"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,32,0,7"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ui:ButtonWithIcon">
<StackPanel>
<TextBlock
x:Name="IconContent"
Text="{Binding IconContent, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Center"
FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="30"/>
<TextBlock
x:Name="Text"
Text="{TemplateBinding Content}"
HorizontalAlignment="Center"
FontSize="14"
Margin="0,10,0,0"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
What did I understand wrong about ListView? Buttons can not be used in ListView?
Update: ButtonWithIcon
public class ButtonWithIcon : Button
{
public static readonly DependencyProperty IconContentProperty =
DependencyProperty.Register("IconContent", typeof(string), typeof(ButtonWithIcon), new PropertyMetadata(default));
public string IconContent
{
get { return (string)GetValue(IconContentProperty); }
set { SetValue(IconContentProperty, value); }
}
}
Update 2:
<DataTemplate x:Name="DefaultWideSideItemTemplate" x:DataType="ui:AppMenuItem">
<Grid>
<StackPanel Margin="0,32,0,7">
<TextBlock Text="{x:Bind Icon}"
HorizontalAlignment="Center"
FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="30"/>
<TextBlock
Text="{x:Bind Name}"
HorizontalAlignment="Center"
Margin="0,10,0,0"
FontSize="14"/>
</StackPanel>
</Grid>
</DataTemplate>
If the ui:ButtonWithIcon is a custom button control, then this is expected behavior. When you click on the custom button, the click event is captured by the button, so the ListView won't trigger the ItemClick event. A simple way to confirm is that you could click on the place that does not belong to the custom button. It should fire the ItemClick event of the ListView.
Update:
Since your code is too messy, I made a simple test using some of your code:
<ListView HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsItemClickEnabled="True"
ItemsSource="{x:Bind MenuItemCollection}"
x:Name="SideMenuListView"
Loaded="SideMenuListView_Loaded"
ItemClick="SideMenuListView_ItemClick"
>
<ListView.ItemTemplate>
<DataTemplate >
<Grid>
<StackPanel Margin="0,32,0,7">
<TextBlock
Text="{Binding}"
HorizontalAlignment="Center"
Margin="0,10,0,0"
FontSize="14"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This works correctly.
You could remove the extra parts of your code to narrow down the issue. Start with a easy sample like the above.

Popup and StayOpen=False doesnt work with MouseUp

I have a StackPanel with two textblocks. When the StackPanel is clicked, in the code behind I set a Popup to IsOpen=True. I would like this Popup to go away when the user clicks anywhere else in the app that is outside of the Popup.
<DataTemplate DataType="{x:Type something}">
<StackPanel Orientation="Vertical" Margin="3" MouseUp="PanelClick">
<Popup AllowsTransparency="True" PopupAnimation="Slide"
StaysOpen="False">
<Border
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Padding="4" Opacity="1" CornerRadius="3"
MinHeight="24" MaxWidth="267">
<StackPanel Orientation="Vertical">
<TextBlock Text="Something"/>
<TextBox>aaa</TextBox>
<TextBox>bbb</TextBox>
</StackPanel>
</Border>
</Popup>
<TextBlock
Text="Something else" />
<TextBlock
Text="Some other stuff" />
</StackPanel>
</DataTemplate>
In the code behind I have
private void PanelClick(object sender, MouseButtonEventArgs e)
{
var panel = sender as StackPanel;
var pop = panel?.Children.Cast<Popup>().FirstOrDefault();
if (pop != null)
{
pop.IsOpen = true;
}
}
With the code as presented, I find that the Popup does NOT go away by itself unless I first click (to give focus) into the aaa or bbb textbox and THEN click away from the pop up. It also gives away if I click on a different app. This seems in contradiction to the StaysOpen documentation [https://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.popup.staysopen%28v=vs.110%29.aspx]
However, if I put a button around my StackPanel and do the code-behind from the Click event then the Popup works as stated in the documentation
<DataTemplate DataType="{x:Type something}">
<Button Click="ButtonDo">
<StackPanel Orientation="Vertical" Margin="3">
<Popup AllowsTransparency="True" PopupAnimation="Slide"
StaysOpen="False">
<Border
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Padding="4" Opacity="1" CornerRadius="3"
MinHeight="24" MaxWidth="267">
<StackPanel Orientation="Vertical">
<TextBlock Text="Something"/>
<TextBox>moo</TextBox>
<TextBox>moo</TextBox>
</StackPanel>
</Border>
</Popup>
<TextBlock
Text="Something else" />
<TextBlock
Text="Some other stuff" />
</StackPanel>
</Button>
</DataTemplate>
Why is there this difference of behaviour?
Also, as a curiosity, attempting to use codebehind with MouseUp from the button doesn't even call the code behind - but that's a problem I don't need to know why.

Frame doesn't work anymore after adding template

I'm trying to learn WPF, I created a resource dictionary with a template for my Frame but after adding the template the Frame doesn't display my Pages anymore. When I remove the template, everything works again (the pages are properly displayed). What am I doing wrong?
ResourceDictionary.xaml (just very basic)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTemplate x:Key="frameTemplate" TargetType="{x:Type Frame}">
<Grid>
<Border BorderBrush="Tomato" BorderThickness="3" Background="Bisque"/>
</Grid>
</ControlTemplate>
</ResourceDictionary>
MainWindow.xaml
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/Icons.xaml" />
<ResourceDictionary Source="ResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Controls:MetroWindow.RightWindowCommands>
<Controls:WindowCommands>
<ToggleButton Content="Menu"
IsChecked="{Binding ElementName=Flyout, Path=IsOpen}" Cursor="Hand"/>
</Controls:WindowCommands>
</Controls:MetroWindow.RightWindowCommands>
<Grid>
<Grid x:Name="menu_grid">
</Grid>
<!-- flyout here, the title bar is not overlapped -->
<Controls:Flyout x:Name="Flyout"
Width="200"
Header="Menu"
IsOpen="True"
Position="Left">
<StackPanel>
<Button HorizontalContentAlignment="Center" VerticalAlignment="Center" Margin="10" Click="DriverButton_Click">
<StackPanel Orientation="Horizontal">
<Rectangle Height="16" Width="16" Margin="5">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource appbar_people}" Stretch="Fill" />
</Rectangle.Fill>
</Rectangle>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Drivers</TextBlock>
</StackPanel>
</Button>
<Button HorizontalContentAlignment="Center" VerticalAlignment="Center" Margin="10" Click="SeasonButton_Click">
<StackPanel Orientation="Horizontal">
<Rectangle Height="16" Width="16" Margin="5">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource appbar_calendar}" Stretch="Fill" />
</Rectangle.Fill>
</Rectangle>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Seasons</TextBlock>
</StackPanel>
</Button>
<Button HorizontalContentAlignment="Center" VerticalAlignment="Center" Margin="10" Click="ConstructorsButton_Click">
<StackPanel Orientation="Horizontal">
<Rectangle Height="16" Width="16" Margin="5">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource appbar_team}" Stretch="Fill" />
</Rectangle.Fill>
</Rectangle>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Constructors</TextBlock>
</StackPanel>
</Button>
</StackPanel>
</Controls:Flyout>
<Grid Margin="200 0 0 0">
<Frame x:Name="_mainFrame" Template="{StaticResource frameTemplate}" />
</Grid>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow
{
DriversPage driversPage = new DriversPage();
SeasonPage seasonsPage = new SeasonPage();
ConstructorsPage constructorsPage = new ConstructorsPage();
public MainWindow()
{
InitializeComponent();
}
private void DriverButton_Click(object sender, RoutedEventArgs e)
{
_mainFrame.Navigate(driversPage);
}
private void SeasonButton_Click(object sender, RoutedEventArgs e)
{
_mainFrame.Navigate(seasonsPage);
}
private void ConstructorsButton_Click(object sender, RoutedEventArgs e)
{
_mainFrame.Navigate(constructorsPage);
}
}
When you override a ControlTemplate, you need to supply the entire template for the control. You are only supplying a Grid with a Border in it, so that is all that is going to render. If you look at the example template for Frame, you will see there is a lot more there. I suspect the important piece that you need to display the page is probably the content presenter named "PART_FrameCP". Try adding that to your template.
<ContentPresenter x:Name="PART_FrameCP" />
Named parts are usually important in templates because the control will look for them. Sometimes, unnamed parts are also searched for by type and therefore may be important as well. It is a good idea to read and understand the example template when creating a template of your own.

Categories

Resources