首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何自动选择绑定的silverlight列表框中的第一项?

如何自动选择绑定的silverlight列表框中的第一项?
EN

Stack Overflow用户
提问于 2010-01-10 08:52:31
回答 3查看 2.1K关注 0票数 1

使用silverlight,我有一个列表框,它将ItemsSource绑定到一个异步更新的ObservableCollection上。我想在绑定更新完成后立即自动选择列表框中的第一项。

我找不到一个好的方法来实现这一点。我在列表框上看不到任何有用的事件来处理,如果我绑定到集合的CollectionChanged事件,绑定还没有更新,所以如果我在那个点设置listbox.selectedindex,我会得到一个值超出范围的异常。有什么想法吗?也许可以通过某种方式来挂接绑定更新?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-01-10 13:52:56

在ListBox上,将SelectedItem属性绑定到代码绑定或视图模型上的属性。然后,在异步回调处理程序中,将该属性设置为集合中的第一项,并引发该属性的PropertyChanged事件(除非已在属性的setter中引发该事件):

代码语言:javascript
运行
复制
MySelectedListItem = _entitylist.FirstOrDefault();
RasisePropertyChanged("MySelectedListItem");
票数 0
EN

Stack Overflow用户

发布于 2012-01-31 01:22:13

我花了很长时间在互联网上寻找这个问题的解决方案,基本上最终还是找到了解决方案。

您要做的是将列表框绑定到一个ICollectionView。然后确保您没有将IsSynchronizedWithCurrentItem设置为false。

不好,不会起作用

代码语言:javascript
运行
复制
IsSynchronizedWithCurrentItem="False"

这是Silverlight的默认设置,不要浪费时间将其键入

代码语言:javascript
运行
复制
IsSynchronizedWithCurrentItem="{x:Null}"

这将在运行时抛出一个错误,我相信这与{x:null}相同

代码语言:javascript
运行
复制
IsSynchronizedWithCurrentItem="True"

ICollectionView有一个名为MoveCurrentToFirst.的方法这个名称看起来有点模棱两可,但它确实将CurrentItem指针移动到了第一个项目(我最初认为它通过将您选择的任何项目移动到第一个项目位置来重新排序集合)。IsSynchronizedWithCurrentItem属性允许列表框(或任何实现选择器的控件)神奇地使用ICollectionViews。在您的代码中,您可以调用ICollectioView.CurrentItem而不是您绑定的Listbox.SelectedItem来获取当前选定的项。

下面是我如何使我的ICollectionView可用于我的视图(我正在使用MVVM):

代码语言:javascript
运行
复制
public System.ComponentModel.ICollectionView NonModifierPricesView
        {
            get
            {
                if (_NonModifierPricesView == null)
                {
                    _NonModifierPricesView = AutoRefreshCollectionViewSourceFactory.Create(x => ((MenuItemPrice)x).PriceType == MenuItemPrice.PriceTypes.NonModifier);
                    _NonModifierPricesView.Source = Prices;
                    _NonModifierPricesView.ApplyFilter(x => ((MenuItemPrice)x).DTO.Active == true);
                }
                ICollectionView v = _NonModifierPricesView.View;
                v.MoveCurrentToFirst();
                return v;
            }
        }

现在,由于要绑定到可观察的集合,因此不能使用默认CollectionViewSource,因为它不知道对源集合的更新。您可能已经注意到,我使用的是一个名为AutoRefreshCollectionViewSource.的自定义CVS实现如果我没记错的话,我在网上找到了代码,并对它进行了修改,以供我自己使用。我已经添加了额外的过滤功能,所以可以更进一步地清理这些类。

以下是我的代码版本:

AutoRefreshCollectionViewSource.cs

代码语言:javascript
运行
复制
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Data;

    public class AutoRefreshCollectionViewSource : System.Windows.Data.CollectionViewSource
    {

        // A delegate for launching a refresh of the view at a different priority.
        private delegate void NoArgDelegate();
        private Predicate<object> MyFilter; // this is the filter we put on when we do ApplyFilter
        private Predicate<object> BaseFilter; // this is the filter that is applied always

        public AutoRefreshCollectionViewSource(Predicate<object> _baseFilter) : base() 
        {
            BaseFilter = _baseFilter;

            if (BaseFilter == null)
                BaseFilter = x => true;

        }

        /// <summary>
        /// A collection containing all objects whose event handlers have been
        /// subscribed to.
        /// </summary>
        private List<INotifyPropertyChanged> colSubscribedItems = new List<INotifyPropertyChanged>();

        // We must override the OnSourceChanged event so that we can subscribe
        // to the objects in the new collection (and unsubscribe from the old items).

        protected override void OnSourceChanged(object oldSource, object newSource)
        {
            // Unsubscribe from the old source.
            if (oldSource != null)
                SubscribeSourceEvents(oldSource, true);

            // Subscribe to the new source.
            if (newSource != null)
                SubscribeSourceEvents(newSource, false);

            base.OnSourceChanged(oldSource, newSource);
        }

        /// <summary>
        /// Adds or Removes EventHandlers to each item in the source collection as well as the
        /// collection itself (if supported).
        /// </summary>
        /// <param name="source">The collection to (un)subscribe to and whose objects should be (un)subscribed.</param>
        /// <param name="remove">Whether or not to subscribe or unsubscribe.</param>
        private void SubscribeSourceEvents(object source, bool remove)
        {
            // Make sure the source is not nothing.
            // This may occur when setting up or tearing down this object.
            if (source != null)

                if (source is INotifyCollectionChanged)
                    // We are (un)subscribing to a specialized collection, it supports the INotifyCollectionChanged event.
                    // (Un)subscribe to the event.
                    if (remove)
                        ((INotifyCollectionChanged)source).CollectionChanged -= Handle_INotifyCollectionChanged;
                    else
                        ((INotifyCollectionChanged)source).CollectionChanged += Handle_INotifyCollectionChanged;

            if (remove)
                // We are unsubscribing so unsubscribe from each object in the collection.
                UnsubscribeAllItemEvents();
            else
                // We are subscribing so subscribe to each object in the collection.
                SubscribeItemsEvents((IEnumerable)source, false);

        }

        /// <summary>
        /// Unsubscribes the NotifyPropertyChanged events from all objects
        /// that have been subscribed to. 
        /// </summary>
        private void UnsubscribeAllItemEvents()
        {
            while (colSubscribedItems.Count > 0)
                SubscribeItemEvents(colSubscribedItems[0], true);
        }

        /// <summary>
        /// Subscribes or unsubscribes to the NotifyPropertyChanged event of all items
        /// in the supplied IEnumerable.
        /// </summary>
        /// <param name="items">The IEnumerable containing the items to (un)subscribe to/from.</param>
        /// <param name="remove">Whether or not to subscribe or unsubscribe.</param>
        private void SubscribeItemsEvents(IEnumerable items, bool remove)
        {
            foreach (object item in items)
                SubscribeItemEvents(item, remove);
        }

        /// <summary>
        /// Subscribes or unsubscribes to the NotifyPropertyChanged event if the supplied
        /// object supports it.
        /// </summary>
        /// <param name="item">The object to (un)subscribe to/from.</param>
        /// <param name="remove">Whether or not to subscribe or unsubscribe.</param>
        private void SubscribeItemEvents(object item, bool remove)
        {
            if (item is INotifyPropertyChanged)
                // We only subscribe of the object supports INotifyPropertyChanged.

                if (remove)
                {
                    // Unsubscribe.
                    ((INotifyPropertyChanged)item).PropertyChanged -= Item_PropertyChanged;
                    colSubscribedItems.Remove((INotifyPropertyChanged)item);
                }
                else
                {
                    // Subscribe.
                    ((INotifyPropertyChanged)item).PropertyChanged += Item_PropertyChanged;
                    colSubscribedItems.Add((INotifyPropertyChanged)item);
                }
        }

        /// <summary>
        /// Handles a property changed event from an item that supports INotifyPropertyChanged.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">The event arguments associated with the event.</param>
        /// <remarks></remarks>
        private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // By default, we do not need to refresh.
            bool refresh = false;

            if (e.PropertyName == "Active" || e.PropertyName == "DTO.Active")
                refresh = true;

            if (refresh)
                // Call the refresh.
                // Notice that the dispatcher will make the call to Refresh the view.  If the dispatcher is not used,
                // there is a possibility for a StackOverFlow to result.
                this.Dispatcher.BeginInvoke(new NoArgDelegate(this.View.Refresh), null);
        }

        /// <summary>
        /// Handles the INotifyCollectionChanged event if the subscribed source supports it.
        /// </summary>
        private void Handle_INotifyCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    SubscribeItemsEvents(e.NewItems, false);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    SubscribeItemsEvents(e.OldItems, true);
                    break;
                case NotifyCollectionChangedAction.Replace:
                    SubscribeItemsEvents(e.OldItems, true);
                    SubscribeItemsEvents(e.NewItems, false);
                    break;
                case NotifyCollectionChangedAction.Reset:
                    UnsubscribeAllItemEvents();
                    SubscribeItemsEvents((IEnumerable)sender, false);
                    break;
            }
        }

        public void ApplyFilter(Predicate<object> f)
        {
            if (f != null)
                MyFilter = f;

            this.View.Filter = x => MyFilter(x) && BaseFilter(x);
            this.View.Refresh();
        }

        public void RemoveFilter()
        {
            this.View.Filter = BaseFilter;
            this.View.Refresh();
}
}

AutoRefreshCollectionViewSourceFactory.cs

代码语言:javascript
运行
复制
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
    public class AutoRefreshCollectionViewSourceFactory
    {
        private static List<AutoRefreshCollectionViewSource> Collections;
        public static AutoRefreshCollectionViewSource Create()
        {
            if (Collections == null)
                Collections = new List<AutoRefreshCollectionViewSource>();
            AutoRefreshCollectionViewSource cvs = new AutoRefreshCollectionViewSource(null);
            Collections.Add(cvs);
            return cvs;
        }
        public static AutoRefreshCollectionViewSource Create(Predicate<object> p)
        {
            if (Collections == null)
                Collections = new List<AutoRefreshCollectionViewSource>();
            AutoRefreshCollectionViewSource cvs = new AutoRefreshCollectionViewSource(p);
            Collections.Add(cvs);
            return cvs;
        }
        public static void ApplyFilterOnCollections()
        {
            foreach (AutoRefreshCollectionViewSource cvs in Collections)
                cvs.ApplyFilter(null);
        }
        public static void RemoveFilterFromCollections()
        {
            foreach (AutoRefreshCollectionViewSource cvs in Collections)
                cvs.RemoveFilter();
        }
        public static void CleanUp()
        {
            Collections = null;
        }
}
票数 1
EN

Stack Overflow用户

发布于 2012-05-31 00:19:26

您可以做的另一件事是创建一个继承自ListBox的UserControl,通过覆盖OnItemsChanged方法公开一个ItemsChanged,并处理此事件并将列表框的SelectedIndex设置为0。

代码语言:javascript
运行
复制
public partial class MyListBox : ListBox
{
    public delegate void ItemsSourceChangedHandler(object sender, EventArgs e);

    #region Override
    protected override void OnItemsChanged(
        NotifyCollectionChangedEventArgs e)
    {
        base.OnItemsChanged(e);
        OnItemsChangedEvent(e);
    }
    #endregion Override

    #region Class Events

    public delegate void ItemsChangedEventHandler(object sender,
        NotifyCollectionChangedEventArgs e);
    public event ItemsChangedEventHandler ItemsChanged;

    private void OnItemsChangedEvent(
        NotifyCollectionChangedEventArgs e)
    {
        if (ItemsChanged != null)
        {
            ItemsChanged(this, e);
        }
    }

    #endregion Class Events

}

用户控件的XAML:

代码语言:javascript
运行
复制
<ListBox x:Class="CoverArtRefiner.MyListBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">

    </Grid>
</ListBox>

在屏幕上添加以下内容:

代码语言:javascript
运行
复制
xmlns:local="clr-namespace:<The Namespace which MyListBox is contained in>"

现在将自定义控件添加到您的window/usercontrol:

代码语言:javascript
运行
复制
<local:MyListBox ItemsChanged="listBox_ItemsChanged" Background="Black" />

最后处理事件:

代码语言:javascript
运行
复制
private void listBox_ItemsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    MyListBox listBox = (MyListBox)sender;
    if (listBox.Items.Count > 0)
        listBox.SelectedIndex = 0;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2035614

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档