0

I have a program with a TreeView Item that holds child nodes with numerical headers. When adding child nodes to the TreeViewItem I would like to add them numerically, but I don't know how to add nodes in between other ones.

I'm guessing I would call to a function that would sort through the child nodes, convert the headers to integers, compare the header with the entered value, and then stop when a node header is greater than the value entered. The new node would have to be entered before the node with the header greater than the new node being added.

Is this the best approach, or is there a better one? Please demonstrate the easiest way to go about this. FYI, my TreeViewItem is in my main window, and being worked on from a separate window.

This should give you an idea of how I add child nodes to my TreeViewItem:

//Global boolean    
// bool isDuplicate;

//OKAY - Add TreeViewItems to location & Checks if location value is numerical
private void button2_Click(object sender, RoutedEventArgs e)
{
      //Checks to see if TreeViewItem is a numerical value
      string Str = textBox1.Text.Trim();
      double Num;

      bool isNum = double.TryParse(Str, out Num);

      //If not numerical value, warn user
      if (isNum == false)
          MessageBox.Show("Value must be Numerical");
      else //else, add location
      {
          //close window
          this.Close();

          //Query for Window1
          var mainWindow = Application.Current.Windows
              .Cast<Window1>()
              .FirstOrDefault(window => window is Window1) as Window1;

          //declare TreeViewItem from mainWindow
          TreeViewItem locations = mainWindow.TreeViewItem;

          //Passes to function -- checks for DUPLICATE locations
          CheckForDuplicate(TreeViewItem.Items, textBox1.Text);

          //if Duplicate exists -- warn user
          if (isDuplicate == true)
             MessageBox.Show("Sorry, the number you entered is a duplicate of a current node, please try again.");
          else //else -- create node
          {
               //Creates TreeViewItems for Location
               TreeViewItem newNode = new TreeViewItem();

               //Sets Headers for new locations
               newNode.Header = textBox1.Text;

               //Add TreeView Item to Locations & Blocking Database
               mainWindow.TreeViewItem.Items.Add(newNode);

           }
       }
}

//Checks to see whether the header entered is a DUPLICATE
private void CheckForDuplicate(ItemCollection treeViewItems, string input)
{
      for (int index = 0; index < treeViewItems.Count; index++)
      {
             TreeViewItem item = (TreeViewItem)treeViewItems[index];
             string header = item.Header.ToString();

             if (header == input)
             {
                   isDuplicate = true;
                   break;
             }
             else
                   isDuplicate = false;
      }
}

Thank you.

Community
  • 1
  • 1
Eric after dark
  • 1,768
  • 4
  • 31
  • 79
  • I see potential problems when your structure gets more complicated. How do you handle nodes with more than one child? If the path of this node is sorted, will it's siblings be sorted as well? When you move a node do you move it's children with it or do you add them to a new parent? – tinstaafl Jul 29 '13 at 18:58
  • I'm only dealing with one parent right now. The children of this `TreeViewItem` will only ever be assigned to one parent. – Eric after dark Jul 29 '13 at 19:01
  • The problem then will be what to do with the children of the child node. You need to remember that a tree structure means that everything is connected. If you're keeping everything simple with only one path, then you'd be better off putting your data into a list and populate the treeview from the data in the list. If you want to sort it, then after every sort clear and re-populate the treeview. It sounds overdone, but it's a lot simpler then removing and inserting nodes once they're added to the treeview. – tinstaafl Jul 29 '13 at 19:59
  • Okay, so put my nodes into a list, and then everytime the list is updated, update the children in the `TreeView`? – Eric after dark Jul 29 '13 at 20:11
  • Yes but you'll have to iterate through the list and add each one as a child of the last one in the treeview. Oops sorry, didn't see the wpf tag. there might be a better way in wpf. – tinstaafl Jul 29 '13 at 20:15

1 Answers1

1

Here is a little example for your special case using ModelView, Binding and CollectionView

ModelViews

public class MainViewModel
{
  private readonly ObservableCollection<TreeItemViewModel> internalChildrens;

  public MainViewModel(string topLevelHeader) {
    this.TopLevelHeader = topLevelHeader;
    this.internalChildrens = new ObservableCollection<TreeItemViewModel>();
    var collView = CollectionViewSource.GetDefaultView(this.internalChildrens);
    collView.SortDescriptions.Add(new SortDescription("Header", ListSortDirection.Ascending));
    this.Childrens = collView;
  }

  public string TopLevelHeader { get; set; }

  public IEnumerable Childrens { get; set; }

  public bool AddNewChildren(double num) {
    var numExists = this.internalChildrens.FirstOrDefault(c => c.Header == num) != null;
    if (!numExists) {
      this.internalChildrens.Add(new TreeItemViewModel() {Header = num});
    }
    return numExists;
  }
}

public class TreeItemViewModel
{
  public double Header { get; set; }
}

Xaml

<Window x:Class="WpfStackOverflowSpielWiese.Window21"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window21"
        Height="300"
        Width="300"
        x:Name="tvExample">

  <Grid DataContext="{Binding ElementName=tvExample, Path=ViewModel, Mode=OneWay}">

    <StackPanel Orientation="Vertical">
      <TextBox x:Name="tb" />
      <Button Content="Add"
              Click="AddNewItemOnClick" />
      <TreeView>
        <TreeViewItem Header="{Binding TopLevelHeader, Mode=OneWay}"
                      ItemsSource="{Binding Childrens, Mode=OneWay}">
          <TreeViewItem.ItemTemplate>
            <HierarchicalDataTemplate>
              <TextBlock Text="{Binding Header}" />
            </HierarchicalDataTemplate>
          </TreeViewItem.ItemTemplate>
        </TreeViewItem>
      </TreeView>
    </StackPanel>

  </Grid>

</Window>

Xaml code behind

public partial class Window21 : Window
{
  public static readonly DependencyProperty ViewModelProperty =
    DependencyProperty.Register("ViewModel", typeof(MainViewModel), typeof(Window21), new PropertyMetadata(default(MainViewModel)));

  public MainViewModel ViewModel {
    get { return (MainViewModel)this.GetValue(ViewModelProperty); }
    set { this.SetValue(ViewModelProperty, value); }
  }

  public Window21() {
    this.InitializeComponent();
    this.ViewModel = new MainViewModel("TopLevel");
  }

  private void AddNewItemOnClick(object sender, RoutedEventArgs e) {
    double Num;
    var isNum = double.TryParse(this.tb.Text, out Num);
    //If not numerical value, warn user
    if (isNum == false) {
      MessageBox.Show("Value must be Numerical");
      return;
    }

    var isDuplicate = this.ViewModel.AddNewChildren(Num);
    if (isDuplicate) {
      MessageBox.Show("Sorry, the number you entered is a duplicate of a current node, please try again.");
      return;
    }
  }
}

hope that helps

punker76
  • 14,326
  • 5
  • 58
  • 96
  • I put it into a test program to see how it works and the only error I get is with the line that says `public IEnumerable Childrens { get; set; }`. The compiler says, `Using the generic type 'System.Collections.Generic.IEnumerable' requires '1' type arguments`. Maybe you know what this means. – Eric after dark Jul 29 '13 at 20:29
  • @Ericafterdark u need the System.Collections not the System.Collections.Generic using (NET4) – punker76 Jul 29 '13 at 20:46
  • Alright, it's working. I'll still have to work on implementing it into my program though. Thank you. – Eric after dark Jul 29 '13 at 20:53
  • Do you think this is a more complicated way of doing this when compared to @tinstaafl's way? What if I just put all of my nodes into a List? – Eric after dark Jul 30 '13 at 12:20