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

Attach to Event of Sibling Control in Custom WPF Control Initialization

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

自定义滑块class:

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 中做我想做的事?我知道我可以在我的视图后面的代码中放置一个方法,使列表视图的 MouseScroll 事件移动滑块,但我希望尽可能多地将滑块的逻辑封装在滑块 class 中.

看来诀窍是使用 PreviewMouseWheel 而不是 MouseWheel。为了将来参考,这里是我当前的 class:

/// <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();
            }
        }
    }
}