我有以下问题:我创建了一个运行良好的 MultiSelectComboBox:
<UserControl x:Class="Test.MultiSelectComboBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test">
<UserControl.Resources>
<DataTemplate x:Key="DropDownTemplate">
<CheckBox Content="{Binding Value}"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"
Tag="{Binding Key}"
Click="CheckBox_Click" />
</DataTemplate>
<DataTemplate x:Key="SelectedTemplate">
<TextBlock Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MultiSelectComboBox}}}" Background="Transparent" Padding="0" />
</DataTemplate>
</UserControl.Resources>
<DockPanel>
<ComboBox
x:Name="comboBox"
SnapsToDevicePixels="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="True"
IsSynchronizedWithCurrentItem="True"
IsDropDownOpen="{Binding IsDropDownOpen}"
HorizontalContentAlignment="Stretch"
StaysOpenOnEdit="True"
SelectionChanged="MultiSelectCombo_SelectionChanged">
<ComboBox.ItemTemplateSelector>
<local:ComboBoxItemTemplateSelector DropDownTemplate="{StaticResource DropDownTemplate}" SelectedTemplate="{StaticResource SelectedTemplate}" />
</ComboBox.ItemTemplateSelector>
<ComboBox.ItemContainerStyle>
<Style>
<Setter Property="ComboBoxItem.Padding" Value="0" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</DockPanel>
和代码隐藏:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Collections;
using System.Windows.Controls.Primitives;
using System.Collections.Specialized;
namespace Test
{
/// <summary>
/// Interaction logic for MultiSelectComboBox.xaml
/// </summary>
public partial class MultiSelectComboBox : UserControl, INotifyPropertyChanged
{
private ObservableCollection<Node> _nodeList;
public MultiSelectComboBox()
{
InitializeComponent();
_nodeList = new ObservableCollection<Node>();
}
#region Dependency Properties
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(Dictionary<int, string>), typeof(MultiSelectComboBox), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(MultiSelectComboBox.OnItemsSourceChanged)));
public static readonly DependencyProperty SelectedValuesProperty = DependencyProperty.Register("SelectedValues", typeof(List<int>), typeof(MultiSelectComboBox), new FrameworkPropertyMetadata(new List<int>(), new PropertyChangedCallback(MultiSelectComboBox.OnSelectedValuesChanged)));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MultiSelectComboBox), new UIPropertyMetadata(string.Empty));
public static readonly DependencyProperty DefaultTextProperty = DependencyProperty.Register("DefaultText", typeof(string), typeof(MultiSelectComboBox), new UIPropertyMetadata(string.Empty));
public Dictionary<int, string> ItemsSource
{
get { return (Dictionary<int, string>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public Dictionary<int, string> SelectedItems
{
get { return ItemsSource.Where(kvp => SelectedValues.Contains(kvp.Key)).ToDictionary(k => k.Key, k => k.Value); }
}
public List<int> SelectedValues
{
get { return (List<int>)GetValue(SelectedValuesProperty); }
set { SetValue(SelectedValuesProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public string DefaultText
{
get { return (string)GetValue(DefaultTextProperty); }
set { SetValue(DefaultTextProperty, value); }
}
#endregion
#region Events
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MultiSelectComboBox control = (MultiSelectComboBox)d;
control.ItemsSourceChanged();
}
private static void OnSelectedValuesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MultiSelectComboBox control = (MultiSelectComboBox)d;
control.SelectedValuesChanged();
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
CheckBox clickedBox = (CheckBox)sender;
if ((int)clickedBox.Tag == -1) //case of "All"-checkbox
{
if (clickedBox.IsChecked.Value) foreach (Node node in _nodeList) node.IsSelected = true;
else foreach (Node node in _nodeList) node.IsSelected = false;
}
else
{
int _selectedCount = 0;
foreach (Node s in _nodeList)
{
if (s.IsSelected && s.Value != "All") _selectedCount++;
}
if (_selectedCount == _nodeList.Count - 1) _nodeList.FirstOrDefault(i => i.Key == -1).IsSelected = true;
else _nodeList.FirstOrDefault(i => i.Key == -1).IsSelected = false;
}
List<int> list = new List<int>();
foreach (Node node in _nodeList)
{
if (node.IsSelected && node.Key != -1)
{
if (this.ItemsSource.Count > 0) list.Add(node.Key);
}
}
SelectedValues = list;
}
#endregion
#region Methods
private void ItemsSourceChanged()
{
_nodeList.Clear();
if (this.ItemsSource.Count > 0) _nodeList.Add(new Node(-1, "All"));
foreach (KeyValuePair<int, string> keyValue in this.ItemsSource)
{
Node node = new Node(keyValue.Key, keyValue.Value);
_nodeList.Add(node);
}
comboBox.ItemsSource = _nodeList;
}
private void SelectedValuesChanged()
{
StringBuilder displayText = new StringBuilder();
foreach (var n in _nodeList)
{
if (this.SelectedValues.Contains(n.Key))
{
n.IsSelected = true;
displayText.Append(n.Value + ", ");
}
else if (n.Key != -1) n.IsSelected = false;
}
this.Text = displayText.ToString().TrimEnd(new char[] { ',', ' ' });
// set DefaultText if nothing else selected
if (string.IsNullOrEmpty(this.Text))
{
this.Text = this.DefaultText;
}
}
#endregion
private void MultiSelectCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
e.Handled = true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Node : INotifyPropertyChanged
{
private int _Key;
private string _Value;
private bool _IsSelected;
public Node(int key, string value)
{
_Key = key;
_Value = value;
}
#region Properties
public int Key
{
get { return _Key; }
set { _Key = value; NotifyPropertyChanged("Key"); }
}
public string Value
{
get { return _Value; }
set { _Value = value; NotifyPropertyChanged("Value"); }
}
public bool IsSelected
{
get { return _IsSelected; }
set { _IsSelected = value; NotifyPropertyChanged("IsSelected"); }
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
public DataTemplate SelectedTemplate { get; set; }
public DataTemplate DropDownTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
/*ComboBoxItem comboBoxItem = ((ContentPresenter)container).FindVisualParent<ComboBoxItem>();
if (comboBoxItem == null)
{
return SelectedTemplate;
}
return DropDownTemplate;*/
if (((ContentPresenter)container).TemplatedParent is ComboBoxItem) return DropDownTemplate;
return SelectedTemplate;
}
}
public static class Helper
{
public static T FindVisualParent<T>(this UIElement element) where T : UIElement
{
UIElement parent = element;
while (parent != null)
{
T correctlyTyped = parent as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
}
}
现在,一切都像魅力一样工作,但是当我想在其中创建一个带有此 MultiSelectComboBox 的新用户控件时,它不会为“SelectedItem”调用 DataTemplateSelector(我们可以在 toggleButton 内部看到)。我看到有几个 ContentPresenter 如果我在我的控件中使用它的 Content-properties 是不同的。有什么错误?如果我将它放在另一个控件中,为什么它的行为会有所不同?
二次控制代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Test
{
/// <summary>
/// Interaktionslogik für TestMCB.xaml
/// </summary>
public partial class SecondMCB : UserControl
{
public static readonly DependencyProperty SelectedValuesProperty = DependencyProperty.Register("SelectedValues", typeof(List<int>), typeof(SecondMCB), new FrameworkPropertyMetadata(new List<int>()));
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(Dictionary<int, string>), typeof(SecondMCB), new FrameworkPropertyMetadata(new Dictionary<int, string>()));
public static readonly DependencyProperty DefaultTextProperty = DependencyProperty.Register("DefaultText", typeof(string), typeof(SecondMCB), new UIPropertyMetadata(string.Empty));
public List<int> SelectedValues
{
get { return (List<int>)GetValue(SelectedValuesProperty); }
set { SetValue(SelectedValuesProperty, value); }
}
public Dictionary<int, string> ItemsSource
{
get { return (Dictionary<int, string>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public string DefaultText
{
get { return (string)GetValue(DefaultTextProperty); }
set { SetValue(DefaultTextProperty, value); }
}
public SecondMCB()
{
InitializeComponent();
}
}
}
和 XAML:
<UserControl x:Class="Test.SecondMCB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Test"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<local:MultiSelectComboBox
SelectedValues="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:SecondMCB}}, Path=SelectedValues, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:SecondMCB}}, Path=ItemsSource, UpdateSourceTrigger=PropertyChanged}"
DefaultText="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:SecondMCB}}, Path=DefaultText, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>