先看看 WPF 自帶的 ComboBox 在非編輯狀態,自定義 ItemTemplate 的狀況下,效果以下圖所示:app
其當前選中的項(紅框內)與自定義的 ItemTemplate 同樣;ide
可是 C1ComboBox 的非編輯狀態(IsEditable="False"):this
總感受它的非編輯狀態並無完成,雖然數字和英文沒法輸入,但在紅框內依舊能夠輸入中文文本(QQ拼音輸入法的中文輸入狀態);而且在非編輯狀態下並不是像 Combobox 的非編輯狀態能夠顯示自定義的 ItemTemplate 效果;這篇文章就介紹如何使用 C1ComboBox 模仿 ComboBox 的非編輯狀態效果。spa
先分析 C1ComboBox 的控件結構:3d
其中 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 }
可是,當 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 }
沒轍,沒找到如何禁止觸發 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,效果以下:
展開狀態:
選擇項改變後狀態: