用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 }
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>