[C1] C1ComboBox 的非編輯狀態優化

1、前言

先看看 WPF 自帶的 ComboBox 在非編輯狀態,自定義 ItemTemplate 的狀況下,效果以下圖所示:app

image

其當前選中的項(紅框內)與自定義的 ItemTemplate 同樣;ide

可是 C1ComboBox 的非編輯狀態(IsEditable="False"):this

image

總感受它的非編輯狀態並無完成,雖然數字和英文沒法輸入,但在紅框內依舊能夠輸入中文文本(QQ拼音輸入法的中文輸入狀態)失望;而且在非編輯狀態下並不是像 Combobox 的非編輯狀態能夠顯示自定義的 ItemTemplate 效果;這篇文章就介紹如何使用 C1ComboBox 模仿 ComboBox 的非編輯狀態效果。spa

2、解決方案

先分析 C1ComboBox 的控件結構:3d

QQ截圖20151018163755

其中 ComboHeader 部分是由兩個控件來回切換顯示的,code

internal C1TextBoxBase _elementEditControl;
internal ContentPresenter _elementContentControl;

_elementEditControl 則是編輯狀態下顯示的控件,_elementContentControl 則是非編輯狀態下顯示的控件(可顯示自定義的 ItemTemplate);orm

而這兩個控件轉換顯示的方法以下(C1TextEditableContentControl):blog

  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 ;ci

因此,當 ScrollViewer 收縮時(IsDropDownOpen 爲 false),設置 ComboHeader 的 IsInEditMode 爲 false, 便可保證下拉選擇項後,在 ComboHeader 顯示自定義的 ItemTemplate;element

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

相關文章
相關標籤/搜索