2

我有以下问题:我创建了一个运行良好的 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>

4

0 回答 0