Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
460 views
in Technique[技术] by (71.8m points)

c# - Highlight multiple items in listbox

Is there any way to achieve something like office undo drop down (image bellow) ? I mean, i want to highlight previous item when user mouse over item other than first ? I tried some control from FluentRibbon but so far without luck..

enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In most cases designing a control like this is done in Blend. However, if you don't know how to use Blend, you can still achieve the similar results with just XAML and the code-behind, but you have to do more work.

We start by creating a class called CustomListBoxItem which inherits from ListBoxItem. Then, we define a dependency property, which is used for highlighting items in the listbox:

public class CustomListBoxItem : ListBoxItem
{
    public static readonly DependencyProperty IsVirtuallySelectedProperty =
       DependencyProperty.Register("IsVirtuallySelected", typeof(bool), 
                                   typeof(CustomListBoxItem),
                                   new PropertyMetadata(false));

    public CustomListBoxItem() : base()
    { }

    public bool IsVirtuallySelected
    {
        get { return (bool)GetValue(IsVirtuallySelectedProperty); }
        set { SetValue(IsVirtuallySelectedProperty, value); }
    }
}

Then we add a listbox and define a style for it in XAML:

<ListBox Name="listBox" MouseMove="listBox_MouseMove" SelectionChanged="listBox_SelectionChanged">
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type local:CustomListBoxItem}">
                <Style.Triggers>
                    <Trigger Property="IsVirtuallySelected" Value="true">
                        <Setter Property="Background" Value="SkyBlue"/>
                    </Trigger>
                    <Trigger Property="IsVirtuallySelected" Value="false">
                        <Setter Property="Background" Value="White"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </ListBox.ItemContainerStyle>
</ListBox>

where local is the namespace in which CustomListBoxItem is defined. This is all we need for the XAML part, the real magic happens in the code behind.

The listBox_MouseMove event handler looks like this:

private void listBox_MouseMove(object sender, MouseEventArgs e)
    {
        bool itemFound = false;

        for (int i = 0; i < listBox.Items.Count; i++)
        {
            var currentItem = listBox.ItemContainerGenerator.ContainerFromIndex(i) as CustomListBoxItem;

            if (currentItem == null) 
                continue;

            // Check whether the cursor is on an item or not.
            if (IsMouseOverItem(currentItem, e.GetPosition((IInputElement)currentItem)))
            {
                // Unselect all items before selecting the new group
                listBox.Items.Cast<CustomListBoxItem>().ToList().ForEach(x => x.IsVirtuallySelected = false);

                // Select the current item and the ones above it
                for (int j = 0; j <= listBox.Items.IndexOf(currentItem); j++)
                {
                    ((CustomListBoxItem)listBox.Items[j]).IsVirtuallySelected = true; 
                }

                itemFound = true;
                break;
            }
        }

        // If the item wasn't found for the mouse point, it means the pointer is not over any item, so unselect all.
        if (!itemFound)
        {
            listBox.Items.Cast<CustomListBoxItem>().ToList().ForEach(x => x.IsVirtuallySelected = false);
        }
    }

And the IsMouseOverItem helper method, which is used to determine if the cursor is on an item, is defined like this:

 private bool IsMouseOverItem(Visual item, Point mouseOverPoint)
    {
        Rect currentDescendantBounds = VisualTreeHelper.GetDescendantBounds(item);
        return currentDescendantBounds.Contains(mouseOverPoint);
    }

And finally the listBox_SelectedChanged event handler which acts as the click handler for the ListBox:

 private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        // Get all the virtually selected items
        List<CustomListBoxItem> selectedItems =
            listBox.Items.Cast<CustomListBoxItem>().Where(x => x.IsVirtuallySelected).ToList();

        if (selectedItems == null || !selectedItems.Any())
            return;

        // Do something with the selected items
        DoCoolStuffWithSelectedItems();

        // Unselsect all.
        listBox.Items.Cast<CustomListBoxItem>().ToList().ForEach(x => x.IsVirtuallySelected = false);
        listBox.UnselectAll();
    }

And boom, we're done. We can now add some items to the ListBox in the class constructor:

 public MainWindow()
    {
        InitializeComponent();


        listBox.Items.Add(new CustomListBoxItem { Content = "hello world!" });
        listBox.Items.Add(new CustomListBoxItem { Content = "wpf is cool" });
        listBox.Items.Add(new CustomListBoxItem { Content = "today is tuesday..." });
        listBox.Items.Add(new CustomListBoxItem { Content = "I like coffee" });
    }

Note that I used a random color as the highlight color in XAML. Feel free to change it and give it a try.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...