項目中有一個多項選擇篩選的功能, 因爲篩選條件太多, 用戶又習慣在平板上進行操做, 因此要求咱們把checkbox 放到一個combobox裏面, 而後checkbox的選項要在combobox裏面顯示出來, 再加一個全選功能. 喏, 就是這種效果.ide
首先, 實現思路是: 1. 自定義一個用戶控件 2.添加一個combobox 3.重載combobox的item模板 4. 給模板中的checkbox添加點擊事件.優化
思路肯定了,就開始實現功能.ui
<Grid> <ComboBox x:Name="CheckableCombo"> <ComboBox.ItemTemplate> <HierarchicalDataTemplate> <CheckBox Content="{Binding Title}" IsChecked="{Binding IsSelected, Mode=TwoWay}" Tag="{RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}" Click="Checkbox_OnClick"></CheckBox> </HierarchicalDataTemplate> </ComboBox.ItemTemplate> <ComboBox.Template> <ControlTemplate TargetType="ComboBox"> <Grid> <ToggleButton Name="ToggleButton" Style="{StaticResource ComboBoxToggleButton}" Focusable="False" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"> </ToggleButton> <ContentPresenter x:Name="Presenter" IsHitTestVisible="False" Margin="3, 3, 23, 3" VerticalAlignment="Center" HorizontalAlignment="Left"> <ContentPresenter.Content> <TextBlock TextTrimming="CharacterEllipsis" Text="{Binding Path=Text, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"> </TextBlock> </ContentPresenter.Content> </ContentPresenter> <TextBox x:Name="EditableTextBox" Style="{x:Null}" Template="{StaticResource ComboBoxTextBox}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="3,3,23,3" Focusable="True" Background="Transparent" Visibility="Hidden" IsReadOnly="{TemplateBinding IsReadOnly}"/> <Popup Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide"> <Grid Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}"> <Border x:Name="DropDownBorder" Background="{StaticResource WindowBackgroundBrush}" BorderThickness="1"/> <ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True" DataContext="{Binding}"> <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" /> </ScrollViewer> </Grid> </Popup> </Grid> <ControlTemplate.Triggers> <Trigger Property="HasItems" Value="false"> <Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/> </Trigger> <Trigger Property="IsGrouping" Value="true"> <Setter Property="ScrollViewer.CanContentScroll" Value="false"/> </Trigger> <Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true"> <Setter TargetName="DropDownBorder" Property="CornerRadius" Value="4"/> <Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/> </Trigger> <Trigger Property="IsEditable" Value="true"> <Setter Property="IsTabStop" Value="false"/> <Setter TargetName="EditableTextBox" Property="Visibility" Value="Visible"/> <Setter TargetName="Presenter" Property="Visibility" Value="Hidden"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </ComboBox.Template> </ComboBox> </Grid>
這裏的重點在於ItemTemplate裏的 checkbox 和 ComboBox.Template 裏的 ContentPresenter.Content –> TextBlock 這麼長的代碼其實主要是重寫這兩個地方, 改變控件原有的功能. 其中checkbox 的content 綁定的Title和IsSelected 是從綁定的數據源裏面提取. 一般itemsource綁定的都是IEnumerable類型, 因此要求你的數據源必定是一個不少具備這兩個屬性的實例的聚合.spa
public class MultipleCheckboxModel : ModelBase<MultipleCheckboxModel> { public Guid Id { get; set; } public string Title { get; set; } private bool _isSelected; public bool IsSelected { get { return _isSelected; } set { _isSelected = value; NotifyPropertyChanged(x => x.IsSelected); } } }
這裏面的ModelBase 繼承了INotifyPropertyChanged 用來實現數據聯動. NotifyErrorsChanged 也是ModelBase裏封裝的方法. 由於這個不是本文章的重點, 就不貼出來了, 你們能夠直接繼承 INotifyPropertyChanged 來實現聯動.code
#region Dependency Properties public IEnumerable<MultipleCheckboxModel> ItemsSource { get { return (IEnumerable<MultipleCheckboxModel>)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); SetText(); } } public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(MultipleCheckbox), new FrameworkPropertyMetadata(null, ItemsSourcePropertyChangedCallback)); private static void ItemsSourcePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { var multioleCheckbox = dependencyObject as MultipleCheckbox; if (multioleCheckbox == null) return; multioleCheckbox.CheckableCombo.ItemsSource = multioleCheckbox.ItemsSource; } public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MultipleCheckbox), new FrameworkPropertyMetadata("", TextPropertyChangedCallback)); private static void TextPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { var multioleCheckbox = dependencyObject as MultipleCheckbox; if (multioleCheckbox == null) return; } public string DefaultText { get { return (string)GetValue(DefaultTextProperty); } set { SetValue(DefaultTextProperty, value); } } public static readonly DependencyProperty DefaultTextProperty = DependencyProperty.Register("DefaultText", typeof(string), typeof(MultipleCheckbox), new UIPropertyMetadata(string.Empty)); #endregion
依賴屬性主要就這三個 使用時能夠直接在view端進行binding沒什麼好說的orm
#region Event private void Checkbox_OnClick(object sender, RoutedEventArgs e) { var checkbox = sender as CheckBox; if (checkbox == null) return; if ((string) checkbox.Content == "All") { Text = ""; if (checkbox.IsChecked != null && checkbox.IsChecked.Value) { ItemsSource.ForEach(x => { x.IsSelected = true; Text = "All"; }); } else { ItemsSource.ForEach(x => { x.IsSelected = false; Text = "None"; }); } } else { SetText(); } } #endregion
這裏的All 就是實現全選功能的, 感受實現方式有些low, (。>ㅅ<。) 主要是時間有點急, 稍後慢慢優化.blog
而後是前面用到的私有方法繼承
#region Private Method private void SetText() { Text = ""; var all = ItemsSource.FirstOrDefault(x => x.Title == "All"); foreach (var item in ItemsSource) { if (item.IsSelected && item.Title != "All") { Text += item.Title + ","; } else if(all != null) { if (all.IsSelected) all.IsSelected = false; } } Text = string.IsNullOrEmpty(Text) ? DefaultText : Text.TrimEnd(new[] { ',' }); } #endregion
至此控件功能完成了事件
<user:MultipleCheckbox DefaultText="Choose" ItemsSource="{Binding OtBeds}" />
private ObservableCollection<MultipleCheckboxModel> _otBeds; public ObservableCollection<MultipleCheckboxModel> OtBeds { get { return _otBeds ?? (_otBeds = new ObservableCollection<MultipleCheckboxModel>()); } } // 初始化OtBeds的時候 不要忘了加一個 OtBeds.Add(new MultipleCheckboxModel {Id = Guid.Empty, Title = "All", IsSelected = true}); //慘不忍睹 這個應該寫在 控件裏面
<UserControl.Resources> <LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1"> <GradientBrush.GradientStops> <GradientStopCollection> <GradientStop Color="#FFF" Offset="0.0"/> <GradientStop Color="#CCC" Offset="1.0"/> </GradientStopCollection> </GradientBrush.GradientStops> </LinearGradientBrush> <LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1"> <GradientBrush.GradientStops> <GradientStopCollection> <GradientStop Color="#CCC" Offset="0.0"/> <GradientStop Color="#444" Offset="1.0"/> </GradientStopCollection> </GradientBrush.GradientStops> </LinearGradientBrush> <SolidColorBrush x:Key="GlyphBrush" Color="#444" /> <LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1"> <GradientBrush.GradientStops> <GradientStopCollection> <GradientStop Color="#FFF" Offset="0.0"/> <GradientStop Color="#AAA" Offset="1.0"/> </GradientStopCollection> </GradientBrush.GradientStops> </LinearGradientBrush> <LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1"> <GradientBrush.GradientStops> <GradientStopCollection> <GradientStop Color="#BBB" Offset="0.0"/> <GradientStop Color="#EEE" Offset="0.1"/> <GradientStop Color="#EEE" Offset="0.9"/> <GradientStop Color="#FFF" Offset="1.0"/> </GradientStopCollection> </GradientBrush.GradientStops> </LinearGradientBrush> <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" /> <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" /> <SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" /> <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" /> <SolidColorBrush x:Key="ComboBox.Static.Border" Color="#FFACACAC"/> <SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="#FFFFFFFF"/> <SolidColorBrush x:Key="ComboBox.Static.Editable.Border" Color="#FFABADB3"/> <SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Background" Color="Transparent"/> <SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Border" Color="Transparent"/> <SolidColorBrush x:Key="ComboBox.MouseOver.Glyph" Color="#FF000000"/> <LinearGradientBrush x:Key="ComboBox.MouseOver.Background" EndPoint="0,1" StartPoint="0,0"> <GradientStop Color="#FFECF4FC" Offset="0.0"/> <GradientStop Color="#FFDCECFC" Offset="1.0"/> </LinearGradientBrush> <SolidColorBrush x:Key="ComboBox.MouseOver.Border" Color="#FF7EB4EA"/> <SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Background" Color="#FFFFFFFF"/> <SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Border" Color="#FF7EB4EA"/> <LinearGradientBrush x:Key="ComboBox.MouseOver.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0"> <GradientStop Color="#FFEBF4FC" Offset="0.0"/> <GradientStop Color="#FFDCECFC" Offset="1.0"/> </LinearGradientBrush> <SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Button.Border" Color="#FF7EB4EA"/> <SolidColorBrush x:Key="ComboBox.Pressed.Glyph" Color="#FF000000"/> <LinearGradientBrush x:Key="ComboBox.Pressed.Background" EndPoint="0,1" StartPoint="0,0"> <GradientStop Color="#FFDAECFC" Offset="0.0"/> <GradientStop Color="#FFC4E0FC" Offset="1.0"/> </LinearGradientBrush> <SolidColorBrush x:Key="ComboBox.Pressed.Border" Color="#FF569DE5"/> <SolidColorBrush x:Key="ComboBox.Pressed.Editable.Background" Color="#FFFFFFFF"/> <SolidColorBrush x:Key="ComboBox.Pressed.Editable.Border" Color="#FF569DE5"/> <LinearGradientBrush x:Key="ComboBox.Pressed.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0"> <GradientStop Color="#FFDAEBFC" Offset="0.0"/> <GradientStop Color="#FFC4E0FC" Offset="1.0"/> </LinearGradientBrush> <SolidColorBrush x:Key="ComboBox.Pressed.Editable.Button.Border" Color="#FF569DE5"/> <SolidColorBrush x:Key="ComboBox.Disabled.Glyph" Color="#FFBFBFBF"/> <SolidColorBrush x:Key="ComboBox.Disabled.Background" Color="#FFF0F0F0"/> <SolidColorBrush x:Key="ComboBox.Disabled.Border" Color="#FFD9D9D9"/> <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Background" Color="#FFFFFFFF"/> <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Border" Color="#FFBFBFBF"/> <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="Transparent"/> <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="Transparent"/> <SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF3C77DD"/> <ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox"> <Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" /> </ControlTemplate> <Style x:Key="ComboBoxToggleButton" TargetType="ToggleButton"> <Setter Property="OverridesDefaultStyle" Value="true"/> <Setter Property="IsTabStop" Value="false"/> <Setter Property="Focusable" Value="false"/> <Setter Property="ClickMode" Value="Press"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ToggleButton}"> <Border x:Name="templateRoot" CornerRadius="5" BorderBrush="{StaticResource ComboBox.Static.Border}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true" Background="White"> <Border x:Name="splitBorder" BorderBrush="Transparent" BorderThickness="1" HorizontalAlignment="Right" Margin="0,0,2,0" SnapsToDevicePixels="true" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"> <Path x:Name="arrow" Data="F1 M 0,0 L 3.667,2.66665 L 7.3334,0 L 7.3334,-1.78168 L 3.6667,0.88501 L0,-1.78168 L0,0 Z" Fill="{StaticResource ComboBox.Static.Glyph}" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </Border> <ControlTemplate.Triggers> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/> <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="true"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Static.Editable.Background}"/> <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Static.Editable.Border}"/> <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Static.Editable.Button.Background}"/> <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Static.Editable.Button.Border}"/> </MultiDataTrigger> <Trigger Property="IsMouseOver" Value="true"> <Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.MouseOver.Glyph}"/> </Trigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Background}"/> <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Border}"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Editable.Background}"/> <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Editable.Border}"/> <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.MouseOver.Editable.Button.Background}"/> <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.MouseOver.Editable.Button.Border}"/> </MultiDataTrigger> <Trigger Property="IsPressed" Value="true"> <Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.Pressed.Glyph}"/> </Trigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Background}"/> <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Border}"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Editable.Background}"/> <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Editable.Border}"/> <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Pressed.Editable.Button.Background}"/> <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Pressed.Editable.Button.Border}"/> </MultiDataTrigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.Disabled.Glyph}"/> </Trigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Background}"/> <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Border}"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Editable.Background}"/> <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Editable.Border}"/> <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Disabled.Editable.Button.Background}"/> <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Disabled.Editable.Button.Border}"/> </MultiDataTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="{x:Type ComboBoxItem}" TargetType="ComboBoxItem"> <Setter Property="SnapsToDevicePixels" Value="true"/> <Setter Property="OverridesDefaultStyle" Value="true"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ComboBoxItem"> <Border Name="Border" Padding="2" SnapsToDevicePixels="true"> <ContentPresenter /> </Border> <ControlTemplate.Triggers> <Trigger Property="IsHighlighted" Value="true"> <Setter TargetName="Border" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources>