• 列表框禁用的项目


    Demo project in action. 介绍 最近,我需要创建一个应用程序,其中某些特性是打开还是关闭取决于客户。其中一组特性包含在一对列表框中,但我找不到任何方法禁用列表中的某些项。我决定创建自己的类,它派生于ListBox,这是我最后的结果。 现在与数据绑定!它实际上没有我想象的那么复杂。下面的更多信息。 使用的代码 DisableListBox包含一个布尔值列表listenable,它定义了启用和禁用哪些项(项中的)。listenable [i]为真,表示项目[i]被启用。我提供了用于添加/插入/删除/移除和清除项的函数,因此您不必担心保持两个列表同步。 隐藏,复制Code

    public void AddItem(object item, bool enabled);
    
    public void InsertItem(int index, object item, bool enabled);
    
    public void RemoveItem(object item);
    
    public void RemoveItemAt(int index);
    
    public void ClearItems();

    现在,如果你想启用或禁用项目后,添加他们,我创建了几个简单的方法: 隐藏,复制Code

    public void EnableItem(object item);
    
    public void EnableItemAt(int index);
    
    public void DisableItem(object item);
    
    public void DisableItemAt(int index);

    我还公开了itemenable,以防您自己想搞乱Items和itemenable列表。 现在是有趣的部分。版本1.1允许数据绑定。控件公开一个属性EnableMember,它的工作方式与DisplayMember和ValueMember类似。EnableMember将获取属性中的值,并使用它来确定是否启用了该项。它使用转换。ToBoolean,这样它可以处理数值类型和字符串。如果在数据绑定时没有设置EnableMember,那么所有项都将默认为enabled。这是代码运行时,EnableMember或数据源改变: 隐藏,收缩,复制Code

    private void RefreshItemEnables()
    {
        // Get enable property
        GetEnableProperty();
    
        // Clear enable list
        ItemEnables.Clear();
    
        // Fill enable list
        for (int i = 0; i < Items.Count; i++)
            ItemEnables.Add(ProduceEnable(i));
    
        // Ensure disabled items are not selected
        for (int i = SelectedItems.Count - 1; i >= 0; i--)
            if (!ItemEnables[SelectedIndices[i]])
                SelectedItems.Remove(SelectedItems[i]);
    }
    
    private void GetEnableProperty()
    {
        // If it should be bound to a property
        if (DataSource != null && EnableMember != string.Empty)
        {
            // Clear property
            enableProperty = null;
    
            // Find property
            foreach (PropertyDescriptor property in DataManager.GetItemProperties())
                if (property.Name == enableMember)
                    enableProperty = property;
        }
    }
    
    private bool ProduceEnable(int i)
    {
        // If databound and enable property is set
        if (DataSource != null && enableProperty != null)
            try
            {
                // Convert property to boolean
                return Convert.ToBoolean(enableProperty.GetValue(Items[i]));
            }
            // Object couldn't be converted to boolean
            catch (InvalidCastException)
            {
                return false;
            }
        else
            return true;
    }

    我还必须处理数据源中的项发生变化时,这是通过注册一些ListBox的DataManager(实际上是一个CurrencyManager)事件来完成的。 隐藏,复制Code

    void DataManager_ListChanged(object sender, ListChangedEventArgs e)
    {
        switch (e.ListChangedType)
        {
            // Handle items being added
            case ListChangedType.ItemAdded:
                ItemEnables.Insert(e.NewIndex, ProduceEnable(e.NewIndex));
                break;
            // Handle items being deleted
            case ListChangedType.ItemDeleted:
                ItemEnables.RemoveAt(e.NewIndex);
                break;
        }
    }
    
    void DataManager_ItemChanged(object sender, ItemChangedEventArgs e)
    {
        // Handle items changing
        if (e.Index > -1)
            SetEnabledAt(e.Index, ProduceEnable(e.Index));
    }

    很多工作都是为了确保残障物品不能被选中。为此,我不得不重写WndProc和捕捉以下消息: 隐藏,复制Code

    // Page Up/Down
    private const int VK_PRIOR = 0x21;
    private const int VK_NEXT = 0x22;
    
    // End/Home
    private const int VK_END = 0x23;
    private const int VK_HOME = 0x24;
    
    // Arrow keys
    private const int VK_LEFT = 0x25;
    private const int VK_UP = 0x26;
    private const int VK_RIGHT = 0x27;
    private const int VK_DOWN = 0x28;
    
    private const int WM_KEYDOWN = 0x100;
    private const int WM_MOUSEMOVE = 0x200;
    private const int WM_LBUTTONDOWN = 0x201;

    首先我处理了鼠标选择: 隐藏,复制Code

    // Intercept mouse selection
    if (m.Msg == WM_MOUSEMOVE || m.Msg == WM_LBUTTONDOWN)
    {
        // Get mouse location
        Point clickedPt = new Point();
        clickedPt.X = lParam & 0x0000FFFF;
        clickedPt.Y = lParam >> 16;
    
        // If point is on a disabled item, ignore mouse
        for (int i = 0; i < Items.Count; i++)
            if (!ItemEnables[i] && GetItemRectangle(i).Contains(clickedPt))
                return;
    }

    然后键盘选择(为了简洁,我只显示了一半的代码): 隐藏,收缩,复制Code

    // Intercept keyboard selection
    if (m.Msg == WM_KEYDOWN)
        // Handle single down
        if (wParam == VK_DOWN || wParam == VK_RIGHT)
        {
            // Select next enabled item
            for (int i = SelectedIndex + 1; i < Items.Count; i++)
                if (ItemEnables[i])
                {
                    SelectedIndex = i;
                    break;
                }
    
            return;
        }
        // Handle single up
        else if (wParam == VK_UP || wParam == VK_LEFT)
        {
            ...
        }
        // Handle page up
        else if (wParam == VK_PRIOR)
        {
            // Ignore if empty
            if (ItemEnables.Count == 0)
                return;
    
            // Get current selected index
            int currentIndex = Math.Max(0, SelectedIndex);
    
            // Get number of items to jump
            int toJump = NumVisibleItems() - 1;
    
            // Check if there are enough items to jump a full page
            if (currentIndex >= toJump)
            {
                // Jump at least a full page if possible
                for (int i = currentIndex - toJump; i >= 0; i--)
                    if (ItemEnables[i])
                    {
                        SelectedIndex = i;
                        return;
                    }
            }
            // If there aren't enough items, try to jump as far as possible
            else
                toJump = currentIndex;
    
            // Jump as far as possible without ending on a disabled item
            for (int i = currentIndex - toJump; i <= currentIndex; i++)
                if (ItemEnables[i])
                {
                    SelectedIndex = i;
                    break;
                }
    
            return;
        }
        // Handle page down
        else if (wParam == VK_NEXT)
        {
            ...
        }
        // Handle end
        else if (wParam == VK_END)
        {
            // Select closest enabled item to end
            for (int i = ItemEnables.Count - 1; i >= 0; i--)
                if (ItemEnables[i])
                {
                    SelectedIndex = i;
                    break;
                }
    
            return;
        }
        // Handle home
        else if (wParam == VK_HOME)
        {
            ...
        }

    最后的任务是处理被禁用物品的绘制。这是通过在第一次设置DrawMode = DrawMode. ownerdrawfixed后重写OnDrawItem来完成的;在构造函数中。我决定公开两个属性EnabledItemColor和DisabledItemColor来设置项目的文本颜色。它们在构造函数中分别默认为黑色和灰色。我还添加了一些代码来处理设置的right ttoleft,并确保它完全像一个列表框一样显示。 隐藏,收缩,复制Code

    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        // Stops control from throwing errors if empty or in design mode
        if (e.Index > -1 && !suspendDraw && !IsDesignMode())
        {
            // Draw the background
            e.DrawBackground();
    
            // Select color to use
            Color color;
            if (Enabled && ItemEnables[e.Index])
                if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                    color = Color.White;
                else
                    color = EnabledItemColor;
            else
                color = DisabledItemColor;
    
            // Align text
            Rectangle shiftedBounds;
            TextFormatFlags alignment;
            if (base.RightToLeft == RightToLeft.No)
            {
                // To look the same as ListBox, the bounds have to be shifted
                shiftedBounds = new Rectangle(e.Bounds.X - 1, e.Bounds.Y, e.Bounds.Width,
                    e.Bounds.Height);
                alignment = TextFormatFlags.Left;
            }
            else
            {
                // To look the same as ListBox, the bounds have to be shifted
                shiftedBounds = new Rectangle(e.Bounds.X + 2, e.Bounds.Y, e.Bounds.Width,
                    e.Bounds.Height);
                alignment = TextFormatFlags.Right;
            }
    
            // Get string to display
            string displayString = GetItemText(Items[e.Index]);
    
            // Draw the string
            TextRenderer.DrawText(e.Graphics, displayString, e.Font, shiftedBounds, color,
                alignment);
    
            // Draw the focus rectangle
            e.DrawFocusRectangle();
        }
    
        // Call base OnDrawItem
        base.OnDrawItem(e);
    }

    的兴趣点 我很高兴地尝试让DisableListBox的名称显示在设计模式中,就像ListBox那样。基本上,这需要在设计器中将DrawMode设置为Normal,而在其他地方设置为OwnerDrawFixed。下面是所有处理这个的代码: 隐藏,收缩,复制Code

    // Set to normal so name shows up in design mode
    private DrawMode drawMode = DrawMode.Normal;
    
    /// <summary>
    /// Gets or sets the drawing mode for the control.
    /// </summary>
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public override DrawMode DrawMode
    {
        get { return drawMode; }
        set
        {
            drawMode = value;
    
            // Keeps base.DrawMode set to Normal so name shows up in the designer
            if (!IsDesignMode()) base.DrawMode = value;
        }
    }
    
    /// <summary>
    /// Initializes a new instance of the DisableListBox class.
    /// </summary>
    public DisableListBox()
    {
        DrawMode = DrawMode.OwnerDrawFixed;
    }
    
    private bool IsDesignMode()
    {
        return DesignMode || LicenseManager.UsageMode == LicenseUsageMode.Designtime;
    }

    IsDesignMode()方法之所以存在,是因为设计模式不喜欢一直工作。你可以在这里了解更多。 特别感谢MSDN论坛上的Hans Passant (nobugz)和Nishant Sivakumar帮助我解决了这个问题。 已知的缺陷(s) 在绑定数据时更改EnableMember将导致禁用已启用的项。如果先前启用的项是选中的项,则将取消选中。不幸的是,CurrencyManager(处理数据绑定)没有“未选中”的位置。如果数据源在您选择其他内容之前发生了更改,则列表将刷新,禁用的项现在将被选中。 我不知道如何解决这个问题,因为总是有这样的情况,所有的项目都是禁用的,所以我不能只是设置它为另一个项目,而不是删除选择。此外,在这种情况下,onselectedindexchange事件甚至不会在项目被重新选择时触发。我不知道如何解决这个问题,所以欢迎任何建议。 另一个“bug”是绘制模式、选择模式和排序属性被隐藏了。DrawMode一直隐藏,因为改变正常不显示禁用物品,如果你要改变它OwnerDrawVariable你将不得不改变OnDrawItem代码无论如何,所以在这一点上你可以取消隐藏它如果你希望能够OwnerDrawFixed和OwnerDrawVariable之间切换。 排序没有实现,因为要保持两个列表(Items和itemenable)同步,我必须在类中编写自定义排序,这似乎有点浪费。另外,我怀疑没有人会经常使用它。如果有人想看到它添加,只要评论,我们会看到。 选择模式是隐藏的,因为多选择选项真的把事情搞砸了。我不知道如何处理在中间有禁用项目时的shift-click,所以像排序一样,我不会实现这个除非我得到它的请求。 历史 2009年7月29日 更新为v1.2Added EnableMemberError火灾事件如果EnableMember值不能转换为布尔值,给予例外和itemBug修复的索引:添加空构造函数抛出一个exceptionBug修复后数据源:更新绑定数据源抛出一个exceptionBug疗法:页面上/下和家庭/结束会让您选择禁用itemsBug预防:不允许改变SelectionMode和排序,直到实现这些功能 2009年7月17日, 更新到1.1,增加了数据绑定和更好的绘图 2009年7月10日, 提交文章 本文转载于:http://www.diyabc.com/frontweb/news372.html

  • 相关阅读:
    电力项目七--js控制文字内容过长的显示和文本字数的显示
    电力项目六--运行监控设计
    Maven开发系统
    SSH电力项目一 搭建Hibernate框架
    网站添加用户风险测评
    tomcat中文配置
    Tomcat映射路径的配置方法
    java中计算时间差
    NSTimer+倒计时功能实现
    设置UITextView光标从起始位置开始
  • 原文地址:https://www.cnblogs.com/Dincat/p/13443912.html
Copyright © 2020-2023  润新知