WPF 自定义单选下拉列表框 支持输入的时候可以过滤,只能下拉的时候不需要过滤

4 篇文章 0 订阅

C#代码, 有点乱,待整理

public class BookComboBoxEdit: ComboBox, INotifyPropertyChanged
    {
        public BookComboBoxEdit()
            : base()
        {
            DataCollection = new List<BookViewModel>()
            {
                new BookViewModel(){Name = "C++语言程序设计",Price =39.9,Publisher = "谭浩强"},
                new BookViewModel(){Name = "Java深入浅出",Price =39.9,Publisher = "谭浩强"}
            };
            DataCollectionView = CollectionViewSource.GetDefaultView(DataCollection);
            DataCollectionView.Filter = new Predicate<object>(OnFilter);

            this.PropertyChanged += SymbolEdit_PropertyChanged;
        }

        private void SymbolEdit_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("SelctedInstance"))
            {
                if (SelctedInstance != null)
                {
                    SelectedBook = SelctedInstance.Name;
                    Price = SelctedInstance.Price;
                    App.Current.Dispatcher.Invoke(()=>DataCollectionView.Refresh());
                }
            }
            if (e.PropertyName.Equals("SelectedBook"))
            {
                if (SelctedInstance != null && !FilterByCondition(SelctedInstance, SelectedBook))
                {
                    SelctedInstance = null;
                    Price = double.NaN;
                }
               App.Current.Dispatcher.Invoke(()=>DataCollectionView.Refresh());
            }
            if (e.PropertyName.Equals("DataCollection"))
            {
                App.Current.Dispatcher.Invoke(() =>
                {
                    if (DataCollection==null)return;
                    DataCollectionView = CollectionViewSource.GetDefaultView(DataCollection);
                    DataCollectionView.Filter += OnFilter;
                });
               
            }
        }



        private bool OnFilter(object mod)
        {
            //只读不可输入的不需要过滤
            if (IsTextEditable) return true;

            var book = mod as BookViewModel;
            if (book != null && !string.IsNullOrWhiteSpace(SelectedBook))
            {
                return FilterByCondition(book, SelectedBook);
            }
            else
                return true;
        }

        private bool FilterByCondition(BookViewModel vm, string condition)
        {
            return vm != null && !string.IsNullOrWhiteSpace(condition)&&(vm.Name.Trim().Contains(condition) || vm.Name.Equals(condition, StringComparison.CurrentCultureIgnoreCase)); 
        }
        private  ICollectionView dataCollectionView;
        private ObservableCollection<BookViewModel> dataCollection;

        public ICollectionView DataCollectionView
        {
            get { return dataCollectionView; }
            set
            {
                if (dataCollectionView != value)
                {
                    dataCollectionView = value;
                    OnPropertyChanged("DataCollectionView");
                };
            }
        }


        public static readonly DependencyProperty DataCollectionProperty = DependencyProperty.Register("DataCollection",
           typeof(IEnumerable<BookViewModel>), typeof(BookComboBoxEdit), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure,
               new PropertyChangedCallback(TotalPropertyChanged)));
        public IEnumerable<BookViewModel> DataCollection
        {
            get { return (IEnumerable<BookViewModel>)GetValue(DataCollectionProperty); }
            set
            {
                SetValue(DataCollectionProperty,value);
            }
        }

        public static readonly DependencyProperty IsTextEditableProperty = DependencyProperty.Register("IsTextEditable",
         typeof(bool), typeof(BookComboBoxEdit), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsMeasure,
             new PropertyChangedCallback(TotalPropertyChanged)));

        public bool IsTextEditable
        {
            get { return (bool)GetValue(IsTextEditableProperty); }
            set { SetValue(IsTextEditableProperty, value); }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }



        public static readonly DependencyProperty SelectedBookProperty = DependencyProperty.Register("SelectedBook",
            typeof(string), typeof(BookComboBoxEdit), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure,
                new PropertyChangedCallback(TotalPropertyChanged)));

        public string SelectedBook
        {
            get { return (string)GetValue(SelectedBookProperty); }
            set { SetValue(SelectedBookProperty, value); }
        }

         private static void TotalPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((BookComboBoxEdit)d).OnPropertyChanged(e.Property.Name);
        }


         public static readonly DependencyProperty SelctedInstancekProperty = DependencyProperty.Register("SelctedInstance",
             typeof(BookViewModel), typeof(BookComboBoxEdit), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure,
                 new PropertyChangedCallback(TotalPropertyChanged)));

        public BookViewModel SelctedInstance
        {
            get { return (BookViewModel)GetValue(SelctedInstancekProperty); }
            set
            {
                SetValue(SelctedInstancekProperty,value);
            }
        }

        private double price=double.NaN;

        public double Price
        {
            get { return price; }
            set
            {
                if (price != null)
                {
                    price = value;
                    OnPropertyChanged("Price");
                }
            }
        }

    }

xaml代码

 <converter:DoubleNanConverter x:Key="doubleNanConv"/>
    <converter:StringToBoolConverter x:Key="stringToBoolCov"/>
    <converter:PoupIsOpenMultiConverter x:Key="poupIsOpenMultiConv"/>
    
    <Style TargetType="TextBlock">
        <Setter Property="VerticalAlignment" Value="Center"/>
    </Style>
    <Style x:Key="noBorderTextBoxStyle" TargetType="TextBox">
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="BorderThickness" Value="0"/>
    </Style>

 <Style x:Key="dropDownToggleButtonStyle" TargetType="ToggleButton">
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderBrush" Value="Transparent"/>
        <Setter Property="Width" Value="25"/>
        <Setter Property="Height" Value="25"/>
        <Setter Property="RenderTransformOrigin" Value="0.5,0.5"></Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ToggleButton">
                    <!--<ContentPresenter />-->
                    <Grid>
                        <Image x:Name="img" Source="../Images/DropDown.png">
                            <Image.RenderTransform>
                                <RotateTransform x:Name="RotateTransfer" CenterX="12.5" CenterY="12.5" Angle="0"/>
                            </Image.RenderTransform>
                        </Image>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="#808080"></Setter>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter Property="Background" Value="#F5F5F5"></Setter>
                        </Trigger>
                        <!--<EventTrigger RoutedEvent="ToggleButton.Checked">
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation By="180" Duration="0:0:.1"
                                             Storyboard.TargetName="RotateTransfer" 
                                             Storyboard.TargetProperty="Angle" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="ToggleButton.Unchecked">
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation By="-180" Duration="0:0:.1"
                                             Storyboard.TargetName="RotateTransfer" 
                                             Storyboard.TargetProperty="Angle" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>-->
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

<Style TargetType="{x:Type controls:BookComboBoxEdit}">
        <Setter Property="Height" Value="Auto"/>
        <Setter Property="Width" Value="200"></Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:BookComboBoxEdit}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="25"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <Border Grid.Row="0" BorderThickness="0.5" BorderBrush="#808080">
                            <Grid  VerticalAlignment="Center">
                                <Grid.ColumnDefinitions> 
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="30"/>
                                </Grid.ColumnDefinitions>
                                <TextBox IsReadOnly="{Binding IsTextEditable,RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource noBorderTextBoxStyle}" Grid.Column="0" Width="Auto" MinWidth="150"  x:Name="txt_Input" 
                                         Text="{Binding SelectedBook,RelativeSource={RelativeSource TemplatedParent},UpdateSourceTrigger=PropertyChanged}" />
                                <TextBlock Grid.Column="1" Margin="8,0,0,0" HorizontalAlignment="Right" Text="{Binding Price,RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource doubleNanConv}}"/>
                                <ToggleButton Grid.Column="2" x:Name="btnToggle" HorizontalAlignment="Right" Margin="8,0,0,0" Style="{StaticResource dropDownToggleButtonStyle}">
                                </ToggleButton>
                            </Grid>
                        </Border>
                        <!--数据匹配列表-->
                        <Popup Grid.Row="1" x:Name="dataList"  StaysOpen="True" PlacementTarget="{Binding ElementName=txt_Input}" AllowsTransparency="True"
                              IsOpen="{Binding ElementName=btnToggle,Path=IsChecked,Mode=OneWay}">
                            <!--<Popup.IsOpen>
                                <MultiBinding Converter="{StaticResource poupIsOpenMultiConv}">
                                    <Binding ElementName="btnToggle" Path="IsChecked"/>
                                    <Binding ElementName="txt_Input" Path="Text"/>
                                </MultiBinding>
                            </Popup.IsOpen>-->
                            <ListView x:Name="_listView" Focusable="True" AllowDrop="True" IsEnabled="True" Width="Auto" Height="Auto"
                                      SelectedItem="{Binding SelctedInstance,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
                                      ItemsSource="{Binding DataCollectionView,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" 
                                      >
                                <ListView.View>
                                    <GridView AllowsColumnReorder="True">
                                        <!--<GridViewColumn Header="选择" CellTemplate="{StaticResource checkBoxGridViewColumn}"/>-->
                                        <GridViewColumn Header="书名" Width="Auto"  DisplayMemberBinding="{Binding Name}"/>
                                        <GridViewColumn Header="价格" Width="Auto" DisplayMemberBinding="{Binding Price}"/>
                                        <GridViewColumn Header="作者" Width="Auto" DisplayMemberBinding="{Binding Publisher}"/>
                                    </GridView>
                                </ListView.View>
                            </ListView>
                        </Popup>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsFocused" SourceName="txt_Input" Value="True"/>
                                <Condition Property="Text" SourceName="txt_Input" Value=""/>
                            </MultiTrigger.Conditions>
                            <MultiTrigger.Setters>
                                <Setter TargetName="dataList" Property="IsOpen" Value="False"></Setter>
                            </MultiTrigger.Setters>
                        </MultiTrigger>

                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding ElementName=txt_Input,Path=IsFocused}" Value="True"/>
                                <Condition  Binding="{Binding ElementName=txt_Input,Path=Text,Converter={StaticResource stringToBoolCov}}"
                                            Value="True"/>
                            </MultiDataTrigger.Conditions>
                            <MultiDataTrigger.Setters>
                                <Setter TargetName="dataList" Property="IsOpen" Value="True"/>
                            </MultiDataTrigger.Setters>
                        </MultiDataTrigger>
                      
                        <EventTrigger SourceName="btnToggle" RoutedEvent="LostFocus">
                            <BeginStoryboard>
                                <Storyboard>
                                    <BooleanAnimationUsingKeyFrames
                                        Storyboard.TargetName="btnToggle"
                                        Storyboard.TargetProperty="IsChecked"
                                        >
                                        <DiscreteBooleanKeyFrame
                                            KeyTime="0:0:0.2"
                                            Value="False"/>
                                    </BooleanAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>

                    </ControlTemplate.Triggers>
                </ControlTemplate>

            </Setter.Value>
        </Setter>
    </Style>

Converter

public class DoubleNanConverter:IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null)
                return string.Empty;

            var d=double.NaN;
            double.TryParse(value.ToString(), out d);
            return  double.IsNaN(d)? string.Empty : d.ToString();
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }
    }


 public class StringToBoolConverter:IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value != null && !string.IsNullOrWhiteSpace(value.ToString());
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
public class PoupIsOpenMultiConverter:IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (values == null || values.Length < 2)
                return false;

            var isChecked = false;
            var text = values[1];
            bool.TryParse(values[0].ToString(), out isChecked);
            if (isChecked)
            {
                return true;
            }
            else
            {
                return text!=null&&!string.IsNullOrWhiteSpace(text.ToString());
            }

        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现整个自定义 `ComboBox` 的点击都能触发下拉事件,您可以使用 `PreviewMouseDown` 事件来处理点击事件,并在事件处理程序中调用 `ComboBox` 的 `IsDropDownOpen` 属性来控制下拉的显示和隐藏。 以下是一个示例,展示了如何实现这个功能: ```xml <ComboBox PreviewMouseDown="ComboBox_PreviewMouseDown"> <ComboBox.Template> <ControlTemplate TargetType="ComboBox"> <Grid> <!-- 添加下拉按钮和显示内容 --> <ToggleButton x:Name="PART_ToggleButton" ClickMode="Press" IsChecked="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/> <ContentPresenter x:Name="PART_ContentPresenter" IsHitTestVisible="False" Content="{TemplateBinding SelectionBoxItem}"/> <!-- 添加下拉内容 --> <Popup x:Name="PART_Popup" IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom"> <Border Background="White" BorderBrush="Black" BorderThickness="1"> <ItemsPresenter/> </Border> </Popup> </Grid> </ControlTemplate> </ComboBox.Template> </ComboBox> ``` 在上面的示例中,我们使用 `PreviewMouseDown` 事件处理程序 `ComboBox_PreviewMouseDown` 来处理点击事件。在事件处理程序中,我们通过设置 `IsDropDownOpen` 属性来控制下拉的显示和隐藏。 ```csharp private void ComboBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) { ComboBox comboBox = (ComboBox)sender; comboBox.IsDropDownOpen = !comboBox.IsDropDownOpen; e.Handled = true; } ``` 在事件处理程序中,我们首先获取到 `ComboBox` 控件,然后通过将 `IsDropDownOpen` 属性的值取反来控制下拉的显示和隐藏。最后,我们将 `e.Handled` 设置为 `true`,以防止点击事件继续传播。 这样,当用户点击整个自定义 `ComboBox` 区域时,都会触发下拉事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值