Quellcode durchsuchen

xls:增加搜索下拉框

xulisong vor 6 Jahren
Ursprung
Commit
cb8ce38732

+ 3 - 0
MBI/FirmLib/Com.FirmLib.UI/Common/CommonStyles.cs

@@ -18,6 +18,7 @@ namespace Com.FirmLib.UI.Common
             LinkButtonKey = new ComponentResourceKey(typeof(CommonStyles), "LinkButton");
             ComboBoxKey = new ComponentResourceKey(typeof(CommonStyles), "ComboBox");
             DataGridCellKey = new ComponentResourceKey(typeof(CommonStyles), "DataGridCell");
+            ControlKey = new ComponentResourceKey(typeof(CommonStyles), nameof(ControlKey));
         }
         public static ResourceKey StarLableKey { get; private set; }
         public static ResourceKey TextBoxKey { get; private set; }
@@ -28,5 +29,7 @@ namespace Com.FirmLib.UI.Common
         public static ResourceKey ComboBoxKey { get; private set; }
 
         public static ResourceKey DataGridCellKey { get; private set; }
+
+        public static ResourceKey ControlKey { get; private set; }
     }
 }

+ 12 - 3
MBI/FirmLib/Com.FirmLib.UI/Manufacturer/WinProductEditor.xaml

@@ -4,7 +4,9 @@
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:fw="http://schemas.FWind/xaml"
-             xmlns:uc="clr-namespace:Com.FirmLib.UI.Common" Name="this"
+             xmlns:uc="clr-namespace:Com.FirmLib.UI.Common"            
+             xmlns:wpf="clr-namespace:SAGA.DotNetUtils.WPF;assembly=SAGA.DotNetUtils"
+             Name="this"
              Title="添加产品" Width="270" SizeToContent="Height" WindowStartupLocation="CenterOwner">
     <Grid Margin="5" Validation.ErrorTemplate="{StaticResource ResourceKey={x:Static fw:ResourceKeys.EmptyErrorTemplateKey}}">
         <Grid.RowDefinitions>
@@ -40,8 +42,15 @@
             </TextBox>
             <Label  Style="{StaticResource ResourceKey={x:Static uc:CommonStyles.StarLableKey}}" Content="所属设备族:"></Label>
 
-            <ComboBox ItemsSource="{Binding Families,Mode=OneWay}" DisplayMemberPath="Name" SelectedValuePath="Value" SelectedValue="{Binding CurrentFamily,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ResourceKey={x:Static uc:CommonStyles.ComboBoxKey}}" Margin="5">
-            </ComboBox>
+            <wpf:SComboBox ItemsSource="{Binding Families,Mode=OneWay}" DisplayMemberPath="Name" SelectedValuePath="Value" Style="{StaticResource ResourceKey={x:Static uc:CommonStyles.ControlKey}}" Margin="5">
+                <wpf:SComboBox.SelectedValue>
+                    <Binding Path="CurrentFamily" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
+                        <Binding.ValidationRules>
+                            <fw:RequireValidationRule ></fw:RequireValidationRule>
+                        </Binding.ValidationRules>
+                    </Binding>
+                </wpf:SComboBox.SelectedValue>
+            </wpf:SComboBox>
         </StackPanel>
         
 

+ 6 - 0
MBI/FirmLib/Com.FirmLib.UI/Themes/CommonStyle.xaml

@@ -135,4 +135,10 @@
             </Trigger>
         </Style.Triggers>
     </Style>
+    <Style x:Key="{x:Static common:CommonStyles.ControlKey}"  TargetType="{x:Type Control}">
+        <Setter Property="BorderThickness" Value="1"></Setter>
+        <Setter Property="BorderBrush" Value="Black"></Setter>
+        <Setter Property="Background" Value="White"></Setter>
+   
+    </Style>
 </ResourceDictionary>

+ 36 - 0
MBI/SAGA.DotNetUtils/Extend/DictionaryExtensions.cs

@@ -0,0 +1,36 @@
+/*-------------------------------------------------------------------------
+ * 功能描述:DictionaryExtensions
+ * 作者:xulisong
+ * 创建时间: 2019/6/6 11:29:24
+ * 版本号:v1.0
+ *  -------------------------------------------------------------------------*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SAGA.DotNetUtils.Extend
+{
+    public static class DictionaryExtensions
+    {
+        /// <summary>
+        /// 增加字典分组值
+        /// </summary>
+        /// <typeparam name="K"></typeparam>
+        /// <typeparam name="V"></typeparam>
+        /// <param name="dic"></param>
+        /// <param name="key"></param>
+        /// <param name="value"></param>
+        public static void AddGroupValue<K, V>(this Dictionary<K, List<V>> dic, K key, V value)
+        {
+            if (!dic.TryGetValue(key, out List<V> values))
+            {
+                values = new List<V>();
+                dic.Add(key, values);
+            }
+            values.Add(value);
+        }
+    }
+}

+ 6 - 0
MBI/SAGA.DotNetUtils/SAGA.DotNetUtils.csproj

@@ -311,6 +311,7 @@
     <Compile Include="Extend\BitmapExtend.cs" />
     <Compile Include="Extend\ConfigurationUtil.cs" />
     <Compile Include="Extend\DateTimeExtend.cs" />
+    <Compile Include="Extend\DictionaryExtensions.cs" />
     <Compile Include="Extend\DoubleExt.cs" />
     <Compile Include="Extend\EnumExtends.cs" />
     <Compile Include="Extend\FilePathExt.cs" />
@@ -498,6 +499,7 @@
       <DependentUpon>EditSaveTextbox.xaml</DependentUpon>
     </Compile>
     <Compile Include="WPF\UserControl\ITextInputControl.cs" />
+    <Compile Include="WPF\UserControl\SComboBox.cs" />
     <Compile Include="WPF\UserControl\SearchInputEditor.cs" />
     <Compile Include="WPF\UserControl\SelectFile_Hyperlink.xaml.cs">
       <DependentUpon>SelectFile_Hyperlink.xaml</DependentUpon>
@@ -545,6 +547,10 @@
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </Page>
+    <Page Include="Themes\SComboBox.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
     <Page Include="Themes\SearchInputEditor.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>

+ 1 - 1
MBI/SAGA.DotNetUtils/Themes/Generic.xaml

@@ -6,7 +6,7 @@
     <ResourceDictionary.MergedDictionaries >
         <ResourceDictionary Source="/SAGA.DotNetUtils;component/Themes/SearchInputEditor.xaml"/>
         <ResourceDictionary Source="/SAGA.DotNetUtils;component/Themes/DataGridStyle.xaml"/>
-        
+        <ResourceDictionary Source="/SAGA.DotNetUtils;component/Themes/SComboBox.xaml"/>
     </ResourceDictionary.MergedDictionaries>
     <!-- TreeViewItem styles -->
     <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>

+ 181 - 0
MBI/SAGA.DotNetUtils/Themes/SComboBox.xaml

@@ -0,0 +1,181 @@
+<ResourceDictionary
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:local="clr-namespace:SAGA.DotNetUtils.WPF">
+
+    <LinearGradientBrush x:Key="TActiveButtonBrush" StartPoint="0,0" EndPoint="0,1">
+        <LinearGradientBrush.GradientStops>
+            <GradientStop Color="#EAF6FD" Offset="0.15" />
+            <GradientStop Color="#D9F0FC" Offset=".5" />
+            <GradientStop Color="#BEE6FD" Offset=".5" />
+            <GradientStop Color="#A7D9F5" Offset="1" />
+        </LinearGradientBrush.GradientStops>
+    </LinearGradientBrush>
+
+    <SolidColorBrush x:Key="TActiveBorderBrush" Color="DarkBlue" />
+
+    <!-- Style for a ListBoxItem displayed inside the embedded list box, to make it a check box when mode = multiple. -->
+    <Style x:Key="TCheckBoxedItemStyleKey" TargetType="{x:Type ListBoxItem}">
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="{x:Type ListBoxItem}">
+                    <Border Background="Transparent">
+                        <CheckBox Focusable="false" 
+                                  Foreground="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Foreground}"
+                                  BorderBrush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Control}}, Path=Foreground}"                                           
+                                  Content="{TemplateBinding ContentPresenter.Content}"  
+                                  IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
+                    </Border>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <!-- Style for a ListBoxItem displayed inside the embedded list box, when mode = single -->
+    <Style x:Key="TNormalItemStyleKey" TargetType="{x:Type ListBoxItem}">
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="{x:Type ListBoxItem}">
+                    <Border Name="border" 
+                            Margin="0,-1"
+                            Background="{TemplateBinding Background}" 
+                            BorderBrush="{TemplateBinding BorderBrush}" 
+                            BorderThickness="1"                            
+                            SnapsToDevicePixels="true">
+                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
+                    </Border>
+                    <ControlTemplate.Triggers>
+                        <Trigger Property="IsMouseOver" Value="True">
+                            <Setter Property="BorderBrush" Value="{x:Static SystemColors.HighlightBrush}" />
+                        </Trigger>
+                        <Trigger Property="IsSelected" Value="True">
+                            <Setter Property="Background" Value="{x:Static SystemColors.HighlightBrush}" />
+                            <Setter Property="Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
+                        </Trigger>
+                    </ControlTemplate.Triggers>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+    <Style x:Key="TFocusVisualStyle">
+        <Setter Property="Control.Template">
+            <Setter.Value>
+                <ControlTemplate>
+                    <Border>
+                        <Rectangle 
+                            Margin="2"
+                            StrokeThickness="1"
+                            Stroke="#b0000000"
+                            StrokeDashArray="1 2"/>
+                    </Border>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+    <!-- Template for the toggle button which is the main view of the control -->
+    <ControlTemplate x:Key="SComboBoxButtonTemplate" TargetType="{x:Type Button}">
+        <Border BorderThickness="{TemplateBinding BorderThickness}"  
+                    Background="{TemplateBinding Background}" 
+                    BorderBrush="{TemplateBinding BorderBrush}"
+                    SnapsToDevicePixels="true">
+            <Grid>
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition />
+                    <ColumnDefinition Width="Auto"/>
+                </Grid.ColumnDefinitions>
+
+                <ContentPresenter Content="{TemplateBinding Content}" />
+
+                <Border Name="arrowBorder" Grid.Column="1" Width="16" BorderBrush="{TemplateBinding BorderBrush}">
+                    <Path HorizontalAlignment="Center" VerticalAlignment="Center" 
+                                      Fill="{TemplateBinding BorderBrush}" Data="M 0 0 L 7 0 L 3.5 4 Z" />
+                </Border>
+            </Grid>
+        </Border>
+        <ControlTemplate.Triggers>
+
+            <Trigger Property="IsMouseOver" Value="True">
+                <Setter TargetName="arrowBorder" Property="Background" Value="{StaticResource TActiveButtonBrush}" />
+                <Setter TargetName="arrowBorder" Property="BorderThickness" Value="1,0,0,0" />
+            </Trigger>
+            <!--<Trigger Property="IsChecked" Value="True">
+                <Setter TargetName="arrowBorder" Property="Background" Value="{StaticResource ActiveButtonBrush}" />
+                <Setter TargetName="arrowBorder" Property="BorderThickness" Value="1,0,0,0" />
+            </Trigger>-->
+        </ControlTemplate.Triggers>
+    </ControlTemplate>
+
+    <ControlTemplate x:Key="SComboBoxTemplate" TargetType="{x:Type local:SComboBox}">
+        <Grid >
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="*"></ColumnDefinition>
+                <ColumnDefinition Width="Auto"></ColumnDefinition>
+            </Grid.ColumnDefinitions>
+            <TextBox IsReadOnly="{Binding RelativeSource={RelativeSource TemplatedParent},Path=IsReadOnly}" BorderThickness="1,1,0,1" BorderBrush="Black" Name="PART_SearchText"  Margin="1,0,0,0" Grid.Column="0" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text,Mode=TwoWay}"></TextBox>
+            <Button Grid.Column="1" Name="PART_Button" IsTabStop="False"
+                          Background="{TemplateBinding Background}"
+                          BorderBrush="{TemplateBinding BorderBrush}"
+                          BorderThickness="{TemplateBinding BorderThickness}"
+                          Template="{StaticResource SComboBoxButtonTemplate}" 
+                          >
+            </Button>
+
+            <Popup Name="PART_Popup" 
+                   StaysOpen="False"
+                   AllowsTransparency="True" 
+                   Placement="Bottom"                 
+                   IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsDropDownOpen,Mode=TwoWay}" 
+                   FocusManager.IsFocusScope="True"
+                   FocusManager.FocusedElement="{Binding ElementName=List}"
+                   PopupAnimation="Slide">
+                <Border Name="Shadow"  
+                                              MaxHeight="{TemplateBinding MaxDropDownHeight}" 
+                                              MinWidth="{TemplateBinding ActualWidth}">
+                    <Border BorderBrush="{TemplateBinding BorderBrush}" 
+                        BorderThickness="{TemplateBinding BorderThickness}"
+                        Background="{TemplateBinding Background}">
+                        <StackPanel>
+                            <ScrollViewer MaxHeight="{TemplateBinding MaxDropDownHeight}" >
+                                <ItemsPresenter x:Name="List" Margin="{TemplateBinding Padding}" KeyboardNavigation.DirectionalNavigation="Contained"
+                                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
+                            </ScrollViewer>
+                        </StackPanel>
+                    </Border>
+                </Border>
+            </Popup>
+
+        </Grid>
+
+        <ControlTemplate.Triggers>
+            <Trigger SourceName="PART_Popup" Property="HasDropShadow" Value="true">
+                <Setter TargetName="Shadow" Property="Margin" Value="0,0,5,5" />
+                <!--<Setter TargetName="Shadow" Property="Color" Value="#71000000" />-->
+            </Trigger>
+            <Trigger Property="IsMouseOver" Value="True">
+                <Setter Property="BorderBrush" Value="{StaticResource TActiveBorderBrush}" />
+            </Trigger>
+            <!--<Trigger SourceName="toggleButton" Property="IsChecked" Value="True">
+                <Setter Property="BorderBrush" Value="{StaticResource ActiveBorderBrush}" />
+            </Trigger>-->
+        </ControlTemplate.Triggers>
+    </ControlTemplate>
+
+    <!-- Style which defines the MultiComboBox -->
+    <Style TargetType="{x:Type local:SComboBox}">
+        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
+        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
+        <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
+        <Setter Property="MinWidth" Value="120"/>
+        <Setter Property="MinHeight" Value="20"/>
+        <Setter Property="Foreground" Value="{x:Static SystemColors.WindowTextBrush}" />
+        <Setter Property="BorderBrush" Value="{x:Static SystemColors.WindowTextBrush}" />
+        <Setter Property="Background" Value="{x:Static SystemColors.WindowBrush}" />
+        <Setter Property="BorderThickness" Value="1" />
+        <Setter Property="IsTabStop" Value="True" />
+        <Setter Property="FocusVisualStyle" Value="{StaticResource TFocusVisualStyle}" />
+        <Setter Property="Template" Value="{StaticResource SComboBoxTemplate}" />
+        <Setter Property="ItemContainerStyle" Value="{StaticResource TNormalItemStyleKey}" />
+    </Style>
+
+</ResourceDictionary>

+ 19 - 0
MBI/SAGA.DotNetUtils/WPF/Extend/UIElementExtensions.cs

@@ -81,5 +81,24 @@ namespace SAGA.DotNetUtils.WPF.Extend
             }
             return list;
         }
+
+        /// <summary>
+        /// 获取元素的指定类型,
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="uiElement"></param>
+        /// <returns></returns>
+        public static T GetParentTypeSelf<T>(this Visual uiElement) where T : FrameworkElement
+        {
+            var result = uiElement as T;
+            if (result != null)
+                return result;
+
+            var d = VisualTreeHelper.GetParent(uiElement);
+            Visual visual = d as Visual;
+            if (visual != null)
+                result = visual.GetParentTypeSelf<T>();
+            return result;
+        }
     }
 }

+ 391 - 0
MBI/SAGA.DotNetUtils/WPF/UserControl/SCombobox.cs

@@ -0,0 +1,391 @@
+/*-------------------------------------------------------------------------
+ * 功能描述:SCombobox
+ * 作者:xulisong
+ * 创建时间: 2019/6/10 11:28:37
+ * 版本号:v1.0
+ *  -------------------------------------------------------------------------*/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Input;
+using SAGA.DotNetUtils.WPF.Extend;
+
+namespace SAGA.DotNetUtils.WPF
+{
+   
+    /// <summary>
+    /// 带搜索功能的comboBox
+    /// </summary>
+    ///     [TemplatePart(Name = "PART_textBoxNewItem", Type=typeof(TextBox))]
+    [TemplatePart(Name = "PART_SearchText", Type = typeof(TextBox))]
+    [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))]
+    [TemplatePart(Name = "PART_Button", Type = typeof(Button))]
+    public class SComboBox : ListBox
+    {
+        static SComboBox()
+        {
+            DefaultStyleKeyProperty.OverrideMetadata(typeof(SComboBox), new FrameworkPropertyMetadata(typeof(SComboBox)));
+        }
+        public SComboBox()
+        {
+            Keyboard.AddPreviewKeyDownHandler(this, OnKeyDown);
+            Keyboard.AddPreviewKeyUpHandler(this, OnKeyUp);
+            this.AddHandler(ListBoxItem.PreviewMouseDownEvent, new MouseButtonEventHandler(MouseButtonEventHandler));
+            this.IsTextSearchCaseSensitive = false;
+            this.IsTextSearchEnabled = false;
+            this.InEdit = false;
+            this.SetValue(KeyboardNavigation.DirectionalNavigationProperty, KeyboardNavigationMode.Contained);
+        }
+
+        private void MouseButtonEventHandler(object sender, MouseButtonEventArgs e)
+        {
+            var element = e.OriginalSource as UIElement;
+            if (element == null)
+                return;
+            var listItem = element.GetParentTypeSelf<ListBoxItem>();
+            if (listItem == null)
+                return;
+            if (listItem.DataContext == this.SelectedItem)
+            {
+                SyncSelectedItemText();
+            }
+        }
+
+        private void OnKeyDown(object sender, KeyEventArgs e)
+        {
+            if (e.Key == Key.Enter && IsDropDownOpen)
+            {
+                this.IsDropDownOpen = false;
+                if (this.SelectedItem != null)
+                {
+                    SyncSelectedItemText();
+                }
+                return;
+            }
+            if (!(Key.Up == e.Key || Key.Down == e.Key))
+
+            {
+                //EditableTextBox.Focus();
+            }
+            else
+            {
+                UIElement item = ItemContainerGenerator.ContainerFromItem(SelectedItem) as UIElement;
+                if ((item == null) && (Items.Count > 0))
+                    item = ItemContainerGenerator.ContainerFromItem(Items[0]) as UIElement;
+                if (item != null)
+                    item.Focus();
+            }
+        }
+        private void OnKeyUp(object sender, KeyEventArgs e)
+        {
+            EditableTextBox.Focus();
+        }
+        #region 属性相关
+        /// <summary>
+        /// 关联文本
+        /// </summary>
+        public static readonly DependencyProperty TextProperty =
+            DependencyProperty.Register("Text", typeof(string), typeof(SComboBox));
+        /// <summary>
+        /// 关联文本
+        /// </summary>
+        public string Text
+        {
+            get { return (string)GetValue(TextProperty); }
+            set { SetValue(TextProperty, value); }
+        }
+        /// <summary>
+        /// 是否下拉打开
+        /// </summary>
+        public static readonly DependencyProperty IsDropDownOpenProperty =
+            DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(SComboBox));
+        /// <summary>
+        /// 是否下拉打开
+        /// </summary>
+        public bool IsDropDownOpen
+        {
+            get { return (bool)GetValue(IsDropDownOpenProperty); }
+            set { SetValue(IsDropDownOpenProperty, value); }
+        }
+        /// <summary>
+        /// Dependency backing for the MaxDropDownHeight property.
+        /// </summary>
+        public static readonly DependencyProperty MaxDropDownHeightProperty =
+            ComboBox.MaxDropDownHeightProperty.AddOwner(typeof(SComboBox));
+        /// <summary>
+        /// Gets or sets a value indicating the maximium height of the drop down.
+        /// </summary>
+        public double MaxDropDownHeight
+        {
+            get { return (double)GetValue(MaxDropDownHeightProperty); }
+            set { SetValue(MaxDropDownHeightProperty, value); }
+        }
+        /// <summary>
+        /// 是否下拉打开
+        /// </summary>
+        public static readonly DependencyProperty IsReadOnlyProperty =
+            DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(SComboBox));
+        /// <summary>
+        /// 是否下拉打开
+        /// </summary>
+        public bool IsReadOnly
+        {
+            get { return (bool)GetValue(IsReadOnlyProperty); }
+            set { SetValue(IsReadOnlyProperty, value); }
+        }
+        #endregion
+        /*
+         * 选中项会在数据源发生变化时变化,所以要保证全局的选中项
+         */
+        /// <summary>
+        /// 真实的选中项
+        /// </summary>
+        private object RealSelectedItem { get; set; }
+        #region 内部编辑
+        private bool InEdit { get; set; }
+        public void BeginInEdit()
+        {
+            InEdit = true;
+        }
+
+        public void EndInEdit()
+        {
+            InEdit = false;
+        }
+        #endregion
+        #region 相关
+        /*
+         * 1、过滤过程中,对SelectedItem赋值,不要引起Text的变化
+         * 2、内部引发的Text事件,不触发过滤事件
+         * 3、编辑完成是离开控件
+         */
+        #endregion
+        private Popup Popup { get; set; }
+        protected TextBox EditableTextBox { get; set; }
+        public override void OnApplyTemplate()
+        {
+            base.OnApplyTemplate();
+            Popup = (Popup)this.Template.FindName("PART_Popup", this);
+            EditableTextBox = (TextBox)this.Template.FindName("PART_SearchText", this);
+            this.EditableTextBox.TextChanged += EditableTextBox_TextChanged;
+            this.EditableTextBox.LostFocus += EditableTextBox_LostFocus;
+
+            var toggle = (Button)this.Template.FindName("PART_Button", this);
+            toggle.Click += Toggle_Click;
+            this.PreviewMouseLeftButtonDown += Toggle_PreviewMouseLeftButtonDown;
+        }
+
+        private void Toggle_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+        {
+            m_IsOpen = this.IsDropDownOpen;
+        }
+
+        private bool m_IsOpen;
+        private void Toggle_Click(object sender, RoutedEventArgs e)
+        {
+            if (this.ItemsSource == null)
+                return;
+            GlobalFlag = true;
+            RefreshFilter();
+            SetSelectedItem();
+            this.IsDropDownOpen = !m_IsOpen;
+            //Popup.StaysOpen = false;
+        }
+
+        private static readonly DependencyProperty m_DisplayProperty =
+            DependencyProperty.Register("m_DisplayProperty", typeof(object), typeof(SComboBox));
+        private void EditableTextBox_LostFocus(object sender, RoutedEventArgs e)
+        {
+            GlobalFlag = true;
+            RefreshFilter();
+            SetSelectedItem();
+        }
+        private string GetDisplay( object item)
+        {       
+            BindingOperations.SetBinding(this, m_DisplayProperty, new Binding(DisplayMemberPath) { Source = item });
+            var propertyValue = GetValue(m_DisplayProperty);
+            BindingOperations.ClearBinding(this, m_DisplayProperty);
+            return propertyValue?.ToString()??string.Empty;
+        }
+        private void SetSelectedItem()
+        {
+            if (this.RealSelectedItem == null)
+            {
+                if (this.ItemsSource != null)
+                {
+                    foreach (var item in ItemsSource)
+                    {
+                        this.RealSelectedItem = item;//.FirstOrDefault();
+                        break;
+                    }
+                }
+
+            }
+            this.SelectedItem = this.RealSelectedItem;
+            try
+            {
+                BeginInEdit();
+                this.Text = GetDisplay(this.SelectedItem);
+            }
+            finally
+            {
+                EndInEdit();
+            }
+        }
+        private void EditableTextBox_TextChanged(object sender, TextChangedEventArgs e)
+        {
+            TextChanged();
+        }
+
+        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
+        {
+            try
+            {
+                BeginInEdit();
+                base.OnSelectionChanged(e);
+                if (!InSelected)
+                {
+                    this.Text = GetDisplay(this.SelectedItem);
+                    this.EditableTextBox.Select(this.Text.Length, 0);
+                    this.RealSelectedItem = this.SelectedItem;
+                }
+
+            }
+            finally
+            {
+                EndInEdit();
+            }
+
+        }
+
+        /// <summary>
+        /// 同步text显示
+        /// </summary>
+        private void SyncSelectedItemText()
+        {
+            try
+            {
+                BeginInEdit();
+                if (!InSelected)
+                {
+                    var text = GetDisplay(this.SelectedItem); 
+                    //this.Text = this.SelectedItem?.ToString() ?? string.Empty;
+                    this.EditableTextBox.Text = text;
+                    this.EditableTextBox.Select(text.Length, 0);
+                    this.RealSelectedItem = this.SelectedItem;
+                }
+            }
+            finally
+            {
+                EndInEdit();
+            }
+        }
+        #region 选项控制
+        /// <summary>
+        /// 是否是内部选择
+        /// </summary>
+        private bool InSelected { get; set; } = false;
+        private void SetSelectedItem(object item)
+        {
+            try
+            {
+                InSelected = true;
+                this.SelectedItem = item;
+            }
+            finally
+            {
+                InSelected = false;
+            }
+
+        }
+        #endregion
+        #region  过滤相关
+        private void RefreshFilter()
+        {
+            if (this.ItemsSource != null)
+            {
+                try
+                {
+                    BeginInEdit();
+                    InSelected = true;
+                    ICollectionView view = CollectionViewSource.GetDefaultView(this.ItemsSource);
+                    view.Refresh();
+                }
+                finally
+                {
+                    InSelected = false;
+                    EndInEdit();
+                }
+            }
+        }
+        /// <summary>
+        /// 全局过滤标志,为true 则不进行过滤,初始值为ture
+        /// </summary>
+        private bool GlobalFlag { get; set; } = true;
+        private bool FilterPredicate(object value)
+        {
+            if (GlobalFlag)
+                return true;
+            if (value == null)
+                return false;
+            var text = this.EditableTextBox.Text;
+            if (text.Length == 0)
+                return true;
+            string prefix = text;
+            return GetDisplay(value).Contains(prefix);
+        }
+        #endregion
+
+        protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
+        {
+            if (newValue != null)
+            {
+                ICollectionView view = CollectionViewSource.GetDefaultView(newValue);
+                view.Filter += this.FilterPredicate;
+            }
+
+            if (oldValue != null)
+            {
+                ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);
+                view.Filter -= this.FilterPredicate;
+            }
+            base.OnItemsSourceChanged(oldValue, newValue);
+        }
+
+        private void TextChanged()
+        {
+            if (!this.IsTextSearchEnabled && !this.InEdit)
+            {
+                var text = this.EditableTextBox.Text ?? string.Empty;
+                if (true || text.Length > 0)
+                {
+                    GlobalFlag = false;
+                    this.RefreshFilter();
+                    //Poupup.IsOpen = true;
+                    IsDropDownOpen = true;
+                    foreach (object item in CollectionViewSource.GetDefaultView(this.ItemsSource))
+                    {
+                        string display = item.ToString();
+                        SetSelectedItem(item);
+                        if (display == text)
+                        {
+                            this.RealSelectedItem = item;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+
+
+    }
+}

+ 63 - 31
MBI/SAGA.GplotRelationComputerManage/RevitSystemParse/Handler/GplotGraphParse.cs

@@ -12,6 +12,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Autodesk.Revit.DB;
 using SAGA.DotNetUtils.Data;
+using SAGA.DotNetUtils.Extend;
 using SAGA.GplotRelationComputerManage.SystemChecks;
 using SAGA.RevitUtils;
 using SAGA.RevitUtils.Data.Graph;
@@ -537,6 +538,24 @@ namespace SAGA.GplotRelationComputerManage
             #endregion
         }
 
+        #region 无向拓扑展开
+
+        private class VertexInfo
+        {
+            public VertexInfo()
+            { }
+
+            public VertexInfo(List<SystemEdge> edges)
+            {
+                AllEdges = edges;
+                CurrentDegree = edges.Count;
+            }
+            /// <summary>
+            /// 关联边
+            /// </summary>
+            public List<SystemEdge> AllEdges { get; set; } = new List<SystemEdge>();
+            public int CurrentDegree { get; set; }
+        }
         /// <summary>
         /// 确定流向
         /// </summary>
@@ -566,12 +585,13 @@ namespace SAGA.GplotRelationComputerManage
              * 2、根据起点遍历所有的节点,并调整节点的顺序
              */
             #endregion
+
+            var allVertexs = graph.GetBootVertexs();
             #region 获取定义端点
-            var vertexes = graph.GetBootVertexs().Where(v => SystemCalcUtil.IsStartValveName(v.GetEquipment()?.Name)).ToList();
+            var vertexes = allVertexs.Where(v => SystemCalcUtil.IsStartValveName(v.GetEquipment()?.Name)).ToList();
             if (!vertexes.Any())
                 return;
             //将顶点按前缀分组
-           // Group
             Dictionary<string, List<SystemVertex>> dicVertexes = new Dictionary<string, List<SystemVertex>>();
             foreach (var systemVertex in vertexes)
             {
@@ -586,40 +606,40 @@ namespace SAGA.GplotRelationComputerManage
                     if (tempEdges.Any(e => e.FlowType == 2))
                     {
                         item.Name = "出口" + item.Id;
+                        dicVertexes.AddGroupValue("出口", systemVertex);
                     }
                     else
                     {
                         item.Name = "入口" + item.Id;
+                        dicVertexes.AddGroupValue("出口", systemVertex);
                     }
-                   // dicValues
                 }
             }
             #endregion
+            #region 这里所有点的边集合信息和度的信息
+            Dictionary<string, VertexInfo> vertexInfos = new Dictionary<string, VertexInfo>();
+            foreach (var systemVertex in allVertexs)
+            {
+                var edges = graph.GetInEdges(systemVertex.Id);
+                vertexInfos[systemVertex.Id] = new VertexInfo(edges);
+            }
+            #endregion
             #region 确定开始点
             HashSet<string> edgeIds = new HashSet<string>();
             //点遍历id区分系 flowType信息
             HashSet<string> vertexIds = new HashSet<string>();
-            foreach (var systemVertex in vertexes)
+            foreach (var dicVertex in dicVertexes)
             {
-                if (vertexIds.Contains(systemVertex.Id))
-                {
-                    continue;
-                }
+                #region 确定初始状态
                 bool isStart = true;
-
-                var currentVertexName = systemVertex.GetEquipment().Name;
-                if (string.IsNullOrWhiteSpace(currentVertexName))
-                {
-                    continue;
-                }
-
                 var useFlowType = 0;
-                if (currentVertexName.Contains("入口"))
+                var key = dicVertex.Key;
+                if (key.Contains("入口"))
                 {
                     useFlowType = 1;
                     isStart = true;
                 }
-                else if (currentVertexName.Contains("出口"))
+                else if (key.Contains("出口"))
                 {
                     useFlowType = 2;
                     isStart = false;
@@ -628,21 +648,23 @@ namespace SAGA.GplotRelationComputerManage
                 {
                     continue;
                 }
-                Queue<SystemVertex> queueVertexes = new Queue<SystemVertex>();
-                queueVertexes.Enqueue(systemVertex);
+                #endregion
                 Func<string, string> commonId = (str) => useFlowType + "_" + str;
-                while (queueVertexes.Any())
+                var useVertexes = new List<SystemVertex>(dicVertex.Value);
+                useVertexes.ForEach(v => vertexInfos[v.Id].CurrentDegree = -1);
+                while (useVertexes.Count > 0)
                 {
-                    var currentVertex = queueVertexes.Dequeue();
-                    //普通节点Id
-                    if (vertexIds.Contains(commonId(currentVertex.Id)))
+                    var useVertex = useVertexes[0];
+                    var useId = useVertex.Id;
+                    useVertexes.RemoveAt(0);
+                    if (vertexIds.Contains(useId))
                     {
                         continue;
                     }
-                    vertexIds.Add(commonId(currentVertex.Id));
-
-                    var edges = graph.GetInEdges(currentVertex.Id);
-                    foreach (var systemEdge in edges)
+                    vertexIds.Add(commonId(useId));
+                    var vertexInfo = vertexInfos[useId];
+                    var currentVextexCount = useVertexes.Count;
+                    foreach (var systemEdge in vertexInfo.AllEdges)
                     {
                         if (systemEdge.FlowType != useFlowType)
                         {
@@ -652,13 +674,13 @@ namespace SAGA.GplotRelationComputerManage
                         {
                             continue;
                         }
-                        var otherId = systemEdge.GetAnotherVertex(currentVertex.Id);
+                        var otherId = systemEdge.GetAnotherVertex(useId);
                         if (vertexes.Any(v => v.Id == otherId))
                         {
                             continue;
                         }
                         edgeIds.Add(systemEdge.Id);
-                        if ((isStart && systemEdge.ContainVertex(currentVertex.Id) == 1) || (!isStart && systemEdge.ContainVertex(currentVertex.Id) == 0))
+                        if ((isStart && systemEdge.ContainVertex(useId) == 1) || (!isStart && systemEdge.ContainVertex(useId) == 0))
                         {
                             systemEdge.Reverse();
                         }
@@ -667,14 +689,24 @@ namespace SAGA.GplotRelationComputerManage
                             var nextVertex = graph.FindVertex(otherId);
                             if (nextVertex != null)
                             {
-                                queueVertexes.Enqueue(nextVertex);
+                                useVertexes.Add(nextVertex);
                             }
                         }
+
+                        var otherInfo = vertexInfos[otherId];
+                        otherInfo.CurrentDegree = otherInfo.CurrentDegree - 1;
+                    }
+
+                    var newVextexCount = useVertexes.Count;
+                    if (currentVextexCount != newVextexCount)
+                    {
+                        useVertexes = useVertexes.OrderBy(v => vertexInfos[v.Id].CurrentDegree).ToList();
                     }
                 }
             }
             #endregion
-        }
+        } 
+        #endregion
         #endregion
         #endregion
     }