最近在做大华监控平台的二次开发,其中有一个需求,监控主机和通道以树形显示,并且能够检索所需的通道。花了两天的时间,查阅了很多资料,最终完美实现。 本次使用的Treeview仅有两层,根节点(监控主机名称,对应属性CCTVHostName )和子节点(监控主机通道信息)。 当搜索文本框文字发生变化的时候,动态检索设备树的节点,包含对应通道的根节点会自动展开,当文本框为空时自动收起所有根节点。
1、资源管理中新建ViewModel文件夹
2、新建ViewModelBase类,并实现接口INotifyPropertyChanged,以实现属性值变化具有通知特性
public class ViewModelBase: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
3、新建TreeviewCCTVHostViewModel类,继承自ViewModelBase
// 设备树节点监控主机绑定类
public class TreeviewCCTVHostViewModel : ViewModelBase
{
public string CCTVHostName { get; set; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="cctvhostname">当前监控主机的名称</param>
public TreeviewCCTVHostViewModel(string cctvhostname)
{
CCTVHostName = cctvhostname;
PSVariable.SqlConnectionstr = ConfigurationManager.ConnectionStrings["Connect_CCTVDB"].ConnectionString.ToString();
// 从数据库查询当前主机名下的所有通道信息
List<CCTVChannelinfo> ChannelLists = SqlHelper.GetCCTVChannelInfo($"select * from CCTV_Channel_Info where HostName='{cctvhostname}'"); // 根据当前主机名,获取到当前主机的信息实体
ChannelInfos = new List<TreeviewChannelnfoViewModel>();
foreach (CCTVChannelinfo item in ChannelLists)
{
// 不包括通道名称为null的通道
if (item.ChannelName != "null")
{
TreeviewChannelnfoViewModel channel = new TreeviewChannelnfoViewModel($"通道{item.ChannelID}-{item.ChannelName}");
ChannelInfos.Add(channel);
}
}
}
// 子节点的集合列表
private List<TreeviewChannelnfoViewModel> _channelinfos;
public List<TreeviewChannelnfoViewModel> ChannelInfos
{
get => _channelinfos;
set
{
_channelinfos = value;
OnPropertyChanged("ChannelInfos");
}
}
// 用于子节点动态选中绑定
private bool _isselected;
public bool IsSelected
{
get => _isselected;
set
{
_isselected = value;
OnPropertyChanged("IsSelected");
}
}
// 用于节点动态展开收起
private bool _isexpanded;
public bool IsExpanded
{
get => _isexpanded;
set
{
_isexpanded = value;
OnPropertyChanged("IsExpanded");
}
}
// 用于子节点的动态隐藏和显示
private Visibility _visible;
public Visibility Visible
{
get => _visible;
set
{
_visible = value;
OnPropertyChanged("Visible");
}
}
}
4、新建TreeviewChannelnfoViewModel 类,继承自ViewModelBase
// 设备树节点通道信息绑定类
public class TreeviewChannelnfoViewModel : ViewModelBase
{
public string ChannelinfoDisplay { get; set; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="channelinfo">当前通道的信息,格式:通道1-赛鲁迪东</param>
public TreeviewChannelnfoViewModel(string channelinfo)
{
ChannelinfoDisplay = channelinfo;
}
// 设备数节点是否选中
private bool _isselected;
public bool IsSelected
{
get => _isselected;
set
{
_isselected = value;
OnPropertyChanged("IsSelected");
OnPropertyChanged("TreeviewSelectedItem");
}
}
// 用户节点动态展开收起
private bool _isexpanded;
public bool IsExpanded
{
get => _isexpanded;
set
{
_isexpanded = value;
OnPropertyChanged("IsExpanded");
}
}
// 用于子节点的动态隐藏和显示
private Visibility _visible;
public Visibility Visible
{
get => _visible;
set
{
_visible = value;
OnPropertyChanged("Visible");
}
}
}
5、新建UIViewModel 类,继承自ViewModelBase
public class UIViewModel : ViewModelBase
{
#region 设备树相关
//设备树搜索框搜索文本框
private string _searchtreeitemtext;
public string SearchTreeitemTEXT
{
get => _searchtreeitemtext;
set
{
_searchtreeitemtext = value;
OnPropertyChanged("SearchTreeitemTEXT");
}
}
// 设备树Treeview itemsSource
private List<TreeviewCCTVHostViewModel> _cctvhosts;
public List<TreeviewCCTVHostViewModel> CCTVHosts
{
get => _cctvhosts;
set
{
_cctvhosts = value;
OnPropertyChanged("TreeviewItemsSource");
}
}
#endregion
}
6、新建MainViewModel类,用于实例化类的对象和初始化相关参数
public class MainViewModel
{
public UIViewModel MyUIVM { get; private set; }
public MainViewModel()
{
InitTreeviewForCCTV(); // 初始化监控主机设备树
}
/// <summary>
/// 初始化CCTV监控主机设备树
/// </summary>
private async void InitTreeviewForCCTV()
{
await Task.Run(() =>
{
// 初始化设备监控主机信息列表
MyUIVM.CCTVHosts = new List<TreeviewCCTVHostViewModel>();
PSVariable.SqlConnectionstr = ConfigurationManager.ConnectionStrings["Connect_CCTVDB"].ConnectionString.ToString();
List<CCTVHost> cctvhosts = SqlHelper.GetCCTVInfo(); // 监控主机信息
foreach (CCTVHost item in cctvhosts)
{
var cctvhost = new TreeviewCCTVHostViewModel(item.HostName);
cctvhost.CCTVHostName = item.HostName;
cctvhost.Visible = System.Windows.Visibility.Visible;
MyUIVM.CCTVHosts.Add(cctvhost);
}
});
}
}
7、程序的主窗体构造函数中,绑定数据上下文:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainViewModel MainVM = new MainViewModel(); //实例化对象
DataContext = MainVM; // 绑定窗体上下文
}
}
8、MainWindow.xaml 中调用:
首先引入命名空间:
xmlns:ViewModel="clr-namespace:CCTV_Video_Download_Auto.ViewModels"
UI控件中绑定
<Grid>
<!--搜索设备树-->
<Border VerticalAlignment="Top" HorizontalAlignment="Center" Margin="10,0,0,0">
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
<TextBox x:Name="TB_SearchTreeitem" Text="{Binding MyUIVM.SearchTreeitemTEXT,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Width="182" Height="22" HorizontalAlignment="Left" VerticalContentAlignment="Center" Background="{x:Null}" BorderThickness="0,0,0,1"
BorderBrush="#666666" Padding="5,0,0,0" FontFamily="Microsoft Yahei UI" FontWeight="Normal" FontSize="11" Cursor="IBeam"
TextChanged="TB_SearchTreeitem_TextChanged"/>
<Button x:Name="BT_SearchTreeitem" IsDefault="True" BorderThickness="0" BorderBrush="{x:Null}" Background="{x:Null}" Margin="3,0,0,0" Cursor="Hand" Click="BT_SearchTreeitem_Click" >
<Button.Content>
<materialDesign:PackIcon Kind="Search" Width="22" Height="25" VerticalAlignment="Center" Foreground="{StaticResource ThemeColorBrush}" Margin="-12,1,0,0"/>
</Button.Content>
</Button>
</StackPanel>
</Border>
<!--设备树-->
<Border VerticalAlignment="Top" Margin="10,35,0,0">
<TreeView x:Name="TV_DeviceList" ItemsSource="{Binding MyUIVM.CCTVHosts,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Foreground="{StaticResource DefaultTextForecolor}" SelectedItemChanged="TV_DeviceList_SelectedItemChanged" >
<TreeView.Resources>
<!--分层数据模板-->
<HierarchicalDataTemplate DataType="{x:Type ViewModel:TreeviewCCTVHostViewModel}" ItemsSource="{Binding ChannelInfos}">
<StackPanel Orientation="Horizontal" Height="28">
<Image Width="22" Source="Resources/image/MonitorHost.png" VerticalAlignment="Center" />
<TextBlock Text="{Binding CCTVHostName}" Foreground="{StaticResource DefaultTextForecolor}" VerticalAlignment="Center" Margin="8,0,0,0"/>
</StackPanel>
</HierarchicalDataTemplate>
<!--数据模板-->
<DataTemplate DataType="{x:Type ViewModel:TreeviewChannelnfoViewModel}" >
<StackPanel Orientation="Horizontal" Height="24">
<Image Width="22" Source="Resources/image/camera.png" VerticalAlignment="Center" />
<TextBlock Text="{Binding ChannelinfoDisplay}" Foreground="{StaticResource DefaultTextForecolor}" FontSize="10" FontWeight="Thin" VerticalAlignment="Center" Margin="6,0,0,0"/>
</StackPanel>
</DataTemplate>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="DarkCyan" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="{StaticResource ThemeColor}" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="Black" />
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding IsExpanded}"/>
<Setter Property="Visibility" Value="{Binding Visible}" />
<!--子节点双击实时预览-->
<EventSetter Event="MouseDoubleClick" Handler="TreeViewItemMouseDoubleClick" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Border>
</Grid>
9、数据绑定说明:
搜索文本框文字:MyUIVM.SearchTreeitemTEXT(双向绑定,动态通知属性值变化)
TreeView 数据源(itemsSource):MyUIVM.CCTVHosts(类型:List)
TreeView 分层数据模板(DataType):ViewModel:TreeviewCCTVHostViewModel
TreeView 分层数据模板(itemsSource):ViewModel:ChannelInfos
TreeView 数据模板(DataType):ViewModel:TreeviewChannelnfoViewModel
TreeView 数据模板(itemsSource):ViewModel:ChannelinfoDisplay
IsSelected:当前节点是否选中
IsExpanded:父级节点,是否展开
Visible:节点的可见性
10、动态搜索设备树的方法:
/// <summary>
/// 根据输入关键字搜索设备树的方法
/// </summary>
/// <param name="keyword">输入关键字</param>
private void SearchTreeviewItem(string keyword)
{
// 遍历设备的ItemsSource
if (keyword == null || keyword == "")
{
foreach (TreeviewCCTVHostViewModel item in PSVariable.MainVM.MyUIVM.CCTVHosts)
{
if (item.IsExpanded)
{
item.IsExpanded = false;
}
if (item.IsSelected == true)
{
item.IsSelected = false;
}
foreach (TreeviewChannelnfoViewModel channel in item.ChannelInfos)
{
if (channel.Visible == Visibility.Collapsed)
{
channel.Visible = Visibility.Visible;
}
if (channel.IsSelected)
{
channel.IsSelected = false;
}
}
}
}
else
{
foreach (TreeviewCCTVHostViewModel item in PSVariable.MainVM.MyUIVM.CCTVHosts)
{
foreach (TreeviewChannelnfoViewModel channel in item.ChannelInfos)
{
if (channel.ChannelinfoDisplay.Contains(keyword))
{
if (channel.Visible == Visibility.Collapsed)
{
channel.Visible = Visibility.Visible;
}
if (item.IsExpanded == false)
{
item.IsExpanded = true;
}
}
else
{
if (channel.Visible == Visibility.Visible)
{
channel.Visible = Visibility.Collapsed;
}
}
}
}
}
}