///////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC PROCEDURES ///////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Returns the <c>ContextMenu</c> for the specified collection of view-models. /// </summary> /// <param name="ownerControl">The owner control.</param> /// <param name="viewModels">The view-models to examine.</param> /// <returns>The <c>ContextMenu</c> for the specified collection of view-models.</returns> public override ContextMenu GetItemContextMenu(TreeListBox ownerControl, IList <ShellObjectViewModel> viewModels) { var menu = base.GetItemContextMenu(ownerControl, viewModels); if (menu != null) { return(menu); } if (viewModels.Count == 1) { var shellObject = viewModels[0].Model as CustomShellObject; if (shellObject != null) { menu = new ContextMenu(); menu.Items.Add(new MenuItem() { Header = "Custom Menu Item" }); return(menu); } } return(null); }
public void TestResetItemsSource() { AvalonTestRunner.RunInSTA(delegate { //populate sample data List <string> businessObjects = new List <string>(4); businessObjects.Add("A"); businessObjects.Add("AA"); businessObjects.Add("AAA"); businessObjects.Add("AAAA"); businessObjects.Add("AAAA"); TreeListBox treeListBox = new TreeListBox(); treeListBox.ExposedGenerateItemsSource(businessObjects); //check that there are 4 items in the items source and that they are of type TreeLiistBoxInfo Assert.AreEqual(5, treeListBox.Items.Count, "Invalid number of items added"); for (int i = 0; i < treeListBox.Items.Count; i++) { Assert.AreEqual(typeof(TreeListBoxInfo), treeListBox.Items[i].GetType(), "Invalid type added to tree list box"); } }); }
void IComponentConnector.Connect(int connectionId, object target) { switch (connectionId) { case 1: this.syncPicturesText = (CheckBox)target; return; case 2: this.syncAllPictures = (CheckBox)target; return; case 3: this.syncIncludeVideo = (CheckBox)target; return; case 4: this.pictureAlbumsTreeView = (TreeListBox)target; return; case 5: this.cancelButton = (Button)target; return; case 6: this.syncButton = (Button)target; return; } this._contentLoaded = true; }
/// <summary> /// Sets whether the specified item is selected. /// </summary> /// <param name="ownerControl">The owner control.</param> /// <param name="item">The item to update.</param> /// <param name="value">The new value.</param> public override void SetIsSelected(TreeListBox ownerControl, object item, bool value) { var model = item as TreeNodeModel; if (model != null) { model.IsSelected = value; } }
/// <summary> /// Returns a string containing the full paths of the specified items. /// </summary> /// <param name="toolboxControl">The <see cref="TreeListBox"/> control.</param> /// <param name="items">The items to examine.</param> /// <returns>A string containing the full paths of the specified items.</returns> private static string JoinItemFullPaths(TreeListBox toolboxControl, IEnumerable <object> items) { var fullPaths = new StringBuilder(); foreach (var item in items) { fullPaths.Append(toolboxControl.GetFullPath(item)).Append('\n'); } return(fullPaths.ToString().TrimEnd()); }
public TreeListsBinding(ITreeSource treeSource, TreeListBox directoryTreeListBox, TreeListBox directoryContentListBox) { Verify.Argument.IsNotNull(treeSource, nameof(treeSource)); Verify.Argument.IsNotNull(directoryTreeListBox, nameof(directoryTreeListBox)); Verify.Argument.IsNotNull(directoryContentListBox, nameof(directoryContentListBox)); TreeSource = treeSource; DirectoryTreeListBox = directoryTreeListBox; DirectoryContentListBox = directoryContentListBox; Progress = directoryTreeListBox.ProgressMonitor; }
public TreeListsBinding(ITreeSource treeSource, TreeListBox directoryTreeListBox, TreeListBox directoryContentListBox) { Verify.Argument.IsNotNull(treeSource, "treeSource"); Verify.Argument.IsNotNull(directoryTreeListBox, "directoryTreeListBox"); Verify.Argument.IsNotNull(directoryContentListBox, "directoryContentListBox"); _treeSource = treeSource; _directoryTreeListBox = directoryTreeListBox; _directoryContentListBox = directoryContentListBox; Progress = directoryTreeListBox.ProgressMonitor; }
void IComponentConnector.Connect(int connectionId, object target) { switch (connectionId) { case 1: this.syncMusicCheckBox = (CheckBox)target; return; case 2: this.syncAllMusicCheckBox = (CheckBox)target; return; case 3: this.playlistsLabel = (Label)target; return; case 4: this.playlistsList = (ListBox)target; this.playlistsList.PreviewKeyDown += new KeyEventHandler(this.listBox_PreviewKeyDown); return; case 5: this.genresLabel = (Label)target; return; case 6: this.genresList = (ListBox)target; this.genresList.PreviewKeyDown += new KeyEventHandler(this.listBox_PreviewKeyDown); return; case 7: this.artistsLabel = (Label)target; return; case 8: this.artistsList = (TreeListBox)target; return; case 9: this.drmMessage = (TextBlock)target; return; case 10: this.cancelButton = (Button)target; return; case 11: this.syncButton = (Button)target; return; } this._contentLoaded = true; }
public static void TreeListBoxPreviewKeyDown(TreeListBox sender, KeyEventArgs e) { if (((sender != null) && (e != null)) && (e.Key == Key.Space)) { TreeListBoxInfo selectedItem = sender.SelectedItem as TreeListBoxInfo; if (selectedItem != null) { SelectableOptionViewModel dataItem = selectedItem.DataItem as SelectableOptionViewModel; dataItem.IsSelected = new bool?(dataItem.IsSelected.HasValue ? !dataItem.IsSelected.Value : false); } e.Handled = true; } }
public void TestItemsSourceCollectionNotifications() { AvalonTestRunner.RunInSTA(delegate { //populate sample data ObservableCollection <string> businessObjects = new ObservableCollection <string>(); businessObjects.Add("A"); businessObjects.Add("AA"); businessObjects.Add("AAA"); businessObjects.Add("AAAA"); TreeListBox treeListBox = new TreeListBox(); treeListBox.ExposedGenerateItemsSource(businessObjects); Assert.AreEqual(4, treeListBox.Items.Count, "Invalid number of items added"); //add anew item and verify that this item was added to the items source businessObjects.Add("NewItem1"); //check the Count Assert.AreEqual(5, treeListBox.Items.Count, "Invalid number of items added"); //check the position of the item Assert.AreEqual("NewItem1", ((TreeListBoxInfo)treeListBox.Items[4]).DataItem, "Item was added at an invalid index"); //insert another item in the begining of the list businessObjects.Insert(1, "NewItem2"); //check the Count Assert.AreEqual(6, treeListBox.Items.Count, "Invalid number of items added"); //check the position of the item Assert.AreEqual("NewItem2", ((TreeListBoxInfo)treeListBox.Items[1]).DataItem, "Item was added at an invalid index"); //Check the remove businessObjects.RemoveAt(1); //check the Count Assert.AreEqual(5, treeListBox.Items.Count, "Invalid number of items added"); //validate that the item was deleted properly for (int i = 0; i < treeListBox.Items.Count; i++) { Assert.AreNotEqual("NewItem2", ((TreeListBoxInfo)treeListBox.Items[i]).DataItem, "Item was removed from the wrong index"); } }); }
///////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC PROCEDURES ///////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Initializes a <see cref="IDataObject"/> based on the specified items. /// </summary> /// <param name="sourceControl">The source control that contains the items.</param> /// <param name="dataObject">The <see cref="IDataObject"/> to initialize.</param> /// <param name="items">The items to examine.</param> /// <returns>The <see cref="DragDropEffects"/> that specifies the allowed drag/drop effects for the <see cref="IDataObject"/>.</returns> /// <remarks> /// The default implementation of this method simply return <c>DragDropEffects.None</c>. /// Override this method to initialize the <see cref="IDataObject"/> with text and/or other data formats /// (such as <see cref="TreeListBox.ItemDataFormat"/> for supporting drag/drop), /// and to return an appropriate <c>DragDropEffects</c> result. /// </remarks> public override DragDropEffects InitializeDataObject(TreeListBox sourceControl, IDataObject dataObject, IEnumerable <object> items) { if (sourceControl == null) { throw new ArgumentNullException(nameof(sourceControl)); } if (dataObject == null) { throw new ArgumentNullException(nameof(dataObject)); } if (items == null) { throw new ArgumentNullException(nameof(items)); } // Verify the source items foreach (var item in items) { if (!TryGetModelsFromItem(sourceControl, item, out var sourceCategoryModel, out _)) { // Quit if any item cannot be recognized return(DragDropEffects.None); } } // Store the full paths to items in case we drop on the tree itself... // Each item needs to have a unique path, which comes from adapter GetPath() calls var fullPaths = JoinItemFullPaths(sourceControl, items); if (string.IsNullOrWhiteSpace(fullPaths)) { return(DragDropEffects.None); } dataObject.SetData(TreeListBox.ItemDataFormat, fullPaths); // If there is one item, store its text so that it can be dropped elsewhere if (items.Count() == 1) { string text = GetDataObjectText(items.First()); if (!string.IsNullOrEmpty(text)) { dataObject.SetData(DataFormats.Text, text); } } return(DragDropEffects.Move | DragDropEffects.Copy); }
/// <summary> /// Tries to get the category model for the specified <see cref="ToolboxTreeNodeModel"/>. /// </summary> /// <param name="toolboxControl">The <see cref="TreeListBox"/> control.</param> /// <param name="itemModel">The <see cref="ToolboxTreeNodeModel"/> to examine.</param> /// <param name="categoryModel">The resulting <see cref="CategoryTreeNodeModel"/>.</param> /// <returns> /// <c>true</c> if a result was found; otherwise, <c>false</c>. /// </returns> private bool TryGetCategory(TreeListBox toolboxControl, ToolboxTreeNodeModel itemModel, out CategoryTreeNodeModel categoryModel) { categoryModel = null; if (itemModel is CategoryTreeNodeModel localCategoryModel) { categoryModel = localCategoryModel; return(true); } var navigator = toolboxControl.GetItemNavigator(itemModel); if (navigator.GoToParent()) { categoryModel = navigator.CurrentItem as CategoryTreeNodeModel; return(categoryModel != null); } return(false); }
///////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC PROCEDURES ///////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Initializes a <see cref="IDataObject"/> based on the specified items. /// </summary> /// <param name="sourceControl">The source control that contains the items.</param> /// <param name="dataObject">The <see cref="IDataObject"/> to initialize.</param> /// <param name="items">The items to examine.</param> /// <returns>The <see cref="DragDropEffects"/> that specifies the allowed drag/drop effects for the <see cref="IDataObject"/>.</returns> /// <remarks> /// The default implementation of this method simply return <c>DragDropEffects.None</c>. /// Override this method to initialize the <see cref="IDataObject"/> with text and/or other data formats /// (such as <see cref="TreeListBox.ItemDataFormat"/> for supporting drag/drop), /// and to return an appropriate <c>DragDropEffects</c> result. /// </remarks> public override DragDropEffects InitializeDataObject(TreeListBox sourceControl, IDataObject dataObject, IEnumerable <object> items) { if (sourceControl == null) { throw new ArgumentNullException("sourceControl"); } if (dataObject == null) { throw new ArgumentNullException("dataObject"); } if (items == null) { throw new ArgumentNullException("items"); } // Store the full paths to items in case we drop on the tree itself... // Each item needs to have a unique path, which comes from adapter GetPath() calls var fullPaths = new StringBuilder(); foreach (var item in items) { fullPaths.AppendLine(sourceControl.GetFullPath(item)); } if (fullPaths.Length > 0) { dataObject.SetData(TreeListBox.ItemDataFormat, fullPaths.ToString()); } // If there is one item, store its text so that it can be dropped elsewhere if (items.Count() == 1) { var viewModel = items.First() as TreeNodeModel; if (viewModel != null) { dataObject.SetData(DataFormats.Text, viewModel.Name); } } return(DragDropEffects.Move); }
/// <summary> /// Tries to get the category and control models for the specified item. /// </summary> /// <param name="toolboxControl">The <see cref="TreeListBox"/> control.</param> /// <param name="item">The item to examine.</param> /// <param name="categoryModel">The resulting <see cref="CategoryTreeNodeModel"/>.</param> /// <param name="controlModel">The resulting <see cref="ControlTreeNodeModel"/>.</param> /// <returns> /// <c>true</c> if results were found; otherwise, <c>false</c>. /// </returns> private bool TryGetModelsFromItem(TreeListBox toolboxControl, object item, out CategoryTreeNodeModel categoryModel, out ControlTreeNodeModel controlModel) { if (item is ControlTreeNodeModel localControlModel) { controlModel = localControlModel; return(TryGetCategory(toolboxControl, controlModel, out categoryModel)); } else if (item is EmptyPlaceholderTreeNodeModel emptyPlaceholderModel) { controlModel = null; return(TryGetCategory(toolboxControl, emptyPlaceholderModel, out categoryModel)); } else if (item is CategoryTreeNodeModel localCategoryModel) { controlModel = null; categoryModel = localCategoryModel; return(true); } categoryModel = null; controlModel = null; return(false); }
/// <summary> /// Returns the item's text by which to match when searching. /// </summary> /// <param name="ownerControl">The owner control.</param> /// <param name="item">The item to examine.</param> /// <returns>The item's string search text.</returns> public override string GetSearchText(TreeListBox ownerControl, object item) { var model = item as TreeNodeModel; return(model != null ? model.Name: null); }
/// <summary> /// Notifies that a drop event occurred over a target item and requests that appropriate updates are made to the supplied <c>DragEventArgs</c>. /// </summary> /// <param name="e">The <see cref="DragEventArgs"/> whose effects should be updated.</param> /// <param name="targetControl">The target control, over which the event occurred</param> /// <param name="targetItem">The target item, which could be <see langword="null"/> if dragging below the last tree item.</param> /// <param name="dropArea">A <see cref="TreeItemDropArea"/> indicating the drop area over the target item.</param> /// <remarks> /// The default implementation of this method sets the <c>e.Effects</c> to <c>DragDropEffects.None</c> and takes no further action. /// Override this method if you wish to support dropping and add logic to properly handle the dragged data. /// </remarks> public override void OnDrop(DragEventArgs e, TreeListBox targetControl, object targetItem, TreeItemDropArea dropArea) { var originalEffects = e.Effects; e.Effects = DragDropEffects.None; // If the drag is over an item and there is item data present... var targetModel = targetItem as TreeNodeModel; if ((targetModel != null) && (dropArea != TreeItemDropArea.None) && (e.Data.GetDataPresent(TreeListBox.ItemDataFormat))) { // Resolve the real target item (in case the drop area is above or below the target item) var targetDropIndex = targetModel.Children.Count; switch (dropArea) { case TreeItemDropArea.Before: case TreeItemDropArea.After: var nav = targetControl.GetItemNavigator(targetItem); if (nav != null) { var targetChildModel = targetModel; var targetChildItem = targetItem; if (!nav.GoToParent()) { return; } targetItem = nav.CurrentItem; targetModel = targetItem as TreeNodeModel; if (targetModel == null) { return; } var index = targetModel.Children.IndexOf(targetChildModel); if (index != -1) { targetDropIndex = index + (dropArea == TreeItemDropArea.After ? 1 : 0); } } break; } // Get the items var fullPaths = e.Data.GetData(TreeListBox.ItemDataFormat) as string; if (!string.IsNullOrEmpty(fullPaths)) { // Locate items based on full path var items = new List <object>(); foreach (var fullPath in fullPaths.Split(new char[] { '\r', '\n' })) { if (!string.IsNullOrEmpty(fullPath)) { var item = targetControl.GetItemByFullPath(fullPath); if (item != null) { items.Add(item); } } } if (items.Count > 0) { // Check each item and validate that various drop operations are allowed before actually executing the drop foreach (var item in items) { if (item == targetItem) { MessageBox.Show("Cannot drop an item on itself.", "Drag and Drop", MessageBoxButton.OK); return; } else { var nav = targetControl.GetItemNavigator(item); if (nav == null) { MessageBox.Show("Cannot drop from a different control.", "Drag and Drop", MessageBoxButton.OK); return; } else { if (nav.GoToCommonAncestor(targetItem)) { if (nav.CurrentItem == item) { MessageBox.Show("Cannot drop onto a descendant item.", "Drag and Drop", MessageBoxButton.OK); return; } } } } } // Only support a single effect (you could add support for other effects like Copy if the Ctrl key is down here) if ((originalEffects & DragDropEffects.Move) == DragDropEffects.Move) { e.Effects = DragDropEffects.Move; e.Handled = true; } // Move items foreach (var item in items) { var nav = targetControl.GetItemNavigator(item); if (nav.GoToParent()) { var itemModel = item as TreeNodeModel; var parentModel = nav.CurrentItem as TreeNodeModel; if ((itemModel != null) && (parentModel != null)) { var index = parentModel.Children.IndexOf(itemModel); if (index != -1) { if ((parentModel == targetModel) && (index < targetDropIndex)) { targetDropIndex--; } parentModel.Children.RemoveAt(index); } else { break; } } else { break; } targetModel.Children.Insert(Math.Max(0, Math.Min(targetModel.Children.Count, targetDropIndex++)), itemModel); targetModel.IsExpanded = true; // Focus the last item if (items[items.Count - 1] == item) { targetControl.FocusItem(itemModel); } } } } } } }
/// <summary> /// Gets an enumerable of all dragged items. /// </summary> /// <param name="dataObject">The <see cref="IDataObject"/> which defines the dragged items.</param> /// <param name="control">The control from which items will be derived.</param> /// <returns>Returns an enumerable of all dragged items which will be empty if no items were found.</returns> internal static IEnumerable <ToolboxTreeNodeModel> GetDraggedModels(IDataObject dataObject, TreeListBox control) { string format = TreeListBox.ItemDataFormat; if (dataObject.GetDataPresent(format)) { string data = dataObject.GetData(format) as string; if (!string.IsNullOrWhiteSpace(data)) { foreach (var fullPath in SplitItemFullPaths(data)) { if (control.GetItemByFullPath(fullPath) is ToolboxTreeNodeModel itemModel) { yield return(itemModel); } } } } }
/// <summary> /// Notifies that a drop event occurred over a target item and requests that appropriate updates are made to the supplied <c>DragEventArgs</c>. /// </summary> /// <param name="e">The <see cref="DragEventArgs"/> whose effects should be updated.</param> /// <param name="targetControl">The target control, over which the event occurred</param> /// <param name="targetItem">The target item, which could be <see langword="null"/> if dragging below the last tree item.</param> /// <param name="dropArea">A <see cref="TreeItemDropArea"/> indicating the drop area over the target item.</param> /// <remarks> /// The default implementation of this method sets the <c>e.Effects</c> to <c>DragDropEffects.None</c> and takes no further action. /// Override this method if you wish to support dropping and add logic to properly handle the dragged data. /// </remarks> public override void OnDrop(DragEventArgs e, TreeListBox targetControl, object targetItem, TreeItemDropArea dropArea) { var originalEffects = e.Effects; e.Effects = DragDropEffects.None; // If the drag is over an item and there is item data present... var targetModel = targetItem as ToolboxTreeNodeModel; if ((targetModel != null) && (dropArea != TreeItemDropArea.None) && (e.Data.GetDataPresent(TreeListBox.ItemDataFormat))) { // Resolve the real target item (in case the drop area is above or below the target item) var targetDropIndex = targetModel.Children.Count; switch (dropArea) { case TreeItemDropArea.Before: case TreeItemDropArea.After: // When dropping before or after, a new node will be inserted as a child to the parent of the target var nav = targetControl.GetItemNavigator(targetItem); if (nav != null) { // Cache the original target var targetChildModel = targetModel; // Quit if unable to move to navigate to the parent if (!nav.GoToParent()) { return; } // Update the target item to be the parent of the original target targetItem = nav.CurrentItem; targetModel = targetItem as ToolboxTreeNodeModel; // Quit if the new target is not the expected model if (targetModel == null) { return; } // Resolve index of the new node based on whether it should be before // or after the original target. var index = targetModel.Children.IndexOf(targetChildModel); if (index != -1) { targetDropIndex = index + (dropArea == TreeItemDropArea.After ? 1 : 0); } } break; } // Resolve the category for the drop target if (!TryGetCategory(targetControl, targetModel, out var targetCategoryModel)) { MessageBox.Show("Unable to determine the drop category.", "Drag and Drop", MessageBoxButton.OK); return; } // Get the dragged controls (only control nodes are currently supported) List <ControlTreeNodeModel> sourceControlModels = GetDraggedModels(e.Data, targetControl) .OfType <ControlTreeNodeModel>() .ToList(); if (sourceControlModels.Count > 0) { // Check each item and validate that various drop operations are allowed before actually executing the drop foreach (var sourceModel in sourceControlModels) { if (sourceModel == targetModel) { MessageBox.Show("Cannot drop an item on itself.", "Drag and Drop", MessageBoxButton.OK); return; } else { var nav = targetControl.GetItemNavigator(sourceModel); if (nav == null) { MessageBox.Show("Cannot drop from a different control.", "Drag and Drop", MessageBoxButton.OK); return; } else { if (nav.GoToCommonAncestor(targetItem)) { if (nav.CurrentItem == sourceModel) { MessageBox.Show("Cannot drop onto a descendant item.", "Drag and Drop", MessageBoxButton.OK); return; } } } } } // Only support a single effect (you could add support for other effects like Copy if the Ctrl key is down here) if ((originalEffects & DragDropEffects.Move) == DragDropEffects.Move) { e.Effects = DragDropEffects.Move; e.Handled = true; } else if ((originalEffects & DragDropEffects.Copy) == DragDropEffects.Copy) { e.Effects = DragDropEffects.Copy; e.Handled = true; } if (IsFavoritesCategory(targetCategoryModel)) { // Controls dragged into favorites category will not actually be moved. They will be added as favorites. foreach (var sourceControlModel in sourceControlModels) { sourceControlModel.IsFavorite = true; } } else { // Complete operation bool isMove = e.Effects == DragDropEffects.Move; foreach (var sourceControlModel in sourceControlModels) { // Resolve the source category of the dragged control if (!TryGetCategory(targetControl, sourceControlModel, out var sourceCategoryModel)) { break; } if (isMove) { // Remove the control from the original category var index = sourceCategoryModel.Children.IndexOf(sourceControlModel); if (index != -1) { if ((sourceCategoryModel == targetCategoryModel) && (index < targetDropIndex)) { targetDropIndex--; } sourceCategoryModel.Children.RemoveAt(index); } else { // Quit processing if any source cannot be located break; } // Add the control to the new category (may be the same as the source if it was repositioned) int resolvedTargetDropIndex = Math.Max(0, Math.Min(targetCategoryModel.Children.Count, targetDropIndex++)); targetCategoryModel.Children.Insert(resolvedTargetDropIndex, sourceControlModel); targetCategoryModel.IsExpanded = true; // Focus the last item that was moved if (sourceControlModels[sourceControlModels.Count - 1] == sourceControlModel) { // Must dispatch the focus changes to allow the view to update based on changes in the models Dispatcher.CurrentDispatcher.BeginInvoke((Action)(() => { targetControl.FocusItem(sourceControlModel); }), DispatcherPriority.Normal); } } } } } } }
/// <summary> /// Notifies that a drag event occurred over a target item, requests that appropriate updates are made to the supplied <c>DragEventArgs</c>, /// and requests that the allowed drop area is returned for visual target feedback. /// </summary> /// <param name="e">The <see cref="DragEventArgs"/> whose effects should be updated.</param> /// <param name="targetControl">The target control, over which the event occurred</param> /// <param name="targetItem">The target item, which could be <see langword="null"/> if dragging below the last tree item.</param> /// <param name="dropArea">A <see cref="TreeItemDropArea"/> indicating the drop area over the target item.</param> /// <returns> /// A <see cref="TreeItemDropArea"/> indicating the allowed drop area, which will be used for visual feedback to the end user. /// Return <c>TreeItemDropArea.None</c> for no visual feedback. /// </returns> /// <remarks> /// The default implementation of this method sets the <c>e.Effects</c> to <c>DragDropEffects.None</c> and returns that no drop area is allowed. /// Override this method if you wish to support dropping and add logic to properly handle the dragged data. /// </remarks> public override TreeItemDropArea OnDragOver(DragEventArgs e, TreeListBox targetControl, object targetItem, TreeItemDropArea dropArea) { // If the drag is over an item, item data present, and the drop target is recognized... if ((dropArea != TreeItemDropArea.None) && (e.Data.GetDataPresent(TreeListBox.ItemDataFormat)) && (TryGetModelsFromItem(targetControl, targetItem, out var targetCategoryModel, out _))) { // Locate the dragged model. If multiple objects were selected, only the first object is used for reference. // The dragged item must be defined in the target control for the drag operation to be allowed. var sourceItem = GetDraggedModels(e.Data, targetControl).FirstOrDefault(); if ((sourceItem != null) && (TryGetModelsFromItem(targetControl, sourceItem, out var sourceCategoryModel, out _))) { if (IsFavoritesCategory(sourceCategoryModel)) { // Dragging from the "Favorites" category is not supported within the Toolbox control. Favorites can only // be dragged outside of the control (i.e. to the designer surface). e.Effects = DragDropEffects.None; e.Handled = true; } else if (IsFavoritesCategory(targetCategoryModel)) { // Dropping on the "Favorites" category will only change if the control is a favorite and will not // alter the location of the source control in the toolbox, so use the 'Copy' effect. if ((e.AllowedEffects & DragDropEffects.Copy) == DragDropEffects.Copy) { e.Effects = DragDropEffects.Copy; e.Handled = true; } } else { // Controls cannot exist in more than one category, so only move effect is allowed if ((e.AllowedEffects & DragDropEffects.Move) == DragDropEffects.Move) { e.Effects = DragDropEffects.Move; e.Handled = true; } } if ((e.Handled) && (e.Effects != DragDropEffects.None)) { // TreeItems can be dropped 'Before', 'On', or 'After' a target node, but drop targets may not support // all of these scenarios. Coerce the drop area, as needed, based on the target node. if (targetItem is CategoryTreeNodeModel) { // Since controls can only be children to a category, not siblings, coerce all drops to 'On' dropArea = TreeItemDropArea.On; } else if (targetItem is EmptyPlaceholderTreeNodeModel) { // When the control is dropped, the empty placeholder will be remove and the dropped control // will be added as first child to the category, so coerce all drops to 'Before' the placeholder. dropArea = TreeItemDropArea.Before; } else if ((targetItem is ControlTreeNodeModel) && (dropArea == TreeItemDropArea.On)) { // A control cannot have children, so coerce dropping 'On' a control to 'After' (i.e. next sibling) dropArea = TreeItemDropArea.After; } return(dropArea); } } } e.Effects = DragDropEffects.None; return(TreeItemDropArea.None); }
public void TestPopulationOfChildren() { AvalonTestRunner.RunInSTA(delegate { //create sample data MockHierarchalObject dataSource = new MockHierarchalObject("A", new List <MockHierarchalObject>( new MockHierarchalObject[] { //children new MockHierarchalObject( "child1 of A"), new MockHierarchalObject( "child2 of A"), new MockHierarchalObject( "child3 of A"), })); //prepare the parent collection TreeListBox parentControl = new TreeListBox(); parentControl.ChildItemSourcePath = "Children"; parentControl.ExposedGenerateItemsSource(new MockHierarchalObject[] { new MockHierarchalObject( "First, item with no children") , //add a simple item that has no children dataSource, //add the item that has children to test on new MockHierarchalObject( "Last, item with no children") }); Assert.AreEqual(3, parentControl.Items.Count, "Invalid number of items"); //create an item to test TreeListBoxItem item = new TreeListBoxItem(parentControl); item.ExposePrepareItem(parentControl.Items[1] as TreeListBoxInfo); //pass the info that was create for the item that has children //expand the item this should result in the items source of the parent include the children item.IsExpanded = true; //3 root nodes and 3 children are suppose to be in the list Assert.AreEqual(3 + 3, parentControl.Items.Count, "Invalid number of items"); //check that the items where create in the right indeces Assert.AreEqual("child1 of A", ((MockHierarchalObject) ((TreeListBoxInfo)parentControl.Items[2]).DataItem).Text, "Child Item was placed in the wrong index"); //validate that the last item(which is index to of the root nodes) of the root nodes has been moved to the proper index //2 is the index in the root nodes, 3 is the count of the children, so the last item should have moved to index 5 Assert.AreEqual("Last, item with no children", ((MockHierarchalObject) ((TreeListBoxInfo)parentControl.Items[2 + 3]).DataItem). Text, "Root Item was placed in the wrong index"); //validate that the level of the items has been incremented //start from index 2, since the children are place at index 2 for (int i = 2; i < 3 + 2; i++) { Assert.AreEqual(1, ((TreeListBoxInfo)parentControl.Items[i]).Level, "Invalid level set for child nodes"); } //Validate the Collapse item.IsExpanded = false; //all children should be dropped Assert.AreEqual(3, parentControl.Items.Count, "Invalid number of items"); //check that the indeces of the items is back to normal Assert.AreEqual("First, item with no children", ((MockHierarchalObject) ((TreeListBoxInfo)parentControl.Items[0]).DataItem).Text, "Root Item was placed in the wrong index"); Assert.AreEqual("A", ((MockHierarchalObject) ((TreeListBoxInfo)parentControl.Items[1]).DataItem).Text, "Root Item was placed in the wrong index"); Assert.AreEqual("Last, item with no children", ((MockHierarchalObject) ((TreeListBoxInfo)parentControl.Items[2]).DataItem).Text, "Root Item was placed in the wrong index"); }); }
/// <summary> /// Returns whether the specified item is currently being edited. /// </summary> /// <param name="ownerControl">The owner control.</param> /// <param name="item">The item to examine.</param> /// <returns> /// <c>true</c> if the specified item is currently being edited; otherwise, <c>false</c>. /// </returns> public override bool GetIsEditing(TreeListBox ownerControl, object item) { var model = item as TreeNodeModel; return(model != null ? model.IsEditing : false); }
///////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC PROCEDURES ///////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Returns an <see cref="IEnumerable"/> that will be used to provide child items for the specified parent item. /// </summary> /// <param name="ownerControl">The owner control.</param> /// <param name="item">The item to examine.</param> /// <returns>An <see cref="IEnumerable"/> that will be used to provide child items for the specified parent item.</returns> public override IEnumerable GetChildren(TreeListBox ownerControl, object item) { var model = item as TreeNodeModel; return(model != null ? model.Children : null); }
/// <summary> /// Notifies that a drag event occurred over a target item, requests that appropriate updates are made to the supplied <c>DragEventArgs</c>, /// and requests that the allowed drop area is returned for visual target feedback. /// </summary> /// <param name="e">The <see cref="DragEventArgs"/> whose effects should be updated.</param> /// <param name="targetControl">The target control, over which the event occurred</param> /// <param name="targetItem">The target item, which could be <see langword="null"/> if dragging below the last tree item.</param> /// <param name="dropArea">A <see cref="TreeItemDropArea"/> indicating the drop area over the target item.</param> /// <returns> /// A <see cref="TreeItemDropArea"/> indicating the allowed drop area, which will be used for visual feedback to the end user. /// Return <c>TreeItemDropArea.None</c> for no visual feedback. /// </returns> /// <remarks> /// The default implementation of this method sets the <c>e.Effects</c> to <c>DragDropEffects.None</c> and returns that no drop area is allowed. /// Override this method if you wish to support dropping and add logic to properly handle the dragged data. /// </remarks> public override TreeItemDropArea OnDragOver(DragEventArgs e, TreeListBox targetControl, object targetItem, TreeItemDropArea dropArea) { // If the drag is over an item and there is item data present... if ((targetItem != null) && (dropArea != TreeItemDropArea.None) && (e.Data.GetDataPresent(TreeListBox.ItemDataFormat))) { var fullPaths = e.Data.GetData(TreeListBox.ItemDataFormat) as string; if (!string.IsNullOrEmpty(fullPaths)) { // Locate the first item based on full path object firstItem = null; foreach (var fullPath in fullPaths.Split(new char[] { '\r', '\n' })) { if (!string.IsNullOrEmpty(fullPath)) { var item = targetControl.GetItemByFullPath(fullPath); if (item != null) { firstItem = item; break; } } } if (firstItem != null) { // Ensure that the first item is already in the target control (nav will be null if not)... if allowing drag/drop onto external // controls, you cannot use the item navigator and must rely on your own item hierarchy logic var firstItemNav = targetControl.GetItemNavigator(firstItem); if (firstItemNav != null) { // Only support a single effect (you could add support for other effects like Copy if the Ctrl key is down here) if ((e.AllowedEffects & DragDropEffects.Move) == DragDropEffects.Move) { e.Effects = DragDropEffects.Move; e.Handled = true; } switch (e.Effects) { case DragDropEffects.Move: // Coerce the resulting drop-area so that if dragging 'after' an item that has a next sibling, the drop area // becomes 'on' the item instead... can still get between the items by dragging 'before' the next sibling in this scenario if (dropArea == TreeItemDropArea.After) { var targetItemNav = targetControl.GetItemNavigator(targetItem); if ((targetItemNav != null) && (targetItemNav.GoToNextSibling())) { dropArea = TreeItemDropArea.On; } } return(dropArea); } } } } } e.Effects = DragDropEffects.None; return(TreeItemDropArea.None); }
/// <summary> /// Returns whether the specified item is capable of being selected. /// </summary> /// <param name="ownerControl">The owner control.</param> /// <param name="item">The item to examine.</param> /// <returns> /// <c>true</c> if the specified item is capable of being selected; otherwise, <c>false</c>. /// </returns> public override bool GetIsSelectable(TreeListBox ownerControl, object item) { var model = item as TreeNodeModel; return(model != null ? model.IsSelectable : true); }
public void TestPopulationOfChildrenFromNotifications() { AvalonTestRunner.RunInSTA(delegate { //children collection ObservableCollection <MockHierarchalObject> childNodes = new ObservableCollection <MockHierarchalObject>( new List <MockHierarchalObject>(new MockHierarchalObject[] { //children new MockHierarchalObject( "child1 of A"), new MockHierarchalObject( "child2 of A"), new MockHierarchalObject( "child3 of A"), })); //create sample data MockHierarchalObject dataSource = new MockHierarchalObject("A", childNodes); //prepare the parent collection TreeListBox parentControl = new TreeListBox(); parentControl.ChildItemSourcePath = "Children"; parentControl.ExposedGenerateItemsSource(new MockHierarchalObject[] { new MockHierarchalObject( "First, item with no children") , //add a simple item that has no children dataSource, //add the item that has children to test on new MockHierarchalObject( "Last, item with no children") }); Assert.AreEqual(3, parentControl.Items.Count, "Invalid number of items"); //create an item to test TreeListBoxItem item = new TreeListBoxItem(parentControl); item.ExposePrepareItem(parentControl.Items[1] as TreeListBoxInfo); //pass the info that was create for the item that has children //expand the item this should result in the items source of the parent include the children item.IsExpanded = true; //3 root nodes and 3 children are suppose to be in the list Assert.AreEqual(3 + 3, parentControl.Items.Count, "Invalid number of items"); //add a new item to child collection and check if it is added childNodes.Add(new MockHierarchalObject("child4 of A")); //3 root nodes and 3 + 1(which is the new one) children are suppose to be in the list Assert.AreEqual(3 + childNodes.Count, parentControl.Items.Count, "Invalid number of items"); //check the index of the new item in the flat list //1 is the index of the container item, and 4 is the count of the children Assert.AreEqual("child4 of A", ((MockHierarchalObject) ((TreeListBoxInfo)parentControl.Items[1 + 4]).DataItem). Text, "Item was not added in the right index"); //check that the insert in a specific index childNodes.Insert(2, new MockHierarchalObject("inserted child of A")); Assert.AreEqual(3 + childNodes.Count, parentControl.Items.Count, "Invalid number of items"); //check that the insert was done in the right index(where 1 is the parent index and 2 is the index in the children collection) Assert.AreEqual("inserted child of A", ((MockHierarchalObject) ((TreeListBoxInfo)parentControl.Items[1 + 3]).DataItem). Text, "Item was not added in the right index"); //check the remove of items childNodes.RemoveAt(2); //check the Count Assert.AreEqual(3 + childNodes.Count, parentControl.Items.Count, "Invalid number of items added"); //validate that the item was deleted properly for (int i = 0; i < parentControl.Items.Count; i++) { Assert.AreNotEqual("inserted child of A", ((MockHierarchalObject) ((TreeListBoxInfo)parentControl.Items[i]). DataItem).Text, "Item was removed from the wrong index"); } //check that if we collapse the items no children are add item.IsExpanded = false; //check that the items where collapsed Assert.AreEqual(3, parentControl.Items.Count, "Invalid number of items after collapse item"); childNodes.Add(new MockHierarchalObject("Test collapse")); //check that the item was not added to the flat list since the item is collapsed Assert.AreEqual(3, parentControl.Items.Count, "Invalid number of items after collapse item"); //check that when you re expand the item all items are generated item.IsExpanded = true; Assert.AreEqual(3 + childNodes.Count, parentControl.Items.Count, "Invalid number of items after collapse item"); }); }