private static ListBoxItem GetVisibleCategory(ListBox parent, int startIndex, Key key) { if (parent.Equals(null)) { return(null); } var index = startIndex; var generator = parent.ItemContainerGenerator; var category = generator.ContainerFromIndex(index) as ListBoxItem; while (category != null && !WpfUtilities.ChildOfType <Expander>(category, string.Empty).IsExpanded) { if (key == Key.Down) { index++; } if (key == Key.Up) { index--; } if (0 <= index && index < parent.Items.Count) { category = generator.ContainerFromIndex(index) as ListBoxItem; } else { category = null; } } return(category); }
private ListBoxItem FindChildListItemByIndex(FrameworkElement parent, string listName, int index = 0) { var list = WpfUtilities.ChildOfType <ListBox>(parent, listName); var generator = list.ItemContainerGenerator; if (0 <= index && index < list.Items.Count) { return(generator.ContainerFromIndex(index) as ListBoxItem); } else { return(null); } }
private ListBoxItem FindFirstVisibleCategory(FrameworkElement librarySearchViewElement) { var firstCategory = FindChildListItemByIndex(librarySearchViewElement, "CategoryListView"); int index = 1; while (firstCategory != null && !WpfUtilities.ChildOfType <Expander>(firstCategory, string.Empty).IsExpanded) { firstCategory = FindChildListItemByIndex(librarySearchViewElement, "CategoryListView", index); index++; } return(firstCategory); }
void DynamoViewModelRequestSaveImage(object sender, ImageSaveEventArgs e) { if (!string.IsNullOrEmpty(e.Path)) { var control = WpfUtilities.ChildOfType <DragCanvas>(this, null); double width = 1; double height = 1; // connectors are most often within the bounding box of the nodes and notes foreach (NodeModel n in dynamoViewModel.Model.CurrentWorkspace.Nodes) { width = Math.Max(n.X + n.Width, width); height = Math.Max(n.Y + n.Height, height); } foreach (NoteModel n in dynamoViewModel.Model.CurrentWorkspace.Notes) { width = Math.Max(n.X + n.Width, width); height = Math.Max(n.Y + n.Height, height); } var rtb = new RenderTargetBitmap(Math.Max(1, (int)width), Math.Max(1, (int)height), 96, 96, PixelFormats.Default); rtb.Render(control); //endcode as PNG var pngEncoder = new PngBitmapEncoder(); pngEncoder.Frames.Add(BitmapFrame.Create(rtb)); try { using (var stm = File.Create(e.Path)) { pngEncoder.Save(stm); } } catch { dynamoViewModel.Model.Logger.Log("Failed to save the Workspace an image."); } } }
/// When a category is collapsed, the selection of underlying sub-category /// list is cleared. As a result any visible StandardPanel will be hidden. private void OnExpanderCollapsed(object sender, System.Windows.RoutedEventArgs e) { BringIntoViewCount++; var expanderContent = (sender as FrameworkElement); expanderContent.BringIntoView(new Rect(0.0, 0.0, 100.0, 20.0)); var buttons = WpfUtilities.ChildOfType <ListView>(expanderContent); if (buttons != null) { buttons.UnselectAll(); } e.Handled = true; }
internal void SaveWorkspaceAsImage(string path) { var initialized = false; var bounds = new Rect(); double minX = 0.0, minY = 0.0; var dragCanvas = WpfUtilities.ChildOfType <DragCanvas>(this); var childrenCount = VisualTreeHelper.GetChildrenCount(dragCanvas); for (int index = 0; index < childrenCount; ++index) { var child = VisualTreeHelper.GetChild(dragCanvas, index); var firstChild = VisualTreeHelper.GetChild(child, 0); switch (firstChild.GetType().Name) { case "NodeView": case "NoteView": case "AnnotationView": break; // Until we completely removed InfoBubbleView (or fixed its broken // size calculation), we will not be including it in our size // calculation here. This means that the info bubble, if any, will // still go beyond the boundaries of the final PNG file. I would // prefer not to add this hack here as it introduces multiple issues // (including NaN for Grid inside the view and the fix would be too // ugly to type in). Suffice to say that InfoBubbleView is not // included in the size calculation for screen capture (work-around // should be obvious). // // case "InfoBubbleView": // child = WpfUtilities.ChildOfType<Grid>(child); // break; // We do not take anything other than those above // into consideration when the canvas size is measured. default: continue; } // Determine the smallest corner of all given visual elements on the // graph. This smallest top-left corner value will be useful in making // the offset later on. // var childBounds = VisualTreeHelper.GetDescendantBounds(child as Visual); minX = childBounds.X < minX ? childBounds.X : minX; minY = childBounds.Y < minY ? childBounds.Y : minY; childBounds.X = (double)(child as Visual).GetValue(Canvas.LeftProperty); childBounds.Y = (double)(child as Visual).GetValue(Canvas.TopProperty); if (initialized) { bounds.Union(childBounds); } else { initialized = true; bounds = childBounds; } } // Nothing found in the canvas, bail out. if (!initialized) { return; } // Add padding to the edge and make them multiples of two (pad 10px on each side). bounds.Width = 20 + ((((int)Math.Ceiling(bounds.Width)) + 1) & ~0x01); bounds.Height = 20 + ((((int)Math.Ceiling(bounds.Height)) + 1) & ~0x01); var currentTransformGroup = WorkspaceElements.RenderTransform as TransformGroup; WorkspaceElements.RenderTransform = new TranslateTransform(10.0 - bounds.X - minX, 10.0 - bounds.Y - minY); WorkspaceElements.UpdateLayout(); var rtb = new RenderTargetBitmap(((int)bounds.Width), ((int)bounds.Height), 96, 96, PixelFormats.Default); rtb.Render(WorkspaceElements); WorkspaceElements.RenderTransform = currentTransformGroup; try { using (var stm = System.IO.File.Create(path)) { // Encode as PNG format var pngEncoder = new PngBitmapEncoder(); pngEncoder.Frames.Add(BitmapFrame.Create(rtb)); pngEncoder.Save(stm); } } catch (Exception) { } }
internal void SaveWorkspaceAsImage(string path) { var initialized = false; var bounds = new Rect(); var dragCanvas = WpfUtilities.ChildOfType <DragCanvas>(this); var childrenCount = VisualTreeHelper.GetChildrenCount(dragCanvas); for (int index = 0; index < childrenCount; ++index) { var child = VisualTreeHelper.GetChild(dragCanvas, index); var firstChild = VisualTreeHelper.GetChild(child, 0); if ((!(firstChild is NodeView)) && (!(firstChild is NoteView)) && (!(firstChild is AnnotationView))) { continue; } var childBounds = VisualTreeHelper.GetDescendantBounds(child as Visual); childBounds.X = (double)(child as Visual).GetValue(Canvas.LeftProperty); childBounds.Y = (double)(child as Visual).GetValue(Canvas.TopProperty); if (initialized) { bounds.Union(childBounds); } else { initialized = true; bounds = childBounds; } } // Nothing found in the canvas, bail out. if (!initialized) { return; } // Add padding to the edge and make them multiples of two (pad 10px on each side). bounds.Width = 20 + ((((int)Math.Ceiling(bounds.Width)) + 1) & ~0x01); bounds.Height = 20 + ((((int)Math.Ceiling(bounds.Height)) + 1) & ~0x01); var currentTransformGroup = WorkspaceElements.RenderTransform as TransformGroup; WorkspaceElements.RenderTransform = new TranslateTransform(10.0 - bounds.X, 10.0 - bounds.Y); WorkspaceElements.UpdateLayout(); var rtb = new RenderTargetBitmap(((int)bounds.Width), ((int)bounds.Height), 96, 96, PixelFormats.Default); rtb.Render(WorkspaceElements); WorkspaceElements.RenderTransform = currentTransformGroup; try { using (var stm = System.IO.File.Create(path)) { // Encode as PNG format var pngEncoder = new PngBitmapEncoder(); pngEncoder.Frames.Add(BitmapFrame.Create(rtb)); pngEncoder.Save(stm); } } catch (Exception) { } }
/// <summary> /// 'CategoryListView' element contains the following child elements (either /// directly, or indirectly nested): 'StackPanel', 'SubCategoryListView', /// 'MemberGroupsListBox' and 'MembersListBox'. If none of these child elements /// choose to process the key event, it gets bubbled up here. This typically /// happens for the following scenarios: /// /// 1. Down key is pressed when selection is on last entry of 'MembersListBox' /// 2. Up key is pressed when selection is on item on first row of 'SubCategoryListView' /// 3. Up key is pressed when selection is on the first entry of 'MembersListBox' /// and there are no classes. /// /// </summary> private void OnCategoryKeyDown(object sender, KeyEventArgs e) { if ((e.Key != Key.Down) && (e.Key != Key.Up)) { return; } // Selected member(in this scenario) can be only first/last member button or class button at the first row. var selectedMember = HighlightedItem; var selectedMemberContext = selectedMember.DataContext as NodeSearchElementViewModel; var categoryListView = sender as ListView; int categoryIndex = 0; for (int i = 0; i < categoryListView.Items.Count; i++) { var category = categoryListView.Items[i] as SearchCategory; if (category.ContainsClassOrMember(selectedMemberContext.Model)) { categoryIndex = i; break; } } if (e.Key == Key.Down) { categoryIndex++; } if (e.Key == Key.Up) { categoryIndex--; } // The selection cannot be moved further up, returning here without handling the key event // so that parent visual element gets to handle it and move selection up to 'Top Result' list. if (categoryIndex < 0) { return; } // We are at the last member and there is no way to move down. if (categoryIndex >= categoryListView.Items.Count) { e.Handled = true; return; } var nextSelectedCategory = GetVisibleCategory(categoryListView, categoryIndex, e.Key); if (nextSelectedCategory == null) { e.Handled = e.Key == Key.Down; return; } if (e.Key == Key.Up) { var memberGroupsList = WpfUtilities.ChildOfType <ListBox>(nextSelectedCategory, "MemberGroupsListBox"); var lastMemberGroup = GetListItemByIndex(memberGroupsList, memberGroupsList.Items.Count - 1); var membersList = WpfUtilities.ChildOfType <ListBox>(lastMemberGroup, "MembersListBox"); // If key is up, then we have to select the last method button. UpdateHighlightedItem(GetListItemByIndex(membersList, membersList.Items.Count - 1)); } else // Otherwise, Down was pressed, and we have to select first class/method button. { #if SEARCH_SHOW_CLASSES if (nextselectedCategoryContent.Classes.Count > 0) { // If classes are presented, then focus on first class. FindFirstChildListItem(nextselectedCategory, "SubCategoryListView").Focus(); } else { // If there are no classes, then focus on first method. var memberGroupsList = FindFirstChildListItem(nextselectedCategory, "MemberGroupsListBox"); FindFirstChildListItem(memberGroupsList, "MembersListBox").Focus(); } #else // If there are no classes, then focus on first method. var memberGroupsList = FindChildListItemByIndex(nextSelectedCategory, "MemberGroupsListBox"); UpdateHighlightedItem(FindChildListItemByIndex(memberGroupsList, "MembersListBox")); #endif } e.Handled = true; }
// The 'StackPanel' that contains 'SubCategoryListView' and 'MemberGroupsListBox' // handles this message. If none of these two list boxes are handling the key // message, that means the currently selected list box item is the first/last item // in these two list boxes. When key message arrives here, it is then the 'StackPanel' // responsibility to move the selection on to the adjacent list box. private void OnCategoryContentKeyDown(object sender, KeyEventArgs e) { if ((e.Key != Key.Down) && (e.Key != Key.Up)) { return; } // Selected member(in this scenario) can be only first/last member button or first/last class button. var selectedMember = HighlightedItem as FrameworkElement; var searchCategoryElement = sender as FrameworkElement; // selectedMember is method button. if (selectedMember.DataContext is NodeSearchElementViewModel) { var searchCategoryContent = searchCategoryElement.DataContext as SearchCategory; // Gotten here because of last method being listed, pressing 'down' array cannot // move down further. Return here to allow higher level visual element to handle // the navigation (to a separate category). if (e.Key == Key.Down) { return; } // Otherwise, pressed Key is Up. #if SEARCH_SHOW_CLASSES // No class is found in this 'SearchCategory', return from here so that higher level // element gets to handle the navigational keys to move focus to the previous category. if (searchCategoryContent.Classes.Count == 0) { return; } // Otherwise, we move to first class button. var listItem = FindFirstChildListItem(searchCategoryElement, "SubCategoryListView"); if (listItem != null) { listItem.Focus(); } e.Handled = true; #endif return; } // selectedMember is class button. if (selectedMember.DataContext is NodeCategoryViewModel) { // We are at the first row of class list. User presses up, we have to move to previous category. // We handle it further. if (e.Key == Key.Up) { return; } // Otherwise user pressed down, we have to move to first member button. var memberGroupsListBox = WpfUtilities.ChildOfType <ListBox>(searchCategoryElement, "MemberGroupsListBox"); var listItem = FindChildListItemByIndex(memberGroupsListBox, "MembersListBox"); if (listItem != null) { UpdateHighlightedItem(listItem); } e.Handled = true; return; } }
// This event is raised only, when we can't go down, to next member. // I.e. we are now at the last member button and we have to move to next member group. private void MemberGroupsKeyDown(object sender, KeyEventArgs e) { if ((e.Key != Key.Down) && (e.Key != Key.Up)) { return; } var selectedMember = HighlightedItem.DataContext as NodeSearchElementViewModel; var memberGroups = (sender as ListBox).Items; var memberGroupListBox = sender as ListBox; int selectedMemberGroupIndex = 0; // Find out to which memberGroup selected member belong. for (int i = 0; i < memberGroups.Count; i++) { var memberGroup = memberGroups[i] as SearchMemberGroup; if (memberGroup.ContainsMember(selectedMember)) { selectedMemberGroupIndex = i; break; } } int nextSelectedMemberGroupIndex = selectedMemberGroupIndex; // If user presses down, then we need to set focus to the next member group. // Otherwise to previous. if (e.Key == Key.Down) { nextSelectedMemberGroupIndex++; } if (e.Key == Key.Up) { nextSelectedMemberGroupIndex--; } // The member group list box does not attempt to process the key event if it // has moved beyond its available list of member groups. In this case, the // key event is considered not handled and will be left to the parent visual // (e.g. class button or another category) to handle. e.Handled = false; if (nextSelectedMemberGroupIndex < 0 || (nextSelectedMemberGroupIndex >= memberGroups.Count)) { return; } var item = GetListItemByIndex(memberGroupListBox, nextSelectedMemberGroupIndex); var nextSelectedMembers = WpfUtilities.ChildOfType <ListBox>(item, "MembersListBox"); // When moving on to the next member group list below (by pressing down arrow), // the focus should moved on to the first member in the member group list. Likewise, // when moving to the previous member group list above, the focus should be set on // the last member in that list. var itemIndex = 0; if (e.Key == Key.Up) { itemIndex = nextSelectedMembers.Items.Count - 1; } UpdateHighlightedItem(GetListItemByIndex(nextSelectedMembers, itemIndex)); e.Handled = true; }
private void OnLibraryWrapPanelKeyDown(object sender, System.Windows.Input.KeyEventArgs e) { var classButton = Keyboard.FocusedElement as ListBoxItem; // Enter collapses and expands class button. if (e.Key == Key.Enter) { classButton.IsSelected = !classButton.IsSelected; e.Handled = true; return; } var buttonsWrapPanel = sender as LibraryWrapPanel; var listButtons = buttonsWrapPanel.Children; // If focused element is NodeSearchElement, that means focused element is inside expanded class. if (classButton.DataContext is NodeSearchElement) { // If user presses Up, we have to move back to selected class. if (e.Key == Key.Up) { var selectedClassButton = listButtons.OfType <ListViewItem>(). Where(button => button.IsSelected).FirstOrDefault(); if (selectedClassButton != null) { selectedClassButton.Focus(); } e.Handled = true; return; } // Otherwise, let user move inside expanded class. return; } // If class is selected, we should move down to ClassDetails. else if (e.Key == Key.Down) { if (classButton.IsSelected) { int classInfoIndex = GetClassInformationIndex(); var classInformationView = listButtons[classInfoIndex]; var firstMemberList = WpfUtilities.ChildOfType <ListBox>(classInformationView, "primaryMembers"); var generator = firstMemberList.ItemContainerGenerator; (generator.ContainerFromIndex(0) as ListBoxItem).Focus(); e.Handled = true; return; } } var selectedIndex = listButtons.IndexOf(classButton); int itemsPerRow = (int)Math.Floor(buttonsWrapPanel.ActualWidth / classButton.ActualWidth); int newIndex = GetIndexNextSelectedItem(e.Key, selectedIndex, itemsPerRow); // If index is out of range class list, that means we have to move to previous category // or to next member group. if ((newIndex < 0) || (newIndex > listButtons.Count)) { e.Handled = false; return; } // Set focus on new item. listButtons[newIndex].Focus(); e.Handled = true; return; }