WPF中使用MVVM模式实现可动态检索的Treeview

最近在做大华监控平台的二次开发,其中有一个需求,监控主机和通道以树形显示,并且能够检索所需的通道。花了两天的时间,查阅了很多资料,最终完美实现。 本次使用的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;
                    }
                }
            }
        }
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: MVVM(Model-View-ViewModel)是一种设计模式,以一种分离职责并且高可测试性的方式构建应用程序的UI层。在WPF应用程序MVVM模式是非常流行的,它将UI控件的状态和行为从UI层分离出来,使得代码更容易测试和维护。 在使用MVVM模式实现WPF TreeView节点的添加、重命名、删除、上/下移动时,我们需要定义以下几个重要的类和接口: 1. Model:表示我们的业务逻辑,比如树形结构节点的保存和查询,在这个例子,我们可以定义一个Node类来表示树形结构的一个节点。 2. View:表示WPF应用程序的UI层。在本例,我们可以在MainWindow.xaml创建一个TreeView控件用于显示树形结构的节点。 3. ViewModel:是连接Model和View的纽带,使用它来协调模型和视图之间的交互。在这个例子,我们需要定义一个NodeViewModel类来表示一个节点在UI的状态和行为,比如属性IsExpanded用于表示该节点是否展开,方法AddNode用于添加子节点等。 通过ViewModel类,我们可以实现TreeView节点的添加、重命名、删除、上/下移动等功能。具体实现可以通过ICommand接口和Command类来完成,它们可以实现对操作的数据绑定以及实现对控件的事件处理。 总之,使用MVVM模式可以让我们更好地管理WPF应用程序的UI层,提高代码的可测试性和可维护性,它是一种非常强大和实用的设计模式。 ### 回答2: MVVM模式是一种用于构建用户界面的软件架构模式,在WPF得到了广泛应用。在MVVM模式,界面(View)、业务逻辑(ViewModel)和数据模型(Model)是分开的,提供了更好的可测试性和更好的代码复用性。 在WPF TreeView控件,我们可以通过MVVM模式实现对节点的添加、重命名、删除以及上(下)移动操作。首先,我们需要定义TreeViewItemViewModel类作为TreeView节点的ViewModel,该类包含节点的名称、子节点集合以及节点的相关操作方法。 在实现TreeView节点的添加操作时,我们需要向TreeViewItemViewModel的子节点集合添加一个新的节点,并且将其同步更新到TreeView。在重命名节点操作,根据节点名称的改变,我们需要同步更新对应节点在TreeView的名称。在删除节点操作,我们需要移除当前节点以及其所有的子节点,并同步更新TreeView。在上(下)移动节点操作,我们需要判断当前节点的位置以及上(下)一个兄弟节点的位置,并将其在TreeView进行移动。 总之,通过使用MVVM模式实现WPF TreeView节点的添加、重命名、删除和上(下)移动操作,可以提高程序的可维护性、可扩展性以及代码复用性,使得程序的开发效率和代码质量得到了提高。 ### 回答3: MVVM是一种常用的软件架构模式,用于分离用户界面的开发和业务逻辑的开发,使程序更加可维护和可测试。在WPF应用程序MVVM模式也非常常用。在MVVM模式,View(视图)与ViewModel(视图模型)通过数据绑定来进行通信。ViewModel处理业务逻辑,View负责UI的呈现和交互。 本程序在MVVM模式实现了对WPF TreeView节点的添加、重命名、删除、上(下)移动功能。具体实现如下: 1. 添加节点:在ViewModel维护一个ObservableCollection,用于存储TreeView的节点。View有一个Button,绑定Command到ViewModel的AddCommand。当用户点击Button时,会触发AddCommand的Execute方法,创建一个新的TreeViewItem,并加入到ObservableCollection。通过数据绑定,TreeView实时地显示所有节点。 2. 重命名节点:在View,为TreeViewItem绑定了一个ContextMenu,包括了重命名和删除两个选项。当用户右键点击TreeViewItem,弹出ContextMenu。当用户选择“重命名”选项时,会触发重命名命令的Execute方法,弹出一个窗口供用户输入新的节点名称。当用户确认新名称时,ViewModel的节点名称更新,TreeView实时刷新节点名称。 3. 删除节点:删除节点也是在ContextMenu实现。当用户选择“删除”选项时,会触发删除命令的Execute方法,从ViewModel的ObservableCollection移除选的节点。TreeView实时刷新节点显示。 4. 移动节点:在View,为TreeViewItem绑定了两个Button,分别代表上移和下移。当用户点击上移按钮时,会触发上移命令的Execute方法,将当前选节点与上一个节点交换位置。当用户点击下移按钮时,会触发下移命令的Execute方法,将当前选节点与下一个节点交换位置。 综上所述,该程序通过使用MVVM模式实现了对WPF TreeView节点的添加、重命名、删除、上(下)移操作,提高了程序的可维护性和可测试性。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值