zoukankan      html  css  js  c++  java
  • [C1] C1ComboBox 的非编辑状态优化

    一、前言

    先看看 WPF 自带的 ComboBox 在非编辑状态,自定义 ItemTemplate 的情况下,效果如下图所示:

    image

    其当前选中的项(红框内)与自定义的 ItemTemplate 一样;

    但是 C1ComboBox 的非编辑状态(IsEditable="False"):

    image

    总感觉它的非编辑状态并没有完成,虽然数字和英文无法输入,但在红框内依旧可以输入中文文本(QQ拼音输入法的中文输入状态)失望;并且在非编辑状态下并非像 Combobox 的非编辑状态可以显示自定义的 ItemTemplate 效果;这篇文章就介绍如何使用 C1ComboBox 模仿 ComboBox 的非编辑状态效果。

    二、解决方案

    先分析 C1ComboBox 的控件结构:

    QQ截图20151018163755

    其中 ComboHeader 部分是由两个控件来回切换显示的,

    internal C1TextBoxBase _elementEditControl;
    internal ContentPresenter _elementContentControl;

    _elementEditControl 则是编辑状态下显示的控件,_elementContentControl 则是非编辑状态下显示的控件(可显示自定义的 ItemTemplate);

    而这两个控件转换显示的方法如下(C1TextEditableContentControl):

      1 protected internal void UpdateVisualState()
      2 {
      3     if (this.EditControl == null || this.ContentControl == null)
      4     {
      5         return;
      6     }
      7     if (this.IsInEditMode || this.IsDropDownOpen)
      8     {
      9         this.EditControl.Opacity = 1.0;
     10         this.EditControl.IsTabStop = true;
     11         this.EditControl.IsHitTestVisible = true;
     12         this.ContentControl.Visibility = Visibility.Collapsed;
     13         this.ContentControl.Content = null;
     14     }
     15     else
     16     {
     17         this.EditControl.Opacity = 1.4012984643248171E-45;
     18         this.EditControl.IsTabStop = false;
     19         this.EditControl.IsHitTestVisible = false;
     20         this.ContentControl.Visibility = Visibility.Visible;
     21         this.ContentControl.Content = this.ActualContent;
     22     }
     23     base.Cursor = (this.IsEditable ? Cursors.IBeam : Cursors.Arrow);
     24 }
     25 // 注:EditControl对应_elementEditControl,ContentControl对应_elementContentControl;

    即,当 this.IsInEditMode || this.IsDropDownOpen 为 false 时,方可显示自定义的 Itemplate ;

    所以,当 ScrollViewer 收缩时(IsDropDownOpen 为 false),设置 ComboHeader 的 IsInEditMode 为 false, 即可保证下拉选择项后,在 ComboHeader 显示自定义的 ItemTemplate;

    Loaded += (sender, e) =>
    {
        C1TextEditableContentControl editBox = GetChildObjects<C1TextEditableContentControl>(cmb, typeof(C1TextEditableContentControl))[0];
        cmb.IsDropDownOpenChanged += (sender2, e2) =>
        {
            editBox.IsInEditMode = false;
        };
    };
      1 public List<T> GetChildObjects<T>(DependencyObject obj, Type typename) where T : FrameworkElement
      2 {
      3     DependencyObject child = null;
      4     List<T> childList = new List<T>();
      5 
      6     for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)
      7     {
      8         child = VisualTreeHelper.GetChild(obj, i);
      9 
     10         if (child is T && (((T)child).GetType() == typename))
     11         {
     12             childList.Add((T)child);
     13         }
     14         childList.AddRange(GetChildObjects<T>(child, typename));
     15     }
     16     return childList;
     17 }
    
    GetChildObjects方法

    但是,当 ComboHeader 获取焦点,仍然会显示 EditControl ,而不是 ContentControl,所以添加如下代码:

    editBox.IsEditable = false;
    editBox.GotFocus += (sender2, e2) =>
    {
        editBox.IsInEditMode = false;
    };

    需要注意的是,如果不设置 editBox.IsEditable = false; ,点击两次 ComboHeader 还是会进入编辑状态,显示 EditControl 的……

    继续但是,当点击 ArrowToggle 按钮展开 ScrollViewer 时,仍旧会显示编辑状态,这个就麻烦了,查看源码可知,该操作时会触发 UpdateSwappedOut 方法以修改 _elementComboHeader 的 ActualContent ,进而触发上面的 UpdateVisualState 方法,而此时 ScrollViewer 的 IsDropDownOpen 属性为 true,导致显示编辑状态,而非自定义的 ItemTemplate;

      1 private void UpdateSwappedOut()
      2 {
      3     if (this._elementComboHeader == null)
      4     {
      5         return;
      6     }
      7     this._elementComboHeader.IsDropDownOpen = this.IsDropDownOpen;
      8     C1ComboBoxItem c1ComboBoxItem = null;
      9     this._isHeaderUpdate = true;
     10     this._elementComboHeader.ActualContent = null;
     11     this._isHeaderUpdate = false;
     12     this._elementComboHeader.UpdateIsWatermarked();
     13     this._elementComboHeader.UpdateVisualState();
     14     if (this.SwappedOutItem != null)
     15     {
     16         this.SwappedOutItem.SwappedOut = false;
     17         this.SwappedOutItem = null;
     18     }
     19     if (this.SelectedItem == null)
     20     {
     21         return;
     22     }
     23     if (this.SelectedIndex != -1)
     24     {
     25         c1ComboBoxItem = (C1ComboBoxItem)base.ItemContainerGenerator.ContainerFromIndex(this.SelectedIndex);
     26         if (c1ComboBoxItem != null && !this.IsDropDownOpen)
     27         {
     28             this.SwappedOutItem = c1ComboBoxItem;
     29             c1ComboBoxItem.SwappedOut = true;
     30         }
     31     }
     32     if (c1ComboBoxItem == null)
     33     {
     34         c1ComboBoxItem = (this.SelectedItem as C1ComboBoxItem);
     35     }
     36     this._isHeaderUpdate = true;
     37     if (c1ComboBoxItem == null)
     38     {
     39         if (base.ItemStringFormat != null && !this.IsEditable && base.ItemTemplate == null)
     40         {
     41             this._elementComboHeader.ActualContent = this.FormattedString(base.ItemStringFormat, this.SelectedItem);
     42         }
     43         else
     44         {
     45             this._elementComboHeader.ActualContent = this.SelectedItem;
     46         }
     47     }
     48     else if (base.ItemStringFormat != null && !this.IsEditable && base.ItemTemplate == null)
     49     {
     50         this._elementComboHeader.ActualContent = this.FormattedString(base.ItemStringFormat, c1ComboBoxItem.Content);
     51     }
     52     else
     53     {
     54         this._elementComboHeader.ActualContent = c1ComboBoxItem.Content;
     55     }
     56     this._isHeaderUpdate = false;
     57     this._elementComboHeader.IsDirty = false;
     58 }
    
    View Code

    没辙,没找到如何禁止触发 UpdateSwappedOut 方法,或者触发后如何设置 IsDropDownOpen 为 false,所以就把 EditControl 和 ContentControl 两个控件拿出来,再自己写一个 UpdateVisualState 来更新两个状态的转换;

      1 private void UpdateVisualState()
      2 {
      3     if (this.EditControl == null || this.ContentControl == null)
      4     {
      5         return;
      6     }
      7     this.EditControl.Opacity = 1.4012984643248171E-45;
      8     this.EditControl.IsTabStop = false;
      9     this.EditControl.IsHitTestVisible = false;
     10     this.ContentControl.Visibility = Visibility.Visible;
     11     C1ComboBoxItem cmbi = ((C1ComboBoxItem)cmb.ItemContainerGenerator.ContainerFromIndex(cmb.SelectedIndex));
     12     this.ContentControl.Content = cmbi.Content;
     13     base.Cursor = (EditBox.IsEditable ? Cursors.IBeam : Cursors.Arrow);
     14 }
      1 EditControl = GetChildObjects<Control>(cmb, "EditControl")[0];
      2 ContentControl = GetChildObjects<ContentPresenter>(cmb, "ContentControl")[0];
      3 cmb.IsDropDownOpenChanged += (sender2, e2) =>
      4 {
      5     // EditBox.IsInEditMode = false;
      6     UpdateVisualState();
      7 };
      1 public List<T> GetChildObjects<T>(DependencyObject obj, string name) where T : FrameworkElement
      2 {
      3     DependencyObject child = null;
      4     List<T> childList = new List<T>();
      5 
      6     for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)
      7     {
      8         child = VisualTreeHelper.GetChild(obj, i);
      9 
     10         if (child is T && (((T)child).Name == name | string.IsNullOrEmpty(name)))
     11         {
     12             childList.Add((T)child);
     13         }
     14         childList.AddRange(GetChildObjects<T>(child, name));
     15     }
     16     return childList;
     17 }

    一切Over,效果如下:

    初始状态:image

    展开状态:

    image

    选择项改变后状态:

    image

  • 相关阅读:
    laravel 服务提供者
    乐观锁和悲观锁
    MySQL索引原理及慢查询优化
    Laravel Session保存机制和terminate中间件
    laravel session踩坑
    理解 JavaScript 的 async/await(转)
    知识点
    js异步
    Office使用笔记
    YUM常用命令
  • 原文地址:https://www.cnblogs.com/memento/p/4889923.html
Copyright © 2011-2022 走看看