WPF树视图-定义父级和子级节点?

时间:2018-11-01 16:52:41

标签: c# wpf collections treeview treeviewitem

我正在尝试在WPF中实现TreeView,以显示在每日帐户余额上显示收费活动,调整活动等的子项。首先,我首先将每日帐户余额详细信息呈现为父节点(我已成功完成),然后尝试例如将文本段显示为要测试的子项,然后从那里扩展以显示子项我最好将其呈现为表格以显示当天的费用,然后分别对当天的费用进行调整,以此类推。这就是我遇到的问题。

这是我的XAML代码:

<Window x:Class="Client_Invoice_Auditor.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Client_Invoice_Auditor"
        xmlns:self="clr-namespace:Client_Invoice_Auditor.CoreClientAR"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Grid>
        <Grid.ColumnDefinitions>

        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20*"/>
            <RowDefinition Height="80*"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0" Grid.Column="0">
            <StackPanel Orientation="Vertical">
                <DockPanel VerticalAlignment="Top" Height="20" Panel.ZIndex="1">
                    <Menu Name="fileMenu" Width="Auto" DockPanel.Dock="Top">
                        <MenuItem Header="File">
                            <MenuItem Header="Open Account File" Click="menuOpenFile_Click"/>
                            <MenuItem Header="Exit" Click="menuExit_Click"/>
                        </MenuItem>
                        <MenuItem Header="Options">
                            <!--<MenuItem Header="Update" Click="update_Click"/>-->
                            <MenuItem Header="About" Click="about_Click"/>
                        </MenuItem>
                    </Menu>
                </DockPanel>
                <WrapPanel Orientation="Horizontal" HorizontalAlignment="Center" Height="Auto">
                    <StackPanel Width="Auto" Orientation="Horizontal" HorizontalAlignment="Center">
                        <Border BorderBrush="MediumAquamarine" BorderThickness="2">
                            <Label Name="AccountNumber"/>
                        </Border>
                        <Border BorderBrush="MediumAquamarine" BorderThickness="2">
                            <Label Name="AcctDesc"/>
                        </Border>
                        <Border BorderBrush="MediumAquamarine" BorderThickness="2">
                            <Label Name="Organization"/>
                        </Border>
                    </StackPanel>
                </WrapPanel>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                    <Label Margin="20,10,0,0" Content="Activity Date Time" />
                    <Label Margin="60,10,0,0" Content="Beginning Balance" />
                    <Label Margin="10,10,0,0" Content="Charge Amount" />
                    <Label Margin="30,10,0,0" Content="Adjustments" />
                    <Label Margin="40,10,0,0" Content="Payments" />
                    <Label Margin="60,10,0,0" Content="End Balance" />
                </StackPanel>
            </StackPanel>

        </Grid>
        <Grid Grid.Row="1" Grid.Column="0">
            <TreeView Name="DABView">
                <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type self:dailyAccountBalance}">
                    <StackPanel Orientation="Horizontal">
                            <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                                <TextBlock Width="150" Text="{Binding DabActivityDate}" />
                                <TextBlock Width="100" Margin="20,0,0,0" Text="{Binding BegBalance}" />
                                <TextBlock Width="100" Margin="20,0,0,0" Text="{Binding ChrgAmount}" />
                                <TextBlock Width="100" Margin="20,0,0,0" Text="{Binding AdjAmount}" />
                                <TextBlock Width="100" Margin="20,0,0,0" Text="{Binding PmtAmount}" />
                                <TextBlock Width="100" Margin="20,0,0,0" Text="{Binding EndBalance}" />
                            </StackPanel>
                        </StackPanel>

                </HierarchicalDataTemplate>
                <DataTemplate x:Key="TestChild">
                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                            <TextBlock Text="YOU IZ GOOD" />
                     </StackPanel>
                </DataTemplate>
                </TreeView.Resources>
            </TreeView>
        </Grid>
    </Grid>
</Window>

我的主窗口的代码位于:

using Client_Invoice_Auditor.CoreClientAR;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Client_Invoice_Auditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        public string accountFile;
        public MainWindow()
        {
            InitializeComponent();
            OpenAccountFile();
        }

        private void OpenAccountFile()
        {
            accountFile = "";
            //errorFilters.Clear();
            // Displays an OpenFileDialog so the user can select a file.  
            Microsoft.Win32.OpenFileDialog openFileDialog1 = new Microsoft.Win32.OpenFileDialog();
            openFileDialog1.Filter = "Account Files|*.acct";
            openFileDialog1.Title = "Select an account file";

            if (openFileDialog1.ShowDialog() == true)
            {
                // Assign the cursor in the Stream to the Form's Cursor property.
                accountFile = openFileDialog1.FileName;

                //openFileDialog1.Dispose();
            }
            if (accountFile == "")
            {
                /*System.Windows.MessageBox.Show("File must be selected in order to continue - exiting now."
                    , "No File Selected", MessageBoxButton.OK);
                this.Close();*/
                if (!AcctDesc.HasContent)
                {
                    AcctDesc.Content = "No account file Loaded";
                    //Version version = Assembly.GetExecutingAssembly().GetName().Version;

                }
            }
            else
            {
                //openFileDialog1 = null;
                Console.WriteLine("Account file path is: " + accountFile);

                DataTable dataAR = new DataTable();

                try
                {
                    Tuple<accountARHeader, List<dailyAccountBalance>, DataTable> loadedAR = dabARLoader.LoadARData(accountFile);
                    //dataAR = loadedAR.Item2;
                    AccountNumber.Content = "Account Number: " + loadedAR.Item1.AccountNumber;
                    AcctDesc.Content = "Description: " + loadedAR.Item1.AccountDescription;
                    Organization.Content = "Client Organization: " + loadedAR.Item1.OrganizationName;
                    //TreeViewItem dummy = new TreeViewItem();
                    //dummy.DataContext = "Hi";
                    //loadedAR.Item2.First().Dummy.Add("La");
                    //DABView.Items.Add(dummy);
                    DABView.ItemsSource = loadedAR.Item2;

                    //DABView.DisplayMemberPath = "A";
                }
                catch (Exception e)
                {
                    System.Windows.MessageBox.Show("I don't wanna open this file! Try another. Error: " + e.Message);
                    OpenAccountFile();
                }

            }
        }

        private void menuOpenFile_Click(object sender, RoutedEventArgs e)
        {
            OpenAccountFile();
        }

        private void menuExit_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        private void about_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.MessageBox.Show("I heard you like clicking buttons.");
        }
    }
}

最后,我相关的类文件:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace Client_Invoice_Auditor.CoreClientAR
{
    public class dailyAccountBalance
    {
        double dabID;

        DateTime dabActivityDate;

        double begBalance;

        double endBalance;

        double chrgAmount;

        double adjAmount;

        double pmtAmount;

        string dabActivitySegment;

        public double DabID { get => dabID; set => dabID = value; }
        public double BegBalance { get => begBalance; set => begBalance = value; }
        public double EndBalance { get => endBalance; set => endBalance = value; }
        public string DabActivitySegment { get => dabActivitySegment; set => dabActivitySegment = value; }
        public DateTime DabActivityDate { get => dabActivityDate; set => dabActivityDate = value; }
        public double ChrgAmount { get => chrgAmount; set => chrgAmount = value; }
        public double AdjAmount { get => adjAmount; set => adjAmount = value; }
        public double PmtAmount { get => pmtAmount; set => pmtAmount = value; }

        public ObservableCollection<dailyAccountBalance> Items { get; set; }
        public List<string> Dummy { get => dummy; set => dummy = value; }
        public IList<TreeViewItem> DummyItems { get => dummyItems; set => dummyItems = value; }

        public dailyAccountBalance()
        {
            this.Items = new ObservableCollection<dailyAccountBalance>();
        }

        List<string> dummy = new List<string>();

        IList<TreeViewItem> dummyItems;
    }
}

例如,是否有一种方法可以在展开时在TreeView中将虚拟字符串显示为一个子代?我需要为此实现扩展方法吗?

非常感谢。

1 个答案:

答案 0 :(得分:0)

通常,我过去所做的是将一个DummyNode添加到TreeNode,将TreeViewItem样式中的“ IsExpanded” TreeViewItem属性绑定到我的视图模型中Node类的IsExpanded属性上,并添加一个bool _isLoaded标志。然后,当IsExpanded触发时,我检查_isLoaded是否为true,如果不是,那么我去加载所有后续数据并将_isLoaded设置为true。

btw ...我使用Prism给我实现了OnPropertyChanged的INotifyPropertyChanged事件的BindableBase类。您可以选择自己实现,而不必使用BindableBase。

示例: 节点类

public class TreeData : BindableBase
{
    private bool _isExpanded = false;
    private bool _isSelected = false;
    private bool _hasChildren = false;
    private Func<List<TreeData>> _getChildrenMethod;
    private bool _isLoaded = false;

    public TreeData(string value, bool hasChildren, Func<List<TreeData>> getChildren)
    {
        Value = value;
        _hasChildren = hasChildren;
        _getChildrenMethod = getChildren;

        if(hasChildren)
        {
            Children.Add(new TreeNode("Dummy", false, null));
        }
    }

    public ObservableCollection<TreeData> Children { get; set; } = new ObservableCollection<TreeData>();
    public string Value {get;set;}

    public bool IsSelected
    {
       get { return _isSelected; }
       set { _isSelected = value; OnPropertyChanged(); }
    }

    public bool IsExpanded
    {
        get { return _isExpanded; }
        set
        {
             _isExpanded = value;

             if(!_isLoaded)
             {
                 // Call load method here
                 if(_addChildrenMethod != null && _hasChildren)
                 {
                     Children.Clear();
                     Children.AddMany(_addChildrenMethod());
                 }
                 _isLoaded = true;
             }

             OnPropertyChanged();
        }
    }
}

查看模型类(拥有项目列表)

public class BusinessViewModel
{
    public ObservableCollection<TreeData> Items { get; set; } = new ObservableCollection<TreeData>();

    public BusinessViewModel()
    { 
        Items.Add(new TreeData("Root Item", hasChildren: true, GetChildren));
    }

    public List<TreeData> GetChildren()
    {
        return new List<TreeData>()
        {
            new TreeData("Child", hasChildren: false, null);
        };
    }
}

然后使用XAML

<TreeView ItemSource="{Binding Items}" >
  <TreeView.Resources>          
      <Style TargetType="TreeViewItem">
         <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
         <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
         <Style.Triggers>
             <Trigger Property="IsSelected" Value="True">
                 <Setter Property="Background" Value="Blue" />
                 <Setter Property="Foreground" Value="White" />
             </Trigger>
         </Style.Triggers>
     </Style>
  </TreeView.Resources>
</TreeView>

希望有帮助:D