代码改变世界

修改Aaron Zhou 的WPF ComboBox 与 MultiComboBox 有感

2013-12-23 16:26  gungrave  阅读(693)  评论(0编辑  收藏  举报

用VS2012 Profile 测试发现ComboBox 与 MultiComboBox的数据源OnDataSourceChanged事件中,this.Items.Add()方法被循环调用并且速度很慢,是界面加载的主要瓶颈

(居然不是访问服务!怒改!)

原来该对象的每一次变更都会触发OnCollectionChanged事件,并会通知前台界面重新渲染,速度很慢。改用中间变量再一次性赋值就可以了。

另外,在对MultiComboBox增加输入搜索功能时发现,自定义的依赖属性DataSource,尽管ViewModel中绑定的是ObservableCollection并用TwoWay的方式,只要对象是通过集合的Add/Remove方式改变,就不会获得PropertyChange通知。因为对象的引用并没有变化。

但默认的ComboBox的ItemSource是可以的。看了源码也终于理解了为什么ComboBox除了ItemSource为什么还要个Items对象。

原来ItemSource与数据源绑定后同时注册了OnCollectionChanged事件,该事件中会对Items对象重新赋值(一次性赋值),由于ItemSource与ViewModel的对象是同一对象,

所以不论是直接赋值还是Add/Remove,ItemSource都能捕获到ViewModel数据源的变化。

 

另一个改动是MultiComboBox的SelectedItems属性,需要其内部不论是直接赋值还是Add/Remove都要通知到ViewModel的数据源对象。由于不能控制ViewModel的编码,只能每次都重新赋值了

 

最后附上可搜索过滤的MultiCombox代码:

  1 using Microsoft.Practices.Prism.Commands;
  2 using Microsoft.Windows.Themes;
  3 using System;
  4 using System.Collections;
  5 using System.Collections.Generic;
  6 using System.Collections.ObjectModel;
  7 using System.Collections.Specialized;
  8 using System.ComponentModel;
  9 using System.Globalization;
 10 using System.Linq;
 11 using System.Text;
 12 using System.Threading.Tasks;
 13 using System.Windows;
 14 using System.Windows.Controls;
 15 using System.Windows.Data;
 16 using System.Windows.Input;
 17 
 18 namespace Common.Controls.CustomControls
 19 {
 20     public class CustomMultipleDropDownBox : ComboBox
 21     {
 22 
 23         #region UI Control Property
 24 
 25         protected ListBox MyListBox
 26         {
 27             get
 28             {
 29                 return base.GetTemplateChild("MyListBox") as ListBox;
 30             }
 31         }
 32 
 33         /// <summary>
 34         /// Gets the text box in charge of the editable portion of the combo box.
 35         /// </summary>
 36         protected TextBox EditableTextBox
 37         {
 38             get
 39             {
 40                 return base.GetTemplateChild("DisPlayTextBlock") as TextBox;
 41             }
 42         }
 43 
 44 
 45         private CheckBox SelectAllCheckBox { get; set; }
 46 
 47         #endregion
 48 
 49         #region Dependency Property
 50         private const string SelectAllItem = "Select All";
 51         private int TotalCount { get; set; }
 52         private List<ComboBoxNode> dataSourceNodeList { get; set; }
 53         public DelegateCommand<ComboBoxNode> CheckBoxSelectCommand { get; set; }
 54         public DelegateCommand ToggleButtonClickCommand { set; get; }
 55 
 56         #region DataSource
 57         public ObservableCollection<ComboBoxNode> NodeList
 58         {
 59             get { return (ObservableCollection<ComboBoxNode>)GetValue(NodeListProperty); }
 60             set { SetValue(NodeListProperty, value); }
 61         }
 62 
 63         public static readonly DependencyProperty NodeListProperty =
 64             DependencyProperty.Register("NodeList", typeof(ObservableCollection<ComboBoxNode>), typeof(CustomMultipleDropDownBox), new PropertyMetadata(null, null));
 65 
 66 
 67         #endregion
 68 
 69         #region SelectedItems && SelectedCount
 70 
 71         private IList m_selectedItemCollection;
 72         protected IList SelectedItemCollection
 73         {
 74             set
 75             {
 76                 m_selectedItemCollection = value;
 77                 if (value != null && m_selectedItemCollection is INotifyCollectionChanged)
 78                 {
 79                     ((INotifyCollectionChanged)m_selectedItemCollection).CollectionChanged
 80                 += new NotifyCollectionChangedEventHandler(OnSelectItemCollectionChanged);
 81                 }
 82             }
 83         }
 84         public IList SelectedItems
 85         {
 86             get { return (IList)GetValue(SelectedItemsProperty); }
 87             set { SetValue(SelectedItemsProperty, value); }
 88         }
 89 
 90         // Using a DependencyProperty as the backing store for SelectedItems.  This enables animation, styling, binding, etc...
 91         public static readonly DependencyProperty SelectedItemsProperty =
 92             DependencyProperty.Register("SelectedItems", typeof(IList), typeof(CustomMultipleDropDownBox), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedItemsChanged)));
 93 
 94         /// <summary>
 95         /// change selected count when selected items changed
 96         /// </summary>
 97         /// <param name="d"></param>
 98         /// <param name="e"></param>
 99         private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
100         {
101             CustomMultipleDropDownBox c = d as CustomMultipleDropDownBox;
102             c.SelectedItemCollection = c.SelectedItems;
103             if (c.SelectedItems != null && c.SelectedItems.Count == 0)
104                 c.SelectedCount = null;
105             else
106                 c.SelectedCount = c.SelectedItems.Count;
107 
108             c.ChangeNodeIsSelectedStatus();
109         }
110 
111         private void OnSelectItemCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
112         {
113             this.ChangeNodeIsSelectedStatus();
114         }
115 
116         /// <summary>
117         /// Used to inform IsMandatory trigger how many items has selected
118         /// </summary>
119         public int? SelectedCount
120         {
121             get { return (int?)GetValue(SelectedCountProperty); }
122             set { SetValue(SelectedCountProperty, value); }
123         }
124 
125         // Using a DependencyProperty as the backing store for SelectedCount.  This enables animation, styling, binding, etc...
126         public static readonly DependencyProperty SelectedCountProperty =
127             DependencyProperty.Register("SelectedCount", typeof(int?), typeof(CustomMultipleDropDownBox), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedCountChanged)));
128 
129         private static void OnSelectedCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
130         {
131             CustomMultipleDropDownBox c = d as CustomMultipleDropDownBox;
132 
133             if (e.NewValue != null && (int)e.NewValue == 0)
134             {
135                 c.SelectedCount = null;
136             }
137         }
138         #endregion
139 
140         #region SeparateString
141         public string SeparateString
142         {
143             get { return (string)GetValue(SeparateStringProperty); }
144             set { SetValue(SeparateStringProperty, value); }
145         }
146 
147         // Using a DependencyProperty as the backing store for SelectedItems.  This enables animation, styling, binding, etc...
148         public static readonly DependencyProperty SeparateStringProperty =
149             DependencyProperty.Register("SeparateString", typeof(string), typeof(CustomMultipleDropDownBox), new PropertyMetadata(";"));
150         #endregion
151 
152         #region ItemEnabledPath
153         public string ItemEnabledPath
154         {
155             get { return (string)GetValue(ItemEnabledPathProperty); }
156             set { SetValue(ItemEnabledPathProperty, value); }
157         }
158 
159         // Using a DependencyProperty as the backing store for DropDownHeight.  This enables animation, styling, binding, etc...
160         public static readonly DependencyProperty ItemEnabledPathProperty =
161             DependencyProperty.Register("ItemEnabledPath", typeof(string), typeof(CustomMultipleDropDownBox), new PropertyMetadata(null));
162         #endregion
163 
164         #region DropDown Height & Width
165         public double DropDownHeight
166         {
167             get { return (double)GetValue(DropDownHeightProperty); }
168             set { SetValue(DropDownHeightProperty, value); }
169         }
170 
171         // Using a DependencyProperty as the backing store for DropDownHeight.  This enables animation, styling, binding, etc...
172         public static readonly DependencyProperty DropDownHeightProperty =
173             DependencyProperty.Register("DropDownHeight", typeof(double), typeof(CustomMultipleDropDownBox), new PropertyMetadata(0.0));
174 
175 
176         public double DropDownWidth
177         {
178             get { return (double)GetValue(DropDownWidthProperty); }
179             set { SetValue(DropDownWidthProperty, value); }
180         }
181 
182         // Using a DependencyProperty as the backing store for DropDownWidth.  This enables animation, styling, binding, etc...
183         public static readonly DependencyProperty DropDownWidthProperty =
184             DependencyProperty.Register("DropDownWidth", typeof(double), typeof(CustomMultipleDropDownBox), new PropertyMetadata(0.0));
185         #endregion
186 
187         #region DisplayText and ToolTip
188         public string DisplayText
189         {
190             get { return (string)GetValue(DisplayTextProperty); }
191             set { SetValue(DisplayTextProperty, value); }
192         }
193 
194         // Using a DependencyProperty as the backing store for DisplayText.  This enables animation, styling, binding, etc...
195         public static readonly DependencyProperty DisplayTextProperty =
196             DependencyProperty.Register("DisplayText", typeof(string), typeof(CustomMultipleDropDownBox), new PropertyMetadata(null, new PropertyChangedCallback(OnDisplayTextChanged)));
197 
198         /// <summary>
199         /// To control tooltip's content and can displayed when this property is changed
200         /// </summary>
201         /// <param name="d"></param>
202         /// <param name="e"></param>
203         private static void OnDisplayTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
204         {
205             CustomMultipleDropDownBox c = d as CustomMultipleDropDownBox;
206 
207             if (e.NewValue != null)
208             {
209                 if (e.NewValue.ToString().Length > c.MaxLength)
210                 {
211                     c.DisplayTextToolTip = true;
212                 }
213                 else
214                 {
215                     c.DisplayTextToolTip = false;
216 
217                 }
218                 c.Text = e.NewValue.ToString();
219             }
220             else
221             {
222                 c.DisplayTextToolTip = false;
223                 c.Text = null;
224             }
225 
226         }
227 
228         /// <summary>
229         /// Set whether tooltip can displayed
230         /// </summary>
231         public bool DisplayTextToolTip
232         {
233             get { return (bool)GetValue(DisplayTextToolTipProperty); }
234             set { SetValue(DisplayTextToolTipProperty, value); }
235         }
236 
237         // Using a DependencyProperty as the backing store for DisplayTextToolTip.  This enables animation, styling, binding, etc...
238         public static readonly DependencyProperty DisplayTextToolTipProperty =
239             DependencyProperty.Register("DisplayTextToolTip", typeof(bool), typeof(CustomMultipleDropDownBox), new PropertyMetadata(false));
240 
241         public int MaxLength
242         {
243             get { return (int)GetValue(MaxLengthProperty); }
244             set { SetValue(MaxLengthProperty, value); }
245         }
246 
247         // Using a DependencyProperty as the backing store for MaxLength.  This enables animation, styling, binding, etc...
248         public static readonly DependencyProperty MaxLengthProperty =
249             DependencyProperty.Register("MaxLength", typeof(int), typeof(CustomMultipleDropDownBox), new PropertyMetadata(0));
250 
251         #endregion
252 
253 
254         #endregion
255 
256         #region construtor
257         static CustomMultipleDropDownBox()
258         {
259             DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomMultipleDropDownBox), new FrameworkPropertyMetadata(typeof(CustomMultipleDropDownBox)));
260 
261         }
262         public CustomMultipleDropDownBox()
263         {
264             this.DropDownWidth = 400;
265             this.DropDownHeight = 150;
266             this.MaxLength = 30;
267             this.DisplayTextToolTip = false;
268             TextCompositionManager.StartComposition(new TextComposition(InputManager.Current, this.EditableTextBox,string.Empty));
269             CheckBoxSelectCommand = new DelegateCommand<ComboBoxNode>(CheckBoxClickCommandProcess);
270             ToggleButtonClickCommand = new DelegateCommand(ToggleButtonClickCommandProcess);
271             dataSourceNodeList = new List<ComboBoxNode>();
272             NodeList = new ObservableCollection<ComboBoxNode>();
273             this.Loaded += CustomMultipleDropDownBox_Loaded;
274         }
275 
276         private void CustomMultipleDropDownBox_LostFocus(object sender, RoutedEventArgs e)
277         {
278             this.IsDropDownOpen = false;
279         }
280 
281         protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
282         {
283             if (e.Key == Key.Down)
284             {
285                 if (!this.IsDropDownOpen)
286                 {
287                     this.DisplayText = null;
288                     this.NodeList = this.dataSourceNodeList.ToObservableCollection();
289                     this.IsDropDownOpen = true;
290                 }
291                 //e.Handled = true;
292             }
293             base.OnPreviewKeyDown(e);
294         }
295 
296         void CustomMultipleDropDownBox_Loaded(object sender, RoutedEventArgs e)
297         {
298             try
299             {
300                 OnApplyTemplate();
301                 this.IsEditable = true;
302 
303                 if (this.SelectedItems == null || this.SelectedItems.Count == 0)
304                 {
305                     this.SelectedCount = null;
306                 }
307                 else
308                 {
309                     this.SelectedCount = this.SelectedItems.Count;
310                     this.DisplayText = GetSelectedText();
311                     if (this.SelectedCount == this.TotalCount)
312                     {
313                         this.SelectAllCheckBox.IsChecked = true;
314                     }
315                 }
316             }
317             catch (Exception)
318             {
319 
320             }
321         }
322 
323         protected override void OnDropDownClosed(EventArgs e)
324         {
325             base.OnDropDownClosed(e);
326             DisplayText = this.GetSelectedText();
327             var selectedItems = this.dataSourceNodeList.
328                 FindAll(p => p.IsSelected && p.Title != SelectAllItem).Select(p => p.Item).ToList();
329             var list = this.SelectedItems.CreateNewInstance();
330             foreach (var item in selectedItems)
331             {
332                 list.Add(item);
333             }
334             this.SelectedItems = list;
335         }
336 
337         //protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
338         //{
339         //    base.OnItemsSourceChanged(oldValue, newValue);
340         //    this.InitDataSourceToComboBoxNodeList();
341 
342         //    DisplayText = null;
343         //    OnApplyTemplate();
344         //}
345 
346         protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
347         {
348             base.OnItemsChanged(e);
349             this.InitDataSourceToComboBoxNodeList();
350 
351             OnApplyTemplate();
352         }
353 
354         public override void OnApplyTemplate()
355         {
356             base.OnApplyTemplate();
357 
358             if (this.MyListBox == null)
359             {
360                 return;
361             }
362 
363             if (this.NodeList == null)
364             {
365                 return;
366             }
367 
368             if (this.EditableTextBox != null)
369             {
370                 this.EditableTextBox.PreviewKeyUp += EditableTextBox_PreviewKeyUp;
371                 this.EditableTextBox.TextChanged += new TextChangedEventHandler(EditableTextBox_TextChanged);
372                 this.EditableTextBox.GotFocus += EditableTextBox_GotFocus;
373                 this.EditableTextBox.MouseLeftButtonUp += EditableTextBox_MouseLeftButtonUp;
374             }
375         }
376 
377         void EditableTextBox_PreviewKeyUp(object sender, KeyEventArgs e)
378         {
379             TextBox text = sender as TextBox;
380             this.FilterDataSourceByInputText(text.Text);
381             if (!this.IsDropDownOpen)
382                 this.IsDropDownOpen = true;
383         }
384 
385         void EditableTextBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
386         {
387             this.DisplayText = null;
388             this.IsDropDownOpen = true;
389         }
390 
391         private void EditableTextBox_GotFocus(object sender, RoutedEventArgs e)
392         {
393             this.DisplayText = null;
394             this.IsDropDownOpen = true;
395         }
396 
397         private void EditableTextBox_TextChanged(object sender, TextChangedEventArgs e)
398         {
399             TextBox text = sender as TextBox;
400             this.FilterDataSourceByInputText(text.Text);
401         }
402 
403         private void ToggleButtonClickCommandProcess()
404         {
405             this.DisplayText = null;
406             this.IsDropDownOpen = true;
407         }
408 
409         private void CheckBoxClickCommandProcess(ComboBoxNode node)
410         {
411             if (this.SelectedItems == null)
412             {
413                 return;
414             }
415 
416             if (node.Title == SelectAllItem)
417             {
418                 foreach (var item in this.dataSourceNodeList)
419                 {
420                     if (item.IsEnabled)
421                         item.IsSelected = node.IsSelected;
422                 }
423             }
424             else
425             {
426                 var selectAllItem = this.dataSourceNodeList.Find(p => p.Title == SelectAllItem);
427                 if (selectAllItem != null)
428                 {
429                     int count = this.dataSourceNodeList.Count(P => P.IsSelected && P.Title != SelectAllItem);
430                     if (!node.IsSelected)
431                     {
432                         selectAllItem.IsSelected = false;
433                     }
434                     else if (count == this.dataSourceNodeList.Count - 1)
435                     {
436                         selectAllItem.IsSelected = true;
437                     }
438                 }
439             }
440 
441         }
442 
443 
444         #endregion
445 
446         #region Method
447 
448         /// <summary>
449         /// Used to display selected item titles when popup closed
450         /// </summary>
451         /// <returns></returns>
452         private string GetSelectedText()
453         {
454             StringBuilder sb = new StringBuilder();
455             foreach (var item in this.dataSourceNodeList)
456             {
457                 if (!item.IsSelected)
458                     continue;
459                 if (item.Title == SelectAllItem)
460                     continue;
461 
462                 sb.Append(item.Title).Append(SeparateString);
463             }
464 
465             if (sb.Length > 0)
466                 return sb.ToString().TrimEnd(SeparateString.ToCharArray());
467             else
468                 return null;
469         }
470 
471         /// <summary>
472         /// Use to generate new display items when search in text box
473         /// </summary>
474         /// <param name="inputText"></param>
475         private void FilterDataSourceByInputText(string inputText)
476         {
477             this.NodeList.Clear();
478             ObservableCollection<ComboBoxNode> tempSource = new ObservableCollection<ComboBoxNode>();
479             if (!string.IsNullOrEmpty(inputText))
480             {
481                 this.NodeList = this.dataSourceNodeList.FindAll(
482                     p => p.Title.StartsWith(inputText.Trim(), true, CultureInfo.CurrentCulture)).ToObservableCollection();
483             }
484             else
485             {
486                 this.NodeList = this.dataSourceNodeList.ToObservableCollection();
487             }
488         }
489 
490         private void InitDataSourceToComboBoxNodeList()
491         {
492             dataSourceNodeList.Clear();
493             NodeList.Clear();
494             TotalCount = 0;
495             if (this.ItemsSource != null)
496             {
497                 ComboBoxNode selectAllNode = new ComboBoxNode(SelectAllItem);
498                 dataSourceNodeList.Add(selectAllNode);
499             }
500 
501             foreach (var item in this.ItemsSource)
502             {
503                 string title = item.GetType().GetProperty(this.DisplayMemberPath).GetValue(item, null).ToString();
504                 bool isEnabled = true;
505                 if (!string.IsNullOrWhiteSpace(this.ItemEnabledPath))
506                 {
507                     try
508                     {
509                         isEnabled = (bool)item.GetType().GetProperty(this.ItemEnabledPath).GetValue(item, null);
510                     }
511                     catch { }
512                 }
513                 ComboBoxNode node = new ComboBoxNode(title, item);
514                 node.IsEnabled = isEnabled;
515                 ChangeNodeIsSelectedStatus(node);
516                 dataSourceNodeList.Add(node);
517                 NodeList = dataSourceNodeList.ToObservableCollection();
518             }
519             //set total count when itemsource changed
520             TotalCount = dataSourceNodeList.Count;
521         }
522 
523 
524         private void ChangeNodeIsSelectedStatus()
525         {
526             if (this.dataSourceNodeList != null && this.SelectedItems != null)
527             {
528                 int selectedCount = this.SelectedItems.Count;
529                 foreach (var node in this.dataSourceNodeList)
530                 {
531                     ChangeNodeIsSelectedStatus(node);
532                 }
533             }
534 
535             this.DisplayText = GetSelectedText();
536         }
537 
538         private void ChangeNodeIsSelectedStatus(ComboBoxNode node)
539         {
540             if (node != null && this.SelectedItems != null)
541             {
542                 int selectedCount = this.SelectedItems.Count;
543                 if (node.Title == SelectAllItem)
544                 {
545                     if (selectedCount == TotalCount - 1)
546                         node.IsSelected = true;
547                     else
548                         node.IsSelected = false;
549                     return;
550                 }
551                 if (this.SelectedItems.Contains(node.Item))
552                     node.IsSelected = true;
553                 else
554                     node.IsSelected = false;
555             }
556         }
557 
558 
559 
560         #endregion
561     }
562 }
View Code
  1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  3                     xmlns:local="clr-namespace:Common.Controls.CustomControls"
  4                     xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" 
  5                      xmlns:Attach="clr-namespace:Common.Common"
  6                     >
  7 
  8     <Style x:Key="MultiComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
  9         <Setter Property="OverridesDefaultStyle" Value="true"/>
 10         <Setter Property="AllowDrop" Value="true"/>
 11         <Setter Property="MinWidth" Value="0"/>
 12         <Setter Property="MinHeight" Value="0"/>
 13         <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
 14         <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
 15         <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
 16         <Setter Property="Template">
 17             <Setter.Value>
 18                 <ControlTemplate TargetType="{x:Type TextBox}">
 19                     <ScrollViewer x:Name="PART_ContentHost" Background="Transparent" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
 20                 </ControlTemplate>
 21             </Setter.Value>
 22         </Setter>
 23     </Style>
 24     <Style  TargetType="{x:Type local:CustomMultipleDropDownBox}">
 25         <Setter Property="FocusVisualStyle">
 26             <Setter.Value>
 27                 <Style>
 28                     <Setter Property="Control.Template">
 29                         <Setter.Value>
 30                             <ControlTemplate>
 31                                 <Rectangle Margin="4,4,21,4" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
 32                             </ControlTemplate>
 33                         </Setter.Value>
 34                     </Setter>
 35                 </Style>
 36             </Setter.Value>
 37         </Setter>
 38         <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
 39         <Setter Property="Background" Value="#ffffff"/>
 40         <Setter Property="Height" Value="28"></Setter>
 41         <Setter Property="Margin" Value="0"/>
 42         <Setter Property="Padding" Value="5,2,5,2"></Setter>
 43         <Setter Property="Foreground" Value="#0c223a"/>
 44         <Setter Property="FontFamily" Value="Arial" />
 45         <Setter Property="BorderThickness" Value="2"/>
 46         <Setter Property="VerticalContentAlignment" Value="Center" />
 47         <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
 48         <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
 49         <Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
 50         <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
 51         <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
 52         <Setter Property="Template">
 53             <Setter.Value>
 54                 <ControlTemplate TargetType="{x:Type local:CustomMultipleDropDownBox}">
 55                     <Border x:Name="BD" CornerRadius="5"  Background="{TemplateBinding Background}" BorderBrush="#5d7fad" BorderThickness="1" SnapsToDevicePixels="True" >
 56                         <Grid x:Name="MainGrid" SnapsToDevicePixels="True" >
 57                             <Grid.ColumnDefinitions>
 58                                 <ColumnDefinition Width="*" />
 59                                 <ColumnDefinition  Width="20"/>
 60                             </Grid.ColumnDefinitions>
 61                             <Popup x:Name="PART_Popup"  AllowsTransparency="True" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"   PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
 62                                 <Themes:SystemDropShadowChrome  x:Name="Shdw"  Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=Placement}" >
 63                                     <Border x:Name="DropDownBorder" Background="#FFFFFF" BorderThickness="1" CornerRadius="5" BorderBrush="#5d7fad" >
 64                                         <!--<StackPanel Orientation="Vertical">-->
 65                                         <!--<ListBox  x:Name="MyListBox"  Height="{TemplateBinding DropDownHeight}" Width="{TemplateBinding Width}"    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
 66                                              KeyboardNavigation.DirectionalNavigation="Contained"   >
 67                                                 </ListBox>-->
 68                                         <ListBox  x:Name="MyListBox" Margin="2"   Width="{Binding Width,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}" 
 69                                                       ItemsSource="{Binding NodeList,Mode=OneWay,RelativeSource={RelativeSource TemplatedParent},UpdateSourceTrigger=PropertyChanged}"    
 70                                                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
 71                                              KeyboardNavigation.DirectionalNavigation="Contained"   Style="{DynamicResource MultiComboBoxListBox}">
 72 
 73                                         </ListBox>
 74                                         <!--<ListBox  Foreground="#0c223a" FontSize="14" FontFamily="Arial"  Height="{TemplateBinding DropDownHeight}" x:Name="MySelectedListBox"  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
 75                                              KeyboardNavigation.DirectionalNavigation="Contained" DisplayMemberPath="{TemplateBinding DisplayMemberPath}" ItemsSource="{TemplateBinding SelectedItems}" >
 76                                                 </ListBox>-->
 77                                         <!--</StackPanel>-->
 78                                     </Border>
 79                                 </Themes:SystemDropShadowChrome>
 80                             </Popup>
 81                             <ToggleButton  IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Grid.Column="1"  
 82                                            Name="ToggleButton" Focusable="True" Command="{Binding ToggleButtonClickCommand, RelativeSource={RelativeSource TemplatedParent}}"  ClickMode="Press" SnapsToDevicePixels="True" Style="{DynamicResource ComboBoxToggleButton2}"/>
 83                             <TextBox x:Name="DisPlayTextBlock" Padding="2"  BorderBrush="Red"  Text="{TemplateBinding DisplayText}"  VerticalAlignment="Center"    Background="Transparent"  Foreground="{TemplateBinding Foreground}" 
 84                                      FontSize="{TemplateBinding Foreground}" FontFamily="{TemplateBinding FontFamily}" HorizontalAlignment="Stretch" Style="{StaticResource MultiComboBoxEditableTextBox}">
 85                             </TextBox>
 86 
 87 
 88                         </Grid>
 89                     </Border>
 90                     <ControlTemplate.Triggers>
 91                         <!--<Trigger Property="Validation.HasError" Value="True">
 92                             <Setter TargetName="BD" Property="BorderBrush" Value="Red"></Setter>
 93                         </Trigger>-->
 94                         <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="True">
 95                             <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
 96                             <Setter Property="Color" TargetName="Shdw" Value="#71000000"/>
 97                         </Trigger>
 98                         <Trigger Property="HasItems" Value="False">
 99                             <Setter Property="Height" TargetName="DropDownBorder" Value="95"/>
100                         </Trigger>
101                         <Trigger Property="IsEnabled" Value="False">
102                             <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
103                             <Setter Property="Background" Value="#FFF4F4F4"/>
104                         </Trigger>
105                         <MultiTrigger>
106                             <MultiTrigger.Conditions>
107                                 <Condition Property="IsGrouping" Value="True"/>
108                                 <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="False"/>
109                             </MultiTrigger.Conditions>
110                             <Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
111                         </MultiTrigger>
112                         <!--<Trigger Property="CanContentScroll" SourceName="DropDownScrollViewer" Value="False">
113                             <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
114                             <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
115                         </Trigger>-->
116                         <Trigger Property="Validation.HasError" Value="True">
117                             <Setter Property="ToolTip">
118                                 <Setter.Value>
119                                     <Binding 
120                             Path="(Validation.Errors).CurrentItem.ErrorContent"
121                             RelativeSource="{x:Static RelativeSource.Self}"
122                             />
123                                 </Setter.Value>
124                             </Setter>
125                         </Trigger>
126                         <Trigger Property="DisplayTextToolTip" Value="True">
127                             <Setter Property="ToolTip">
128                                 <Setter.Value>
129                                     <Binding 
130                             Path="DisplayText"
131                             RelativeSource="{x:Static RelativeSource.Self}"
132                             />
133                                 </Setter.Value>
134                             </Setter>
135                         </Trigger>
136                     </ControlTemplate.Triggers>
137                 </ControlTemplate>
138             </Setter.Value>
139         </Setter>
140         <Setter Property="Validation.ErrorTemplate">
141             <Setter.Value>
142                 <ControlTemplate>
143                     <AdornedElementPlaceholder>
144                         <Border BorderBrush="Red" BorderThickness="1" CornerRadius="5" SnapsToDevicePixels="True" />
145 
146                     </AdornedElementPlaceholder>
147                 </ControlTemplate>
148             </Setter.Value>
149         </Setter>
150         <Style.Triggers>
151             <Trigger Property="Attach:AttachProperty.IsMandatory" Value="True">
152                 <Setter Property="Background" Value="LightYellow"/>
153             </Trigger>
154             <Trigger Property="IsFocused" Value="True" >
155                 <Setter Property="Button.Effect">
156                     <Setter.Value>
157                         <DropShadowEffect ShadowDepth="0" BlurRadius="8" Color="#fe1C67C7"></DropShadowEffect>
158                     </Setter.Value>
159                 </Setter>
160             </Trigger>
161             <Trigger Property="IsMouseOver" Value="True">
162                 <Setter Property="Button.Effect">
163                     <Setter.Value>
164                         <DropShadowEffect ShadowDepth="0" BlurRadius="8" Color="#Fe9DBADF"></DropShadowEffect>
165                     </Setter.Value>
166                 </Setter>
167             </Trigger>
168             <Trigger Property="IsEnabled" Value="False">
169                 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
170                 <Setter Property="Background" Value="#FFF4F4F4"/>
171             </Trigger>
172         </Style.Triggers>
173     </Style>
174     <Geometry x:Key="DownArrowGeometry">M 0 0 L 3.5 4 L 7 0 Z</Geometry>
175     <Style x:Key="ComboBoxToggleButton2" TargetType="{x:Type ToggleButton}">
176         <Setter Property="OverridesDefaultStyle" Value="true"/>
177         <Setter Property="IsTabStop" Value="false"/>
178         <Setter Property="Focusable" Value="false"/>
179         <Setter Property="ClickMode" Value="Press"/>
180         <Setter Property="Cursor" Value="Hand"/>
181         <Setter Property="Background" Value="Transparent"/>
182         <Setter Property="Template">
183             <Setter.Value>
184                 <ControlTemplate TargetType="{x:Type ToggleButton}">
185                     <Grid>
186                         <Grid.ColumnDefinitions>
187                             <ColumnDefinition />
188                             <ColumnDefinition Width="20" />
189                         </Grid.ColumnDefinitions>
190                         <Border x:Name="Border" Grid.ColumnSpan="2"  Background="{TemplateBinding Background}"   SnapsToDevicePixels="True" />
191                         <Grid Grid.Column="1" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Background="{TemplateBinding Background}">
192                             <Path x:Name="Arrow" Data="{StaticResource DownArrowGeometry}" Fill="#5d7fad" HorizontalAlignment="Center" Margin="0,1,0,0" VerticalAlignment="Center"/>
193                         </Grid>
194                     </Grid>
195 
196                 </ControlTemplate>
197             </Setter.Value>
198         </Setter>
199     </Style>
200     <Style x:Key="MultiComboBoxListBox" TargetType="{x:Type ListBox}">
201         <Setter Property="BorderThickness" Value="0"></Setter>
202         <Setter Property="ItemTemplate">
203             <Setter.Value>
204                 <DataTemplate>
205                     <CheckBox Margin="0,2,0,0" IsChecked="{Binding IsSelected,Mode=TwoWay}"
206                               IsEnabled="{Binding IsEnabled, Mode=OneWay}" 
207                               Command="{Binding CheckBoxSelectCommand,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:CustomMultipleDropDownBox}}}"
208                               CommandParameter="{Binding}"
209                               Tag="{Binding Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
210                               Content="{Binding Title}" 
211                               Width="{Binding DropDownWidth,Mode=OneWay}"></CheckBox>
212 
213                 </DataTemplate>
214             </Setter.Value>
215         </Setter>
216     </Style>
217 </ResourceDictionary>
View Code