在自定义WPF控件初始化中附加到同级控制事件

时间:2015-05-15 15:48:25

标签: wpf wpf-controls

我正在尝试创建一个WPF自定义滑块控件,它充当Listview的滚动条。我这样做是通过将listview的名称放在我的自定义滑块的Tag属性中,然后使用滑块的OnValueChange事件来滚动列表视图。这很好用,但是当我使用鼠标滚轮滚动列表视图时,滑块不会移动。我需要的是一种在我的自定义滑块初始化时将方法附加到listview的MouseWheel事件的方法。这是我尝试过的:

自定义滑块类:

public class LabeledScrollbar : Slider
{
    public override void EndInit()
    {
        var listbox = (ListBox)this.FindName(this.Tag.ToString());
        if (listbox != null)
        {
            listbox.MouseWheel += new MouseWheelEventHandler(this.OnMouseWheel);
        }
        base.EndInit();
    }

    protected void OnMouseWheel(object sender, MouseWheelEventArgs e)
    {
        this.Value += 5;
    }

    protected override void OnValueChanged(double oldValue, double newValue)
    {
        var listBox = (ListBox)this.FindName(this.Tag.ToString());
        var collection = (CollectionView)CollectionViewSource.GetDefaultView(listBox.ItemsSource);

        if (newValue == this.Maximum)
        {
            if (VisualTreeHelper.GetChildrenCount(listBox) > 0)
            {
                var chrome = VisualTreeHelper.GetChild(listBox, 0);
                var scrollView = (ScrollViewer)VisualTreeHelper.GetChild(chrome, 0);
                scrollView.ScrollToTop();
            }
        }
        else
        {
            var index = (collection.Count - 1) - (int)Math.Floor(newValue);
            var selectedItem = collection.GetItemAt(index);
            listBox.ScrollIntoView(selectedItem);
        }
    }
}

XAML:

<ListView x:Name="listViewCategories"> 
    ... 
</ListView>
<local:LabeledScrollbar x:Name="categoryScrollbar" Orientation="Vertical" TickPlacement="BottomRight" Tag="listViewCategories"></local:LabeledScrollbar>

虽然我在列表视图中滚动时似乎会触发OnMouseWheel,但它没有发生,我无法找到任何其他尝试。有没有办法在WPF中做我想做的事情?我知道我可以在我的视图后面的代码中放置一个方法来使listview的MouseScroll事件移动滑块,但我希望尽可能多地封装滑块类中滑块的逻辑。

1 个答案:

答案 0 :(得分:0)

所以似乎诀窍是使用PreviewMouseWheel而不是MouseWheel。以下是我目前的课程:

/// <summary>
/// This class creates a custom control that can be used as a scrollbar for a listbox that displays the current
/// group visible in the listbox on a small label next to the slider thumb.
/// 
/// To use it, set the Tag value to the name of the listbox the scollbar will be controlling.
/// </summary>
public class LabeledScrollbar : Slider
{
    //Tracks control initialization to ensure it only gets loaded once
    private bool initialized = false;

    //The listview this slider will control
    private ListView listView;

    public LabeledScrollbar(): base()
    {
        this.Loaded += LabeledScrollbar_Loaded;
    }

    void LabeledScrollbar_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
        //The tag must be set to the name of a listbox
        listView = (ListView)this.FindName(this.Tag.ToString());

        if (listView != null && !this.initialized)
        {
            //Make sure that the mouse wheel event in the linked listbox is handled
            listView.PreviewMouseWheel += (s, ev) =>
            {
                if (ev.Delta > 0)
                    this.Value += 3;
                else
                    this.Value -= 3;
            };

            //Move scrollbar and list to the top if the collection changes
            ((INotifyCollectionChanged)listView.Items).CollectionChanged += (s, ev) =>
            {
                this.Maximum = ((ItemCollection)listView.Items).Count - 1;
                this.Value = this.Maximum;
            };

            //Get the max value of the slider by checking the tag value and looking up the associated listbox
            this.Maximum = ((ItemCollection)listView.Items).Count - 1;
            this.Value = this.Maximum;
            this.initialized = true;
        }
    }

    protected override void OnValueChanged(double oldValue, double newValue)
    {
        //Refresh the tickbar so that it will render for a new value
        InvalidateTickbar();

        //Scroll the list box to the correct location
        ScrollToIndex(newValue);
    }

    private void ScrollToIndex(double newValue)
    {
        if (newValue == this.Maximum)
        {
            //ScrollIntoView method does not scroll to the top so 
            //we need to access the scrollview to move the slider to the top
            if (VisualTreeHelper.GetChildrenCount(listView) > 0)
            {
                var chrome = VisualTreeHelper.GetChild(listView, 0);
                var scrollView = (ScrollViewer)VisualTreeHelper.GetChild(chrome, 0);
                scrollView.ScrollToTop();
            }
        }
        else
        {
            var collection = (CollectionView)CollectionViewSource.GetDefaultView(listView.ItemsSource);
            var index = (collection.Count - 1) - (int)Math.Floor(newValue);
            var selectedItem = collection.GetItemAt(index);
            listView.ScrollIntoView(selectedItem);
        }
    }

    private void InvalidateTickbar()
    {
        //update the tickbar for the new position
        if (VisualTreeHelper.GetChildrenCount(this) > 0)
        {
            var firstChild = VisualTreeHelper.GetChild(this, 0);
            if (VisualTreeHelper.GetChildrenCount(firstChild) > 0)
            {
                var secondChild = (CustomTickBar)VisualTreeHelper.GetChild(firstChild, 0);
                secondChild.InvalidateVisual();
            }
        }
    }
}