标签:closed on() bottom lte image rgs protected contain csharp
实现思路:
1.继承ComboBox
2.重写ComboBox的模板,把列表控件替换成树形控件
3.重写SelectedItem, SelectedValue,DisplayMemberPath,SelectedValuePath
效果截图:

XAML代码
<!--TreeSelect普通样式-->
<Style x:Key="DefaultTreeSelectStyle" TargetType="{x:Type controls:TreeSelect}">
<Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
<Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="{DynamicResource TextBoxBorderBrush}" />
<Setter Property="controls:ControlAttachProperty.FocusBackground" Value="Transparent" />
<Setter Property="controls:ControlAttachProperty.FocusBorderBrush" Value="{DynamicResource TextBoxFocusBorderBrush}" />
<Setter Property="controls:ControlAttachProperty.MouseOverBorderBrush" Value="{DynamicResource TextBoxMouseOverBorderBrush}" />
<Setter Property="controls:ControlAttachProperty.PopupBackground" Value="{DynamicResource WhiteBrush}" />
<Setter Property="FontFamily" Value="{DynamicResource ContentFontFamily}" />
<Setter Property="FontSize" Value="{DynamicResource ContentFontSize}" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="MaxDropDownHeight" Value="200" />
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
<Setter Property="MinHeight" Value="22" />
<Setter Property="Padding" Value="2" />
<Setter Property="ItemTemplate">
<Setter.Value>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel MinHeight="22" Orientation="Horizontal" Background="Transparent" HorizontalAlignment="Left" >
<TextBlock Text="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
</StackPanel>
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:TreeSelect}">
<Grid x:Name="PART_Root">
<Border x:Name="Bg" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" />
<Grid x:Name="PART_InnerGrid" Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="21" />
</Grid.ColumnDefinitions>
<!--Label区域-->
<ContentControl x:Name="Label" Template="{TemplateBinding controls:ControlAttachProperty.LabelTemplate}" IsTabStop="False" IsHitTestVisible="False"
Content="{TemplateBinding controls:ControlAttachProperty.Label}" Margin="1,1,0,1"/>
<!--附加内容区域-->
<Border x:Name="PART_AttachContent" Panel.ZIndex="2" Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Center" >
<ContentControl VerticalAlignment="Center" VerticalContentAlignment="Center" Template="{TemplateBinding controls:ControlAttachProperty.AttachContent}" />
</Border>
<!--下拉按钮-->
<ToggleButton x:Name="PART_DropDownToggle" Panel.ZIndex="1" IsTabStop="False" Style="{StaticResource ComboToggleButton}"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Grid.Column="1" Grid.ColumnSpan="3" IsEnabled="{Binding Path=IsReadOnly,RelativeSource={RelativeSource TemplatedParent},
Converter={x:Static controls:XConverter.TrueToFalseConverter},Mode=OneWay}" Margin="2 1 10 1"
Background="{TemplateBinding controls:ControlAttachProperty.FocusBackground}"/>
<!--水印-->
<Border Grid.Column="1">
<TextBlock x:Name="Message" Padding="0" Visibility="Collapsed" Text="{TemplateBinding controls:ControlAttachProperty.Watermark}"
Foreground="{TemplateBinding Foreground}" IsHitTestVisible="False" Opacity="0.6" HorizontalAlignment="Left" TextAlignment="Center"
VerticalAlignment="Center" Margin="5,2,5,2" />
</Border>
<!--内容区-->
<Grid Grid.Column="1" Margin="2 0 0 0">
<!--文本编辑-->
<TextBox x:Name="PART_EditableTextBox" Style="{StaticResource EditableTextBoxStyle}" FontSize="{TemplateBinding FontSize}"
HorizontalAlignment="Stretch" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" IsHitTestVisible="True"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
IsReadOnly="{TemplateBinding IsReadOnly}" FontFamily="{TemplateBinding FontFamily}" Foreground="{TemplateBinding Foreground}"
Text="{Binding Path=Text,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}" />
</Grid>
<!--弹出多选列表-->
<Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" StaysOpen="False"
IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Grid Width="{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}" MaxHeight="{Binding MaxDropDownHeight, RelativeSource={RelativeSource TemplatedParent}}">
<Border x:Name="PopupBorder" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="Stretch"
Height="Auto" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding controls:ControlAttachProperty.PopupBackground}"/>
<controls:ExtendedTreeView x:Name="PART_TreeView" Margin="2" ItemsSource="{Binding ItemsSource,RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{TemplateBinding MaxDropDownHeight}" ItemTemplate="{TemplateBinding ItemTemplate}"
Style="{StaticResource DefaultMetroTreeView}">
</controls:ExtendedTreeView>
</Grid>
</Popup>
</Grid>
</Grid>
<!--触发器-->
<ControlTemplate.Triggers>
<!--1.显示水印-->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="">
<Setter TargetName="Message" Property="Visibility" Value="Visible" />
</DataTrigger>
<!--编辑模式-->
<Trigger Property="IsEditable" Value="True">
<Setter TargetName="PART_DropDownToggle" Property="Grid.Column" Value="3" />
<Setter TargetName="PART_DropDownToggle" Property="Grid.ColumnSpan" Value="1" />
<Setter TargetName="PART_DropDownToggle" Property="Background" Value="Transparent" />
<Setter Property="IsTabStop" Value="false" />
<Setter TargetName="PART_DropDownToggle" Property="Focusable" Value="False" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="{Binding Path=(controls:ControlAttachProperty.MouseOverBorderBrush),RelativeSource={RelativeSource Self}}"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="{Binding Path=(controls:ControlAttachProperty.FocusBorderBrush),RelativeSource={RelativeSource Self}}"/>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="BorderBrush" Value="{Binding Path=(controls:ControlAttachProperty.FocusBorderBrush),RelativeSource={RelativeSource Self}}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="PART_Root" Property="Opacity" Value="{DynamicResource DisableOpacity}"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
cs代码
/// <summary> /// TreeSelect.xaml 的交互逻辑 /// </summary> [TemplatePart(Name = "PART_TreeView", Type = typeof(TreeView))] public partial class TreeSelect : ComboBox { public bool IsMulti { get { return (bool)GetValue(IsMultiProperty); } set { SetValue(IsMultiProperty, value); } } public static readonly DependencyProperty IsMultiProperty = DependencyProperty.Register("IsMulti", typeof(bool), typeof(TreeSelect), new PropertyMetadata(false)); public new string DisplayMemberPath { get { return (string)GetValue(DisplayMemberPathProperty); } set { SetValue(DisplayMemberPathProperty, value); } } public new static readonly DependencyProperty DisplayMemberPathProperty = DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(TreeSelect)); public new string SelectedValuePath { get { return (string)GetValue(SelectedValuePathProperty); } set { SetValue(SelectedValuePathProperty, value); } } public new static readonly DependencyProperty SelectedValuePathProperty = DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(TreeSelect)); /// <summary> /// Selected item of the TreeView /// </summary> public new object SelectedItem { get { return (object)GetValue(SelectedItemProperty); } set { SetValue(SelectedItemProperty, value); } } public new static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(TreeSelect), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedItemChanged))); private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ((TreeSelect)sender).UpdateSelectedItem(); } public new object SelectedValue { get { return (object)GetValue(SelectedValueProperty); } set { SetValue(SelectedValueProperty, value); } } public new static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(object), typeof(TreeSelect), new PropertyMetadata(null)); public new string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(TreeSelect)); /// <summary> /// Gets or sets text separator. /// </summary> public string TextSeparator { get { return (string)GetValue(TextSeparatorProperty); } set { SetValue(TextSeparatorProperty, value); } } public static readonly DependencyProperty TextSeparatorProperty = DependencyProperty.Register("TextSeparator", typeof(string), typeof(TreeSelect), new PropertyMetadata(",")); /// <summary> /// Gets or sets max text length. /// </summary> public int? MaxTextLength { get { return (int?)GetValue(MaxTextLengthProperty); } set { SetValue(MaxTextLengthProperty, value); } } public static readonly DependencyProperty MaxTextLengthProperty = DependencyProperty.Register("MaxTextLength", typeof(int?), typeof(TreeSelect)); /// <summary> /// Gets or sets text filler when text length exceeded. /// </summary> public string ExceededTextFiller { get { return (string)GetValue(ExceededTextFillerProperty); } set { SetValue(ExceededTextFillerProperty, value); } } public static readonly DependencyProperty ExceededTextFillerProperty = DependencyProperty.Register("ExceededTextFiller", typeof(string), typeof(TreeSelect), new PropertyMetadata("...")); static TreeSelect() { DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeSelect), new FrameworkPropertyMetadata(typeof(TreeSelect))); } public TreeSelect() { Loaded -= TreeSelect_Loaded; Loaded += TreeSelect_Loaded; SizeChanged -= TreeSelect_SizeChanged; SizeChanged += TreeSelect_SizeChanged; } private ExtendedTreeView _treeView; public override void OnApplyTemplate() { base.OnApplyTemplate(); this._treeView = Template.FindName("PART_TreeView", this) as ExtendedTreeView; if (this._treeView != null) { //this._treeView.SelectedItemChanged += _TreeView_SelectedItemChanged; _treeView.OnHierarchyMouseUp += new MouseEventHandler(OnTreeViewHierarchyMouseUp); _treeView.AddHandler(System.Windows.Controls.TreeViewItem.SelectedEvent, new System.Windows.RoutedEventHandler(treeview_Selected)); } } //protected override void OnDropDownClosed(EventArgs e) //{ // base.OnDropDownClosed(e); // this.SelectedItem = _treeView.SelectedItem; // this.UpdateText(); //} //protected override void OnDropDownOpened(EventArgs e) //{ // base.OnDropDownOpened(e); // this.UpdateText(); //} /// <summary> /// Handles clicks on any item in the tree view /// </summary> private void OnTreeViewHierarchyMouseUp(object sender, MouseEventArgs e) { //This line isn‘t obligatory because it is executed in the OnDropDownClosed method, but be it so this.SelectedItem = _treeView.SelectedItem; this.UpdateText(); base.SelectedItem = this.SelectedItem; this.IsDropDownOpen = false; } private void treeview_Selected(object sender, RoutedEventArgs e) { TreeViewItem item = (e.OriginalSource as TreeViewItem); if (item != null) { item.BringIntoView(); } } protected override bool IsItemItsOwnContainerOverride(object item) { if (item is ComboBoxItem) return true; else return false; } protected override void PrepareContainerForItemOverride(DependencyObject element, object item) { var uie = element as FrameworkElement; if (!(item is ComboBoxItem)) { var textBinding = new Binding(DisplayMemberPath); textBinding.Source = item; uie.SetBinding(ContentPresenter.ContentProperty, textBinding); } base.PrepareContainerForItemOverride(element, item); } private void TreeSelect_SizeChanged(object sender, SizeChangedEventArgs e) { if (!IsLoaded) return; UpdateText(); } private void TreeSelect_Loaded(object sender, RoutedEventArgs e) { UpdateText(); } private void UpdateSelectedItem() { UpdateText(); base.SelectedItem = this.SelectedItem; if (this.SelectedItem == null || string.IsNullOrEmpty(this.SelectedValuePath)) { SelectedValue = null; } else { SelectedValue = this.SelectedItem.GetType().GetProperty(SelectedValuePath).GetValue(this.SelectedItem, null); } } private void _TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { if (!IsLoaded) return; UpdateText(); base.SelectedItem = this.SelectedItem; } private void UpdateText() { if (IsMulti == false) { Text = GenerateText(SelectedItem); } else { Text = GenerateText(); } } public string GenerateText(object selectedItem) { var text = ""; if (selectedItem == null) { text = ""; } else if (selectedItem is ComboBoxItem) { var msi = selectedItem as ComboBoxItem; text += msi.Content.ToString(); } else { if (!string.IsNullOrEmpty(DisplayMemberPath) && selectedItem.GetType().GetProperty(DisplayMemberPath) != null) text += selectedItem.GetType().GetProperty(DisplayMemberPath).GetValue(selectedItem, null).ToString(); else text += selectedItem.ToString(); if (selectedItem.GetType().GetProperty("IsSelected") != null) { selectedItem.GetType().GetProperty("IsSelected").SetValue(selectedItem, true); } } return text; } public string GenerateText() { var text = ""; var isFirst = true; foreach (var item in Items) { if (!isFirst) text += TextSeparator; else isFirst = false; if (item is ComboBoxItem) { var msi = item as ComboBoxItem; text += msi.Content.ToString(); } else { if (item.GetType().GetProperty("IsSelected") != null && item.GetType().GetProperty("IsSelected").GetValue(item, null).ToString() == "true") { if (!string.IsNullOrEmpty(DisplayMemberPath) && item.GetType().GetProperty(DisplayMemberPath) != null) text += item.GetType().GetProperty(DisplayMemberPath).GetValue(item, null).ToString(); else text += item.ToString(); } } if (MaxTextLength == null) { if (!ValidateStringWidth(text + ExceededTextFiller)) { if (text.Length == 0) return null; text = text.Remove(text.Length - 1); while (!ValidateStringWidth(text + ExceededTextFiller)) { if (text.Length == 0) return null; text = text.Remove(text.Length - 1); } return text + ExceededTextFiller; } } else if (text.Length >= MaxTextLength) { return text.Cut((int)MaxTextLength, ExceededTextFiller); } } return text; } private bool ValidateStringWidth(string text) { var size = MeasureString(text); if (size.Width > (ActualWidth - Padding.Left - Padding.Right - 30)) return false; else return true; } private Size MeasureString(string candidate) { var formattedText = new FormattedText(candidate, System.Globalization.CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(FontFamily, FontStyle, FontWeight, FontStretch), FontSize, Brushes.Black, new NumberSubstitution(), TextFormattingMode.Display); return new Size(formattedText.Width, formattedText.Height); } }
使用:
<util:TreeSelect
Width="200"
ItemsSource="{Binding Data}"
SelectedItem="{Binding SelectedData}"
ItemTemplate="{x:Null}"
DisplayMemberPath="Name" xmlns:util="https://astudio.github.io/utilcontrol">
<util:TreeSelect.Resources>
<HierarchicalDataTemplate
DataType="{x:Type viewmodel:Person}"
ItemsSource="{Binding Path=Children}">
<StackPanel
Orientation="Horizontal">
<Path
x:Name="IconPath"
Width="18"
Height="18"
Stretch="Fill"
Fill="Black"
Data="F1 M 24.0033,56.0078L 24.0033,38.0053L 22.0031,40.0056L 19.0027,35.0049L 38.0053,20.0028L 45.0063,25.5299L 45.0063,21.753L 49.0068,21.0029L 49.0068,28.6882L 57.008,35.0049L 54.0075,40.0056L 52.0073,38.0053L 52.0073,56.0078L 24.0033,56.0078 Z M 38.0053,26.9204L 27.0038,36.005L 27.0038,53.0074L 33.0046,53.0074L 33.0046,42.006L 43.006,42.006L 43.006,53.0074L 49.0068,53.0074L 49.0068,36.005L 38.0053,26.9204 Z " />
<Grid
Margin="2,0,2,0">
<TextBlock
x:Name="txtName"
Text="{Binding Name, Mode=TwoWay}"
Width="Auto" />
</Grid>
</StackPanel>
</HierarchicalDataTemplate>
</util:TreeSelect.Resources>
</util:TreeSelect>
完成,这个还支持多选的,没有完全完成,待续。
标签:closed on() bottom lte image rgs protected contain csharp
原文地址:https://www.cnblogs.com/akwkevin/p/14199966.html