C# 带有组合框的CustomControl

C# 带有组合框的CustomControl,c#,.net,wpf,combobox,custom-controls,C#,.net,Wpf,Combobox,Custom Controls,使用组合框创建自定义控件时遇到问题 这是我的简单代码: public class MyComboBox : Control { public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } // Using a De

使用组合框创建自定义控件时遇到问题 这是我的简单代码:

public class MyComboBox : Control
{
    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ItemsSource.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(MyComboBox), new UIPropertyMetadata(null));




    public string DisplayMemberPath
    {
        get { return (string)GetValue(DisplayMemberPathProperty); }
        set { SetValue(DisplayMemberPathProperty, value); }
    }

    // Using a DependencyProperty as the backing store for DisplayMemberPath.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DisplayMemberPathProperty =
        DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(MyComboBox), new UIPropertyMetadata(""));





    public string SelectedValuePath
    {
        get { return (string)GetValue(SelectedValuePathProperty); }
        set { SetValue(SelectedValuePathProperty, value); }
    }

    // Using a DependencyProperty as the backing store for SelectedValuePath.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedValuePathProperty =
        DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(MyComboBox), new UIPropertyMetadata(""));





    public object SelectedValue
    {
        get { return (object)GetValue(SelectedValueProperty); }
        set { SetValue(SelectedValueProperty, value); }
    }

    // Using a DependencyProperty as the backing store for SelectedValue.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedValueProperty =
        DependencyProperty.Register("SelectedValue", typeof(object), typeof(MyComboBox), new UIPropertyMetadata(null));




    public int SelectedIndex
    {
        get { return (int)GetValue(SelectedIndexProperty); }
        set { SetValue(SelectedIndexProperty, value); }
    }

    // Using a DependencyProperty as the backing store for SelectedIndex.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedIndexProperty =
        DependencyProperty.Register("SelectedIndex", typeof(int), typeof(MyComboBox), new UIPropertyMetadata(0));



    static MyComboBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MyComboBox), new FrameworkPropertyMetadata(typeof(MyComboBox)));
    }
}
这是它的Generic.xaml:

<Style TargetType="{x:Type local:MyComboBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyComboBox}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" Text="MyComboBox" />
                    <ComboBox Grid.Column="1"
                              ItemsSource="{Binding Path=ItemsSource, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                              SelectedIndex="{Binding Path=SelectedIndex, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                              DisplayMemberPath="{Binding Path=DisplayMemberPath, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                              SelectedValuePath="{Binding Path=SelectedValuePath, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                              SelectedValue="{Binding Path=SelectedValue, RelativeSource={RelativeSource Mode=TemplatedParent}}">
                    </ComboBox>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
当我从Generic.xaml中删除属性SelectedIndex时,问题就消失了,但我需要它,因为我希望使用我的控件的人可以拥有与组合框相同的基本功能。

有人知道怎么解决吗?

起初,我觉得你的问题在SelectedValue中。在您的VM中,它的类型是int,但WPF希望它是KeyValuePair。集合项的类型

ItemsSource="{Binding Path=MyNumbers}" // KeyValuePair
     DisplayMemberPath="Key" 
     SelectedValuePath="Value"
     SelectedValue="{Binding Path=MyNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" // int
然后我意识到我的错误,SelectedItem必须是KeyValuePair类型。
错误消息看起来是WPF没有查看SelectedValuePath提供的items属性,而是试图将其显式转换为KeyValue对。这不是记录在案的行为。

我自己找到了解决方案。

问题出在我为SelectedIndex插入的默认值上:它必须是-1而不是0。

您有没有理由从头开始创建一个组合框,而不扩展/使用现有的组合框?这只是我想要构建的自定义控件的一部分。我只取不起作用的部分。而且,如果你从MyComboBox中选择一个值,一切都开始正常工作。那么问题就出在VS的设计器上了?在运行时一切正常?不,问题在于启动应用程序时。在有人更改组合框值之前,红色边框是可见的。
public class ViewModel : INotifyPropertyChanged
{
    private int _number;
    public int Number
    {
        get { return _number; }
        set
        {
            _number = value;
            OnPropertyChanged("Number");
        }
    }

    public Dictionary<string, int> Numbers { get; set; }

    private int _myNumber;
    public int MyNumber
    {
        get { return _myNumber; }
        set
        {
            _myNumber = value;
            OnPropertyChanged("MyNumber");
        }
    }

    public Dictionary<string, int> MyNumbers { get; set; }

    public ViewModel()
    {
        Numbers = new Dictionary<string, int>()
        {
            { "One", 1 },
            { "Two", 2 },
            { "Three", 3 }
        };
        Number = 1;

        MyNumbers = new Dictionary<string, int>()
        {
            { "Four", 4 },
            { "Five", 5 },
            { "Six", 6 }
        };
        MyNumber = 4;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler e = PropertyChanged;
        if (e != null)
        {
            e(this, new PropertyChangedEventArgs(name));
        }
    }
}
System.Windows.Data Error: 23 : Cannot convert '[Four, 4]' from type 'KeyValuePair`2' to type 'System.Int32' for 'en-US' culture with default conversions; consider using Converter property of Binding. NotSupportedException:'System.NotSupportedException: Int32Converter cannot convert from System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].
   at System.ComponentModel.TypeConverter.GetConvertFromException(Object value)
   at System.ComponentModel.TypeConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   at System.ComponentModel.BaseNumberConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)'
System.Windows.Data Error: 7 : ConvertBack cannot convert value '[Four, 4]' (type 'KeyValuePair`2'). BindingExpression:Path=MyNumber; DataItem='ViewModel' (HashCode=55591935); target element is 'MyComboBox' (Name=''); target property is 'SelectedValue' (type 'Object') NotSupportedException:'System.NotSupportedException: Int32Converter cannot convert from System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].
   at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)
   at MS.Internal.Data.ObjectTargetConverter.ConvertBack(Object o, Type type, Object parameter, CultureInfo culture)
   at System.Windows.Data.BindingExpression.ConvertBackHelper(IValueConverter converter, Object value, Type sourceType, Object parameter, CultureInfo culture)'
ItemsSource="{Binding Path=MyNumbers}" // KeyValuePair
     DisplayMemberPath="Key" 
     SelectedValuePath="Value"
     SelectedValue="{Binding Path=MyNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" // int