Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wpf disable repeatbuttons when scrolled to top/bottom

I'm making a touchscreen interface that uses a listbox.
I have a button above and below the listbox for page up/down.

I'm trying to get it to where when scrolled all the way up the pageup button gets disabled.
and when scrolled all the way down the pagedown button gets disabled too.

Here's the code in my Styles.xaml for the Listbox

<Style x:Key="{x:Type ListBox}" TargetType="{x:Type ListBox}">  
    <Setter Property="Template">  
        <Setter.Value>  
            <ControlTemplate x:Key="{x:Type ListBox}" TargetType="{x:Type ListBox}">  
                <DockPanel>  
                    <RepeatButton x:Name="LineUpButton" DockPanel.Dock="Top"  
                        HorizontalAlignment="Stretch"   
                        Height="50"  
                        Content="/\"  
                        Command="{x:Static ScrollBar.PageUpCommand}"  
                        CommandTarget="{Binding ElementName=scrollviewer}" />    
                    <RepeatButton x:Name="LineDownButton" DockPanel.Dock="Bottom"  
                        HorizontalAlignment="Stretch"  
                        Height="50"  
                        Content="\/"  
                        Command="{x:Static ScrollBar.PageDownCommand}"  
                        CommandTarget="{Binding ElementName=scrollviewer}" />  
                    <Border BorderThickness="1" BorderBrush="Gray" Background="White">    
                        <ScrollViewer x:Name="scrollviewer">  
                            <ItemsPresenter/>  
                        </ScrollViewer>  
                    </Border>  
                </DockPanel>  
            </ControlTemplate>  
        </Setter.Value>  
    </Setter>  
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/>  
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden"/>  
    <Setter Property="FocusVisualStyle" Value="{x:Null}" />  
</Style> 

And here's where I instantiate the listbox

<ListBox SelectedItem="{Binding SelectedCan}" ItemsSource="{Binding Path=SelectedKioskCashCans}">  
    <ListBox.ItemTemplate>  
        <DataTemplate>  
            <ContentPresenter Content="{Binding image}" MaxWidth="75" />  
        </DataTemplate>  
     </ListBox.ItemTemplate>  
     <ListBox.ItemsPanel>  
         <ItemsPanelTemplate>  
             <VirtualizingStackPanel Orientation="Vertical"/>  
         </ItemsPanelTemplate>  
     </ListBox.ItemsPanel>  
</ListBox> 

I searched all around yesterday with no luck.
I'm hoping to be able to do it all in xaml.

I'm using images for the buttons but took them out for readability above,
they really look like...

<RepeatButton x:Name="LineUpButton" DockPanel.Dock="Top" HorizontalAlignment="Stretch" 
    Height="50"      
    Command="{x:Static ScrollBar.PageUpCommand}"      
    CommandTarget="{Binding ElementName=scrollviewer}">
        <RepeatButton.Template>
             <ControlTemplate TargetType="{x:Type RepeatButton}">
                 <Grid>
                     <Image Name="Normal" Source="/Images/up.png"/>
                     <Image Name="Pressed" Source="/Images/up.png" Visibility="Hidden"/>
                 </Grid>
                 <ControlTemplate.Triggers>
                      <Trigger Property="IsPressed" Value="True">
                          <Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
                          <Setter TargetName="Pressed" Property="Visibility" Value="Visible"/>
                      </Trigger>
                  </ControlTemplate.Triggers>
             </ControlTemplate>
        </RepeatButton.Template>
   </RepeatButton>
like image 477
Charlie W. Avatar asked Oct 13 '22 17:10

Charlie W.


1 Answers

Just use CanExecute method of the PageUpCommand for that. Return false if where are no pages left and the button will become disabled automatically.

EDIT:

I have created a simple attached behavior that can be used to fix this problem. Just set the following attached property on the ScrollViewer:

<ScrollViewer x:Name="scrollviewer"
              z:ScrollBarCommandsCanExecuteFixBehavior.IsEnabled="True">  
     <ItemsPresenter/>  
</ScrollViewer> 

And here is the source code of the behavior:

public static class ScrollBarCommandsCanExecuteFixBehavior
{
    #region Nested Types

    public class CommandCanExecuteMonitor<T> where T : UIElement
    {
        protected T Target { get; private set; }

        protected CommandCanExecuteMonitor(T target, RoutedCommand command)
        {
            Target = target;

            var binding = new CommandBinding(command);

            binding.CanExecute += OnCanExecute;

            target.CommandBindings.Add(binding);
        }

        protected virtual void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {

        }
    }

    public class PageUpCanExecuteMonitor : CommandCanExecuteMonitor<ScrollViewer>
    {
        public PageUpCanExecuteMonitor(ScrollViewer scrollViewer)
            : base(scrollViewer, ScrollBar.PageUpCommand)
        {
        }

        protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (e.Handled)
            {
                return;
            }

            if (Equals(Target.VerticalOffset, 0.0))
            {
                e.CanExecute = false;
                e.Handled = true;
            }
        }
    }

    public class PageDownCanExecuteMonitor : CommandCanExecuteMonitor<ScrollViewer>
    {
        public PageDownCanExecuteMonitor(ScrollViewer scrollViewer)
            : base(scrollViewer, ScrollBar.PageDownCommand)
        {
        }

        protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (e.Handled)
            {
                return;
            }

            if (Equals(Target.VerticalOffset, Target.ScrollableHeight))
            {
                e.CanExecute = false;
                e.Handled = true;
            }
        }
    }

    #endregion

    #region IsEnabled Attached Property

    public static bool GetIsEnabled(DependencyObject obj)
    {
        return (bool) obj.GetValue(IsEnabledProperty);
    }

    public static void SetIsEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(IsEnabledProperty, value);
    }

    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.RegisterAttached("IsEnabled", typeof (bool), typeof (ScrollBarCommandsCanExecuteFixBehavior), new PropertyMetadata(false, OnIsEnabledChanged));

    private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if ((bool) e.NewValue)
        {
            var scrollViewer = d as ScrollViewer;

            if (scrollViewer != null)
            {
                OnAttached(scrollViewer);
            }
            else
            {
                throw new NotSupportedException("This behavior only supports ScrollViewer instances.");
            }
        }
    }

    private static void OnAttached(ScrollViewer target)
    {
        SetPageUpCanExecuteMonitor(target, new PageUpCanExecuteMonitor(target));
        SetPageDownCanExecuteMonitor(target, new PageDownCanExecuteMonitor(target));
    }

    #endregion

    #region PageUpCanExecuteMonitor Attached Property

    private static void SetPageUpCanExecuteMonitor(DependencyObject obj, PageUpCanExecuteMonitor value)
    {
        obj.SetValue(PageUpCanExecuteMonitorProperty, value);
    }

    private static readonly DependencyProperty PageUpCanExecuteMonitorProperty =
        DependencyProperty.RegisterAttached("PageUpCanExecuteMonitor", typeof (PageUpCanExecuteMonitor), typeof (ScrollBarCommandsCanExecuteFixBehavior), new PropertyMetadata(null));

    #endregion

    #region PageDownCanExecuteMonitor Attached Property

    private static void SetPageDownCanExecuteMonitor(DependencyObject obj, PageDownCanExecuteMonitor value)
    {
        obj.SetValue(PageDownCanExecuteMonitorProperty, value);
    }

    private static readonly DependencyProperty PageDownCanExecuteMonitorProperty =
        DependencyProperty.RegisterAttached("PageDownCanExecuteMonitor", typeof (PageDownCanExecuteMonitor), typeof (ScrollBarCommandsCanExecuteFixBehavior), new PropertyMetadata(null));

    #endregion
}

The basic idea is that we add a CommandBinding to the ScrollViewer for each of the commands and subscribe to the CanExecute event on those bindings. In the event handler we check the current position of the scroll and set the e.CanExecute property accordingly.

like image 137
Pavlo Glazkov Avatar answered Jan 17 '23 17:01

Pavlo Glazkov