2012-12-02 135 views
0

我遇到了麻煩,使用ComboBox創建自定義控件。
這是我簡單的代碼:
帶組合框的自定義控件

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> 

爲了測試它,我做了這個MainWindow.xaml一個簡單的WPF應用程序:

<Window x:Class="Example.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:Example" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.DataContext> 
     <local:ViewModel /> 
    </Window.DataContext> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
     </Grid.RowDefinitions> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition /> 
      <ColumnDefinition /> 
     </Grid.ColumnDefinitions> 

     <ComboBox Grid.Row="0" Grid.Column="0" Margin="4" VerticalAlignment="Center" 
        ItemsSource="{Binding Path=Numbers}" 
        DisplayMemberPath="Key" 
        SelectedValuePath="Value" 
        SelectedValue="{Binding Path=Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 
     <TextBlock Grid.Row="0" Grid.Column="1" Margin="4" HorizontalAlignment="Center" VerticalAlignment="Center" 
        Text="{Binding Path=Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 

     <local:MyComboBox Grid.Row="1" Grid.Column="0" Margin="4" VerticalAlignment="Center" 
          ItemsSource="{Binding Path=MyNumbers}" 
          DisplayMemberPath="Key" 
          SelectedValuePath="Value" 
          SelectedValue="{Binding Path=MyNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 
     <TextBlock Grid.Row="1" Grid.Column="1" Margin="4" HorizontalAlignment="Center" VerticalAlignment="Center" 
        Text="{Binding Path=MyNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 
    </Grid> 
</Window> 

and this ViewModel:

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)); 
     } 
    } 
} 

當我啓動它,我自定義的控制有一個紅色的邊框和Visual Studio信號的輸出窗口此錯誤:

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)' 

的問題消失了,當我從Generic.xaml刪除屬性的SelectedIndex,但我需要它,因爲我希望那些將使用我的控件的人可以擁有ComboBox的基本功能。
任何人都知道如何解決它?

+0

是否有理由讓您從頭開始創建組合框,而不是擴展/使用現有組合框? – LightStriker

+0

這只是我想要構建的自定義控件的一部分。我只拿走那些不起作用的部分。 –

回答

1

我自己找到解決方案。
問題是我爲SelectedIndex插入的默認值:它必須是-1而不是0.

0

起初,我覺得你的問題在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給出的項目屬性,但會嘗試將其顯式轉換爲KeyValue對。這不是記錄的行爲。

+0

此外,如果您從MyComboBox中選擇一個值,一切都會開始正常工作。 –

+0

所以這個問題只是VS的設計師?在運行時一切正常嗎? –

+0

當您啓動應用程序時,問題不大。在有人改變組合框值之前,紅色邊框是可見的。 –