/// <summary> /// Display a notification to display on top right corner of canvas /// and display message passed as param /// </summary> /// <param name="content">The target content to display.</param> internal void CreateRealTimeInfoWindow(string content) { //Search a UIElement with the Name "statusBarPanel" inside the Dynamo VisualTree UIElement hostUIElement = Guide.FindChild(mainRootElement, "statusBarPanel"); // When popup already exist, replace the content if (exitTourPopup != null && exitTourPopup.IsOpen) { exitTourPopup.TextContent = content; } else { // Otherwise creates the RealTimeInfoWindow popup and set up all the needed values // to show the popup over the Dynamo workspace exitTourPopup = new RealTimeInfoWindow() { VerticalOffset = ExitTourVerticalOffset, HorizontalOffset = ExitTourHorizontalOffset, Placement = PlacementMode.Left, TextContent = content }; if (hostUIElement != null) { exitTourPopup.PlacementTarget = hostUIElement; } exitTourPopup.IsOpen = true; } }
/// <summary> /// This method will update the Target in case the UI Automation was executed and the Popup was waiting for a specific Window to be opened /// </summary> internal void UpdatePlacementTarget() { if (stepUIPopup == null) { return; } //This means that the HostPopupInfo.HostUIElementString is in a different Window than DynamoView if (!string.IsNullOrEmpty(HostPopupInfo.WindowName)) { Window ownedWindow = Guide.FindWindowOwned(HostPopupInfo.WindowName, MainWindow as Window); if (ownedWindow == null) { return; } HostPopupInfo.HostUIElement = ownedWindow; stepUIPopup.PlacementTarget = ownedWindow; UpdateLocation(); } //This case will be used for UIElements that are in the Dynamo VisualTree but they are shown until there is a user interaction (like the SideBar cases) var hostUIElement = Guide.FindChild(MainWindow, HostPopupInfo.HostUIElementString); if (hostUIElement == null) { return; } HostPopupInfo.HostUIElement = hostUIElement; stepUIPopup.PlacementTarget = hostUIElement; UpdateLocation(); }
/// <summary> /// This method will update the CutOff rectangle size everytime that the step change /// </summary> /// <param name="bVisible">It will say if the CutOff Area will be disabled or enabled</param> private void SetCutOffSectionSize(bool bVisible) { if (HostPopupInfo.CutOffRectArea == null) { return; } if (bVisible) { //This will validate that HostPopupInfo.HostUIElement is in the MainWindow VisualTree otherwise the TransformToAncestor() will crash var foundUIElement = Guide.FindChild(MainWindow, HostPopupInfo.HostUIElementString); if (foundUIElement == null) { return; } Point relativePoint = HostPopupInfo.HostUIElement.TransformToAncestor(MainWindow) .Transform(new Point(0, 0)); var holeWidth = HostPopupInfo.HostUIElement.DesiredSize.Width + HostPopupInfo.CutOffRectArea.WidthBoxDelta; var holeHeight = HostPopupInfo.HostUIElement.DesiredSize.Height + HostPopupInfo.CutOffRectArea.HeightBoxDelta; if (StepGuideBackground.CutOffBackgroundArea != null) { StepGuideBackground.CutOffBackgroundArea.CutOffRect = new Rect(relativePoint.X, relativePoint.Y, holeWidth, holeHeight); } } else { StepGuideBackground.ClearCutOffSection(); } }
/// <summary> /// This method will return a new HostControlInfo object populated with the information passed as parameter /// Basically this method store the information coming from Step and search the UIElement in the main WPF VisualTree /// </summary> /// <param name="jsonStepInfo">Step that contains all the info deserialized from the Json file</param> /// <returns></returns> private HostControlInfo CreateHostControl(HostControlInfo jsonHostControlInfo) { //We use the HostControlInfo copy construtor var popupInfo = new HostControlInfo(jsonHostControlInfo, mainRootElement); //If the CutOff area was defined in the json file then a section of the background overlay will be removed if (jsonHostControlInfo.CutOffRectArea != null) { popupInfo.CutOffRectArea = new CutOffArea() { WidthBoxDelta = jsonHostControlInfo.CutOffRectArea.WidthBoxDelta, HeightBoxDelta = jsonHostControlInfo.CutOffRectArea.HeightBoxDelta }; } //If the Highlight area was defined in the json file then a rectangle will be highlighted in the Overlay if (jsonHostControlInfo.HighlightRectArea != null) { //We use the HighlightArea copy construtor popupInfo.HighlightRectArea = new HighlightArea(jsonHostControlInfo.HighlightRectArea); } //The host_ui_element read from the json file need to exists otherwise the host will be null UIElement hostUIElement = Guide.FindChild(mainRootElement, popupInfo.HostUIElementString); if (hostUIElement != null) { popupInfo.HostUIElement = hostUIElement; } return(popupInfo); }
/// <summary> /// This method will be executed when packages search window is opened, so it can identify the close button of the window /// </summary> /// <param name="stepInfo"></param> /// <param name="uiAutomationData"></param> /// <param name="enableFunction"></param> /// <param name="currentFlow"></param> internal static void ExecuteClosePackagesSearch(Step stepInfo, StepUIAutomation uiAutomationData, bool enableFunction, GuideFlow currentFlow) { if (enableFunction) { CurrentExecutingStep = stepInfo; if (stepInfo.ExitGuide != null) { exitGuide = stepInfo.ExitGuide; } Window ownedWindow = Guide.FindWindowOwned(stepInfo.HostPopupInfo.WindowName, stepInfo.MainWindow as Window); foreach (var handler in uiAutomationData.AutomaticHandlers) { if (ownedWindow == null) { return; } CloseButtonSearchPackages = Guide.FindChild(ownedWindow, handler.HandlerElement) as Button; CloseButtonSearchPackages.Click += CloseButton_Click; } } else { if (CloseButtonSearchPackages != null) { CloseButtonSearchPackages.Click -= CloseButton_Click; } } }
internal static void EnableNextButton(Step stepInfo, StepUIAutomation uiAutomationData, bool enableFunction, GuideFlow currentFlow) { if (searchPackagesLoaded) { var nextButton = Guide.FindChild((CurrentExecutingStep.StepUIPopup as PopupWindow).mainPopupGrid, uiAutomationData.ElementName) as Button; if (nextButton != null) { nextButton.IsEnabled = true; } } }
/// <summary> /// This method will be executed when the OnGuidedTourStart event is raised /// </summary> /// <param name="args">This parameter will contain the GuideName as a string</param> private void TourStarted(GuidedTourStateEventArgs args) { tourStarted = true; currentGuide = (from guide in Guides where guide.Name.Equals(args.GuideName) select guide).FirstOrDefault(); if (currentGuide != null) { //Show background overlay guideBackgroundElement.Visibility = Visibility.Visible; currentGuide.GuideBackgroundElement = guideBackgroundElement; currentGuide.MainWindow = mainRootElement; currentGuide.LibraryView = Guide.FindChild(mainRootElement, libraryViewName); currentGuide.Initialize(); currentGuide.Play(); GuidesValidationMethods.CurrentExecutingGuide = currentGuide; } }
/// <summary> /// Show the tooltip in the DynamoUI, first execute UI Automation, then Prevalidation, then calculate target host and finally highlight an element /// </summary> internal void Show(Guide.GuideFlow currentFlow) { //In case the UIAutomation info is set for the Step we execute all the UI Automation actions when the Next button is pressed if (UIAutomation != null) { foreach (var automation in UIAutomation) { ExecuteUIAutomationStep(automation, true, currentFlow); } } //If the PreValidation info was read from the json file then is executed and it will decide which Step should be shown and which not if (PreValidationInfo != null) { ExecutePreValidation(); } //After the UI Automation is done we need to calculate the Target (if is not in DynamoView) CalculateTargetHost(); //After the Popup.PlacementTarget was recalculated (or calculated) then we proceed to put the cut off section SetCutOffSectionSize(true); //After UI Automation and calculate the target we need to highlight the element (otherwise probably won't exist) SetHighlightSection(true); if (HostPopupInfo.WindowName != null && HostPopupInfo.WindowName.Equals(nameof(LibraryView))) { var automationStep = (from automation in UIAutomation where automation.Name.Equals(calculateLibraryFuncName) select automation).FirstOrDefault(); GuidesValidationMethods.CalculateLibraryItemLocation(this, automationStep, true, Guide.GuideFlow.CURRENT); } stepUIPopup.IsOpen = true; if (this.StepUIPopup is PopupWindow popupWindow) { if (Guide.FindChild((popupWindow).mainPopupGrid, NextButton) is Button nextbuttonFound) { nextbuttonFound.Focus(); } } }
/// <summary> /// This method will be executed when passing from Step 2 to Step 3 in the Packages guide, so it will show the TermsOfUse Window in case it was not accepted yet /// </summary> /// <param name="stepInfo"></param> /// <param name="uiAutomationData"></param> /// <param name="enableFunction"></param> /// <param name="currentFlow"></param> internal static void ExecuteTermsOfServiceFlow(Step stepInfo, StepUIAutomation uiAutomationData, bool enableFunction, GuideFlow currentFlow) { CurrentExecutingStep = stepInfo; if (stepInfo.ExitGuide != null) { exitGuide = stepInfo.ExitGuide; } //When enableFunction = true, means we want to show the TermsOfUse Window (this is executed in the UIAutomation step in the Show() method) if (enableFunction) { //If the TermsOfService is not accepted yet it will show the TermsOfUseView otherwise it will show the PackageManagerSearchView stepInfo.DynamoViewModelStep.ShowPackageManagerSearch(null); Window ownedWindow = Guide.FindWindowOwned(stepInfo.HostPopupInfo.WindowName, stepInfo.MainWindow as Window); foreach (var handler in uiAutomationData.AutomaticHandlers) { if (ownedWindow == null) { return; } UIElement element = Guide.FindChild(ownedWindow, handler.HandlerElement); //When the Accept button is pressed in the TermsOfUseView then we need to move to the next Step if (element != null) { ManageEventHandler(element, handler.HandlerElementEvent, handler.ExecuteMethod); } } } //When enableFunction = false, means we are hiding (closing) the TermsOfUse Window due that we are moving to the next Step or we are exiting the Guide else { Window ownedWindow = Guide.FindWindowOwned(stepInfo.HostPopupInfo.WindowName, stepInfo.MainWindow as Window); if (ownedWindow == null) { return; } //Tries to close the TermsOfUseView or the PackageManagerSearchView if they were opened previously Guide.CloseWindowOwned(stepInfo.HostPopupInfo.WindowName, stepInfo.MainWindow as Window); } }
/// <summary> /// Creates the background for the GuidedTour /// </summary> private void CreateBackground() { Window mainWindow = Window.GetWindow(mainRootElement); if (guideBackgroundElement == null) { guideBackgroundElement = new GuideBackground(mainWindow) { Name = guideBackgroundName, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, Visibility = Visibility.Hidden }; Grid mainGrid = Guide.FindChild(mainRootElement, mainGridName) as Grid; mainGrid.Children.Add(guideBackgroundElement); Grid.SetColumnSpan(guideBackgroundElement, 5); Grid.SetRowSpan(guideBackgroundElement, 6); } }
/// <summary> /// This method will subscribe the Next button from the Step8 Popup for clicking the Package already installed (then it will be expanded). /// </summary> /// <param name="stepInfo">Information about the Step</param> /// <param name="uiAutomationData">Specific UI Automation step that is being executed</param> /// <param name="enableFunction">it says if the functionality should be enabled or disabled</param> /// <param name="currentFlow">The current flow of the Guide can be FORWARD or BACKWARD</param> internal static void SubscribeNextButtonClickEvent(Step stepInfo, StepUIAutomation uiAutomationData, bool enableFunction, GuideFlow currentFlow) { CurrentExecutingStep = stepInfo; //if there is not handler then the function should return if (uiAutomationData.AutomaticHandlers == null || uiAutomationData.AutomaticHandlers.Count == 0) { return; } //Due that only one handler was configured we get the first one var automaticHandler = uiAutomationData.AutomaticHandlers.FirstOrDefault(); //Find the NextButton inside the Popup var nextbuttonFound = Guide.FindChild((CurrentExecutingStep.StepUIPopup as PopupWindow).mainPopupGrid, automaticHandler.HandlerElement) as Button; if (nextbuttonFound == null) { return; } //Add or Remove the handler assigned to the Button.Click ManageEventHandler(nextbuttonFound, automaticHandler.HandlerElementEvent, automaticHandler.ExecuteMethod, enableFunction); }
/// <summary> /// This method will subcribe the menu "Packages->Search for a Package" MenuItem to the Next event so when is pressed we will moved to the next Step /// </summary> /// <param name="stepInfo">Information about the Step</param> /// <param name="uiAutomationData">Information about UI Automation that is being executed</param> /// <param name="enableFunction">Variable used to know if we are executing the automation or undoing changes</param> /// <param name="currentFlow">Current Guide Flow</param> internal static void SubscribeSearchForPackagesOption(Step stepInfo, StepUIAutomation uiAutomationData, bool enableFunction, GuideFlow currentFlow) { CurrentExecutingStep = stepInfo; //We try to find the WindowElementNameString (in this case the MenuItem) in the DynamoView VisualTree var foundUIElement = Guide.FindChild(CurrentExecutingStep.MainWindow, CurrentExecutingStep.HostPopupInfo.HighlightRectArea.WindowElementNameString) as MenuItem; if (foundUIElement == null) { return; } if (enableFunction) { //Executed then Showing the Step foundUIElement.Click += SearchForPackage_Click; } else { //Just executed when exiting the Guide or when passing to the next Step foundUIElement.Click -= SearchForPackage_Click; } }
/// <summary> /// This method will be executed when passing from Step 6 to Step 7 in the Packages guide, so it will subscribe the install button event /// </summary> /// <param name="stepInfo"></param> /// <param name="uiAutomationData"></param> /// <param name="enableFunction"></param> /// <param name="currentFlow"></param> internal static void ExecuteInstallPackagesFlow(Step stepInfo, StepUIAutomation uiAutomationData, bool enableFunction, GuideFlow currentFlow) { CurrentExecutingStep = stepInfo; Window ownedWindow = Guide.FindWindowOwned(stepInfo.HostPopupInfo.WindowName, stepInfo.MainWindow as Window); if (enableFunction) { if (ownedWindow != null) { viewModel = ownedWindow.DataContext as PackageManagerSearchViewModel; } Button buttonElement = Guide.FindChild(ownedWindow, stepInfo.HostPopupInfo.HostUIElementString) as Button; viewModel.PackageManagerClientViewModel.Downloads.CollectionChanged += Downloads_CollectionChanged; } else { //Tries to close the TermsOfUseView or the PackageManagerSearchView if they were opened previously Guide.CloseWindowOwned(stepInfo.HostPopupInfo.WindowName, stepInfo.MainWindow as Window); } }
/// <summary> /// This method will be used to subscribe a method to the Button.Click event of the ViewDetails button located in the PackageManagerSearch Window /// </summary> /// <param name="stepInfo">Information about the Step</param> /// <param name="uiAutomationData">Information about UI Automation that is being executed</param> /// <param name="enableFunction">Variable used to know if we are executing the automation or undoing changes</param> /// <param name="currentFlow">Current Guide Flow</param> internal static void SubscribeViewDetailsEvent(Step stepInfo, StepUIAutomation uiAutomationData, bool enableFunction, GuideFlow currentFlow) { CurrentExecutingStep = stepInfo; PackageManagerSearchView packageManager = Guide.FindWindowOwned(stepInfo.HostPopupInfo.WindowName, stepInfo.MainWindow as Window) as PackageManagerSearchView; if (packageManager == null) { return; } Button foundElement = Guide.FindChild(packageManager, stepInfo.HostPopupInfo.HighlightRectArea.WindowElementNameString) as Button; if (foundElement == null) { return; } if (enableFunction) { foundElement.Click += ViewDetails_Click; } else { foundElement.Click -= ViewDetails_Click; } }
/// <summary> /// This function will highlight a Window element (the element can be located in DynamoView or another Window or can be a MenuItem /// <param name="bVisible">Indicates if the highlight should be applied or removed</param> internal void HighlightWindowElement(bool bVisible) { //Check if the HighlightRectArea was provided in the json file and the HostUIElement was found in the DynamoView VisualTree if (HostPopupInfo.HighlightRectArea == null || HostPopupInfo.HostUIElement == null) { return; } //Check if the WindowElementNameString was provided in the json and is not empty if (string.IsNullOrEmpty(HostPopupInfo.HighlightRectArea.WindowElementNameString)) { return; } //If is MenuItem type means that the Popup.TargetPlacement will be calculated dinamically if (HostPopupInfo.HighlightRectArea.UIElementTypeString.Equals(typeof(MenuItem).Name)) { //We try to find the WindowElementNameString (in this case the MenuItem) in the DynamoView VisualTree var foundUIElement = Guide.FindChild(HostPopupInfo.HostUIElement, HostPopupInfo.HighlightRectArea.WindowElementNameString); if (foundUIElement != null) { var subMenuItem = foundUIElement as MenuItem; //If the HighlightRectArea.WindowElementNameString described is a MenuItem (Dynamo menu) then we need to add the Rectangle dynamically to the Template HighlightMenuItem(subMenuItem, bVisible); } } //The HighlightRectArea.UIElementTypeString was provided but the type is DynamoView then we will search the element in the DynamoView VisualTree else if (HostPopupInfo.HighlightRectArea.UIElementTypeString.Equals(typeof(DynamoView).Name)) { string highlightColor = HostPopupInfo.HighlightRectArea.HighlightColor; //Find the in the DynamoView VisualTree the specified Element (WindowElementNameString) var hostUIElement = Guide.FindChild(MainWindow, HostPopupInfo.HighlightRectArea.WindowElementNameString); if (hostUIElement == null) { return; } //If the Element was found we need to calculate the X,Y coordinates based in the UIElement Ancestor Point relativePoint = hostUIElement.TransformToAncestor(MainWindow) .Transform(new Point(0, 0)); var holeWidth = hostUIElement.DesiredSize.Width + HostPopupInfo.HighlightRectArea.WidthBoxDelta; var holeHeight = hostUIElement.DesiredSize.Height + HostPopupInfo.HighlightRectArea.HeightBoxDelta; //Activate the Highlight rectangle from the GuideBackground StepGuideBackground.HighlightBackgroundArea.SetHighlighRectSize(relativePoint.Y, relativePoint.X, holeWidth, holeHeight); if (string.IsNullOrEmpty(highlightColor)) { StepGuideBackground.GuideHighlightRectangle.Stroke = Brushes.Transparent; } else { //This section will put the desired color in the Highlight rectangle (read from the json file) var converter = new BrushConverter(); var brush = (Brush)converter.ConvertFromString(highlightColor); StepGuideBackground.GuideHighlightRectangle.Stroke = brush; } } //This case is for when the item to be highlighted is inside the LibraryView (WebBrowser component) else if (HostPopupInfo.HighlightRectArea.UIElementTypeString.Equals(typeof(WebBrowser).Name)) { //We need to access the WebBrowser instance and call a js function to highlight the html div border of the item HighlightLibraryItem(bVisible); } //If the UIElementTypeString is not a MenuItem and also not a DynamoView we need to find the Window in the OwnedWindows and search the element inside it else { string highlightColor = HostPopupInfo.HighlightRectArea.HighlightColor; Window ownedWindow = Guide.FindWindowOwned(HostPopupInfo.HighlightRectArea.WindowName, MainWindow as Window); if (ownedWindow == null) { return; } UIElement foundElement = Guide.FindChild(ownedWindow, HostPopupInfo.HighlightRectArea.WindowElementNameString); switch (HostPopupInfo.HighlightRectArea.UIElementTypeString.ToUpper()) { //We need to highlight a Button (if the Button template doesn't have a grid then the template needs to be updated) case "BUTTON": var buttonElement = foundElement as Button; if (buttonElement == null) { return; } //We will be searching for the Grid name provided in the json file and then add the Highlight Rectangle var bordersGrid = buttonElement.Template.FindName(HostPopupInfo.HighlightRectArea.UIElementGridContainer, buttonElement) as Grid; if (bordersGrid == null) { return; } if (bVisible) { var buttonRectangle = CreateRectangle(bordersGrid, HostPopupInfo.HighlightRectArea.HighlightColor); //The Rectangle will be added dynamically in a specific step and then when passing to next step we will remove it bordersGrid.Children.Add(buttonRectangle); } else { //When we need to undo the highlight we find the Rectangle and remove it var buttonRectangle = bordersGrid.Children.OfType <Rectangle>().Where(rect => rect.Name.Equals("HighlightRectangle")).FirstOrDefault(); if (buttonRectangle != null) { bordersGrid.Children.Remove(buttonRectangle); } } break; } } }
/// <summary> /// This method will execute an UIAutomation action over a specific UIElement /// </summary> /// <param name="uiAutomationData">UIAutomation info read from a json file</param> /// <param name="enableUIAutomation">Enable/Disable the automation action for a specific UIElement</param> private void ExecuteUIAutomationStep(StepUIAutomation uiAutomationData, bool enableUIAutomation, Guide.GuideFlow currentFlow) { var popupBorderName = "SubmenuBorder"; //This section will search the UIElement dynamically in the Dynamo VisualTree in which an automation action will be executed UIElement automationUIElement = Guide.FindChild(MainWindow, uiAutomationData.Name); if (automationUIElement != null) { uiAutomationData.UIElementAutomation = automationUIElement; } switch (uiAutomationData.ControlType) { case StepUIAutomation.UIControlType.MENUITEM: if (uiAutomationData.UIElementAutomation == null) { return; } MenuItem menuEntry = uiAutomationData.UIElementAutomation as MenuItem; if (menuEntry == null) { return; } switch (uiAutomationData.Action) { case StepUIAutomation.UIAction.OPEN: menuEntry.IsSubmenuOpen = enableUIAutomation; menuEntry.StaysOpenOnClick = enableUIAutomation; break; } //Means that the Popup location needs to be updated after the MenuItem is opened if (uiAutomationData.UpdatePlacementTarget) { //We need to find the border inside the MenuItem.Popup so we can get its Width (this template is defined in MenuStyleDictionary.xaml) var menuPopupBorder = menuEntry.Template.FindName(popupBorderName, menuEntry) as Border; if (menuPopupBorder == null) { return; } //We need to do this substraction so the Step Popup wil be located at the end of the MenuItem Popup. stepUIPopup.HorizontalOffset = (menuPopupBorder.ActualWidth - menuEntry.ActualWidth) + HostPopupInfo.HorizontalPopupOffSet; UpdatePopupLocationInvoke(stepUIPopup); } break; //In this case the UI Automation will be done using a Function located in the static class GuidesValidationMethods case StepUIAutomation.UIControlType.FUNCTION: MethodInfo builderMethod = typeof(GuidesValidationMethods).GetMethod(uiAutomationData.Name, BindingFlags.Static | BindingFlags.NonPublic); object[] parametersArray = new object[] { this, uiAutomationData, enableUIAutomation, currentFlow }; builderMethod.Invoke(null, parametersArray); //If UpdatePlacementTarget = true then means that a new Window was opened after executing the funtion then we need to update the Popup.PlacementTarget if (uiAutomationData.UpdatePlacementTarget) { UpdatePlacementTarget(); } break; //In this case the UI Automation will be done over a WPF Button case StepUIAutomation.UIControlType.BUTTON: if (string.IsNullOrEmpty(uiAutomationData.WindowName)) { return; } //This means that the Button is in a PopupWindow (instead of the DynamoView) so we need to find the button and then apply the automation if (uiAutomationData.WindowName.Equals(WindowNamePopup)) { //Finds the Button inside the PopupWindow var buttonFound = Guide.FindChild((stepUIPopup as PopupWindow).mainPopupGrid, uiAutomationData.Name) as Button; if (buttonFound == null) { return; } switch (uiAutomationData.Action) { case StepUIAutomation.UIAction.DISABLE: if (enableUIAutomation) { buttonFound.IsEnabled = false; } else { buttonFound.IsEnabled = true; } break; } } break; case StepUIAutomation.UIControlType.JSFUNCTION: if (string.IsNullOrEmpty(uiAutomationData.JSFunctionName)) { return; } //We need to create a new list for the parameters due that we will be adding the enableUIAutomation boolean value var parametersJSFunction = new List <object>(uiAutomationData.JSParameters); parametersJSFunction.Add(enableUIAutomation); //Create the array for the parameters that will be sent to the JS Function object[] jsParameters = parametersJSFunction.ToArray(); //Create the array for the paramateres that will be sent to the WebBrowser.InvokeScript Method object[] parametersInvokeScript = new object[] { uiAutomationData.JSFunctionName, jsParameters }; //Execute the JS function with the provided parameters ResourceUtilities.ExecuteJSFunction(MainWindow, HostPopupInfo, parametersInvokeScript); break; } }
/// <summary> /// This method will execute an UIAutomation action over a specific UIElement /// </summary> /// <param name="uiAutomationData">UIAutomation info read from a json file</param> /// <param name="enableUIAutomation">Enable/Disable the automation action for a specific UIElement</param> private void ExecuteUIAutomationStep(StepUIAutomation uiAutomationData, bool enableUIAutomation, Guide.GuideFlow currentFlow) { //This section will search the UIElement dynamically in the Dynamo VisualTree in which an automation action will be executed UIElement automationUIElement = Guide.FindChild(MainWindow, uiAutomationData.Name); if (automationUIElement != null) { uiAutomationData.UIElementAutomation = automationUIElement; } switch (uiAutomationData.ControlType) { case StepUIAutomation.UIControlType.MENUITEM: if (uiAutomationData.UIElementAutomation == null) { return; } MenuItem menuEntry = uiAutomationData.UIElementAutomation as MenuItem; if (menuEntry == null) { return; } switch (uiAutomationData.Action) { case StepUIAutomation.UIAction.OPEN: menuEntry.IsSubmenuOpen = enableUIAutomation; menuEntry.StaysOpenOnClick = enableUIAutomation; break; } break; //In this case the UI Automation will be done using a Function located in the static class GuidesValidationMethods case StepUIAutomation.UIControlType.FUNCTION: MethodInfo builderMethod = typeof(GuidesValidationMethods).GetMethod(uiAutomationData.Name, BindingFlags.Static | BindingFlags.NonPublic); object[] parametersArray = new object[] { this, uiAutomationData, enableUIAutomation, currentFlow }; builderMethod.Invoke(null, parametersArray); //If UpdatePlacementTarget = true then means that a new Window was opened after executing the funtion then we need to update the Popup.PlacementTarget if (uiAutomationData.UpdatePlacementTarget) { UpdatePlacementTarget(); } break; //In this case the UI Automation will be done over a WPF Button case StepUIAutomation.UIControlType.BUTTON: if (string.IsNullOrEmpty(uiAutomationData.WindowName)) { return; } //This means that the Button is in a PopupWindow (instead of the DynamoView) so we need to find the button and then apply the automation if (uiAutomationData.WindowName.Equals(WindowNamePopup)) { //Finds the Button inside the PopupWindow var buttonFound = Guide.FindChild((stepUIPopup as PopupWindow).mainPopupGrid, uiAutomationData.Name) as Button; if (buttonFound == null) { return; } switch (uiAutomationData.Action) { case StepUIAutomation.UIAction.DISABLE: if (enableUIAutomation) { buttonFound.IsEnabled = false; } else { buttonFound.IsEnabled = true; } break; } } break; } }
/// <summary> /// GuidesManager Constructor /// </summary> /// <param name="root">root item of the main Dynamo Window </param> /// <param name="dynViewModel"></param> public GuidesManager(UIElement root, DynamoViewModel dynViewModel) { mainRootElement = root; dynamoViewModel = dynViewModel; guideBackgroundElement = Guide.FindChild(root, guideBackgroundName) as GuideBackground; }
/// <summary> /// This method will be opening the SideBar Package Details (or closing it when enableFunction = false) /// </summary> /// <param name="stepInfo">Information about the Step</param> /// <param name="uiAutomationData">Information about UI Automation that is being executed</param> /// <param name="enableFunction">Variable used to know if we are executing the automation or undoing changes</param> /// <param name="currentFlow">Current Guide Flow</param> internal static void ExecuteViewDetailsSideBar(Step stepInfo, StepUIAutomation uiAutomationData, bool enableFunction, GuideFlow currentFlow) { const string packageDetailsName = "Package Details"; const string closeButtonName = "CloseButton"; const string packageSearchWindowName = "PackageSearch"; CurrentExecutingStep = stepInfo; var stepMainWindow = stepInfo.MainWindow as Window; var packageDetailsWindow = Guide.FindChild(stepMainWindow, stepInfo.HostPopupInfo.HostUIElementString) as UserControl; if (enableFunction) { //This section will open the Package Details Sidebar PackageManagerSearchView packageManager = Guide.FindWindowOwned(packageSearchWindowName, stepMainWindow) as PackageManagerSearchView; if (packageManager == null) { return; } PackageManagerSearchViewModel packageManagerViewModel = packageManager.DataContext as PackageManagerSearchViewModel; //If the results in the PackageManagerSearch are null then we cannot open the Package Detail tab if (packageManagerViewModel == null || packageManagerViewModel.SearchResults.Count == 0) { return; } //We take the first result from the PackageManagerSearch PackageManagerSearchElementViewModel packageManagerSearchElementViewModel = packageManagerViewModel.SearchResults[0]; if (packageManagerSearchElementViewModel == null) { return; } if (packageDetailsWindow == null) { packageManagerViewModel.ViewPackageDetailsCommand.Execute(packageManagerSearchElementViewModel.Model); } //The PackageDetails sidebar is using events when is being shown then we need to execute those events before setting the Popup.PlacementTarget. //otherwise the sidebar will not be present (and we don't have host for the Popup) and the Popup will be located out of the Dynamo window CurrentExecutingStep.MainWindow.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { })); } else { //This section will close the Package Details Sidebar (just in case is still opened), //due that the sidebar (UserControl) is inserted inside a TabItem the only way to close is by using the method dynamoView.CloseExtensionTab var dynamoView = (stepMainWindow as DynamoView); if (packageDetailsWindow == null) { return; } //In order to close the Package Details tab we need first to get the Tab, then get the Close button and finally call the event to close it TabItem tabitem = dynamoView.ExtensionTabItems.OfType <TabItem>().SingleOrDefault(n => n.Header.ToString() == packageDetailsName); if (tabitem == null) { return; } //Get the Close button from the PackageDetailsView Button closeButton = Guide.FindChild(tabitem, closeButtonName) as Button; if (closeButton == null) { return; } dynamoView.CloseExtensionTab(closeButton, null); } }