I'm using Xamarin.UITest to write some automation.
The target app has this in its markup:
<ContentPage.ToolbarItems>
<ToolbarItem Icon="Settings" AutomationId="SettingsToolbarItem" Order="Primary" Priority="1" Command="{Binding ShowSettingsCommand}" />
</ContentPage.ToolbarItems>
So far I've had 3 approaches:
Using .Class and Indexing successfully finds the element
systemMenuButton = x => x.Class("android.support.v7.view.menu.ActionMenuItemView").Index(1);
Using .Property fails to find the element
systemMenuButton = e => e.Property("Command", "ShowSettingsCommand");
Similarly, using .Marked also fails to find the element
systemMenuButton = x => x.Marked("SettingsToolbarItem");
Relevant automation code is as follows:
using Query = System.Func<Xamarin.UITest.Queries.AppQuery, Xamarin.UITest.Queries.AppQuery>;
....
protected readonly Query systemMenuButton = x => x.Marked("SettingsToolbarItem");
....
app.Tap(systemMenuButton);
I get a generic "unable to find element" exception:
Unable to find element. Query for Marked("SettingsToolbarItem") gave no results.
I don't get this exception when clicking on other elements outside the ContentPage.ToolbarItems block on the same View/Page
I've hit the same problem - for some reason Android can't detect ToolbarItem.AutomationId.
A workaround is to assign ToolbarItem.Text the same value as ToolbarItem.AutomationId.
Xamarin.Forms.ContentPage
<ContentPage.ToolbarItems>
<ToolbarItem Icon="Settings" Text="SettingsToolbarItem" AutomationId="SettingsToolbarItem" Order="Primary" Priority="1" Command="{Binding ShowSettingsCommand}" />
</ContentPage.ToolbarItems>
Xamarin.UITest
using Query = System.Func<Xamarin.UITest.Queries.AppQuery, Xamarin.UITest.Queries.AppQuery>;
// ....
protected readonly Query systemMenuButton = x => x.Marked("SettingsToolbarItem");
//....
public void TapSystemMenuButton()
{
app.Tap(systemMenuButton);
app.Screenshot("Tapped System Menu Button");
}
Here's is a sample app where I use similar logic to tap a ToolbarItem in a UITest: https://github.com/brminnick/InvestmentDataSampleApp/
Edit
In the comments, you mentioned that you do not have access to the source code of the Xamarin.Forms app.
If you are unable to change the Xamarin.Forms source code, you will have to use x => x.Class("ActionMenuItemView").Index(1).
I don't recommend going this route because the int parameter of Index can vary depending on the device; it is not guaranteed to always be 1.
public void TapSystemMenuButton()
{
if (app is iOSApp)
app.Tap(systemMenuButton);
else
app.Tap(x => x.Class("ActionMenuItemView").Index(1));
app.Screenshot("Tapped System Menu Button");
}
Related
I'm creating a VSIX that contains a Tool Window. I would like to move the menu item that opens it into Debug > Windows, instead of View > Other Windows. I would also like to show it only when the target project is in debug mode (for example like the Call Stack menu item).
I managed to show the menu item in the Debug menu, but not in Debug > Windows. In my .vsct I've used:
<!-- ... -->
<Extern href="VSDbgCmd.h"/>
<Extern href="VsDebugGuids.h"/>
<!-- ... -->
<Parent guid="guidVSDebugGroup" id="IDM_DEBUG_WINDOWS"/>
<!-- ... -->
Regarding showing it only in debug mode, I don't have any idea and I couldn't find anything useful.
So my question is: how do I make my menu item behave like Call Stack menu item (in Debug > Window and shown only in debug mode)?
While there is a UIContext for "debugging", there doesn't appear to be one for when the debugger actually enteres break mode. (Hint, I used https://marketplace.visualstudio.com/items?itemName=PaulHarrington.ComponentDiagnostics extension to monitor the active UI Contexts)
So you'll need to add DynamicVisibility and DefaultInvisible command flags to your button:
<Extern href="stdidcmd.h"/>
<Extern href="vsshlids.h"/>
<Extern href="VSDbgCmd.h"/>
<Extern href="VsDebugGuids.h"/>
<Commands package="guidIaxToolWindowPackage">
<Buttons>
<Button guid="guidIaxToolWindowPackageCmdSet" id="IaxToolWindowCommandId" priority="0x0002" type="Button">
<Parent guid="guidVSDebugGroup" id="IDG_DEBUG_WINDOWS_GENERAL"/>
<Icon guid="guidImages" id="bmpPic1" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>Show IaxToolWindow</ButtonText>
</Strings>
</Button>
</Buttons>
Then ensure your package is loaded whenever the debugger attaches to or launches a process, by decorating your AsyncPackage class with the ProvideAutoLoad attribute below:
namespace IaxToolwindow
{
// use Debugging UI Context to ensure package get loaded when debugging starts
[ProvideAutoLoad(VSConstants.UICONTEXT.Debugging_string, PackageAutoLoadFlags.BackgroundLoad)]
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About
[ProvideMenuResource("Menus.ctmenu", 1)]
[ProvideToolWindow(typeof(IaxToolWindow))]
[Guid(IaxToolWindowPackage.PackageGuidString)]
public sealed class IaxToolWindowPackage : AsyncPackage
{
public const string PackageGuidString = "256ca1a6-6b6d-4329-a7f9-15ee5bbf8114";
Then add a BeforeQueryStatus command handler for your menu command, to make the menu item visible when IVSDebugger.GetMode returns DBGMODE_BREAK. For example:
private IaxToolWindowCommand(AsyncPackage package, OleMenuCommandService commandService)
{
this.package = package ?? throw new ArgumentNullException(nameof(package));
commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
var menuCommandID = new CommandID(CommandSet, CommandId);
var menuItem = new OleMenuCommand(this.Execute, menuCommandID);
menuItem.BeforeQueryStatus += IAxToolWindowCommand_BeforeQueryStatus;
commandService.AddCommand(menuItem);
}
private void IAxToolWindowCommand_BeforeQueryStatus(object sender, EventArgs e)
{
ThreadHelper.JoinableTaskFactory.Run(async delegate
{
await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
OleMenuCommand menuItem = (OleMenuCommand)sender;
IVsDebugger vsDebugger = await ServiceProvider.GetServiceAsync(typeof(IVsDebugger)) as IVsDebugger;
Assumes.Present(vsDebugger);
DBGMODE[] dbgmode = new DBGMODE[1];
int hr = vsDebugger.GetMode(dbgmode);
// show menu item when debugger is in break mode
menuItem.Visible = (dbgmode[0] == DBGMODE.DBGMODE_Break);
});
}
This was built off a wizard generated AsyncToolwindow project. But note I changed up the MenuCommand to OleMenuCommand, so we can add and use a BeforeQueryStatus handler to control the visibility of the menu item.
The ProvideAutoLoad is used to ensure the package is loaded whenever a debug session is started, so it's started in time to make the menu item visible (when the debugger is in break mode).
If there had been a UIContext guid activated when the debugger entered break mode, we could have just added a VisbilityConstraint to the .vsct. But given the absence of a UIContext specific to break mode, you'll need to do something similar to the above, to make the menu item visible only when in break mode.
Sincerely,
For reproducing the problem, I have added 2 controls to a content page: Picker and Entry. The container is a TableView section.
If I click on the Entry control, causing the keyboard to open, or enter characters in it, the Picker selection is opening. It's opening every time I enter a character or removing one (so when the value is changing).
I don't have this problem if the main container isn't a TableView, but for example a StackLayout.
I have this problem since Xamarin Forms v3.3. In previous versions, like 3.2, this problem didn't exist. The problem also exists in the 3.4 beta.
Also: the problem only occurs in Android (version 4 - 9). Not in iOS.
Below you'll find my code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:PickerBugXF33"
x:Class="PickerBugXF33.MainPage">
<StackLayout>
<TableView x:Name="testTableView" Intent="Form" HasUnevenRows="true" />
</StackLayout>
</ContentPage>
Code behind:
using Xamarin.Forms;
namespace PickerBugXF33
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
testTableView.Root = new TableRoot();
var section = new TableSection("Test section");
testTableView.Root.Add(section);
var viewCell = new ViewCell();
section.Add(viewCell);
var stack = new StackLayout() { Orientation = StackOrientation.Vertical };
viewCell.View = stack;
var picker = new Picker();
picker.Items.Add("Test item in picker");
stack.Children.Add(picker);
var entry = new Entry();
stack.Children.Add(entry);
}
}
}
NB: This is a test project. The real live project is much larger in which the page has to be created via code.
Update 1, 11/9/2018: Might be solved by commit: https://github.com/xamarin/Xamarin.Forms/pull/4344/commits/8401d81745ad248b5b70aa669863c8b8c8753e66
I have a Xamarin.Forms project, and I have a custom control that should open a new page when tapped. Unfortunately, nothing happens when I call Navigation.PushAsync(...);. I've read countless StackOverflow questions (such as this one) and Xamarin Forums threads and done several Google searches, but none of the solutions seem to solve my issue.
My control inherits from ContentView, as all Xamarin.Forms views
do.
src is a custom class that contains some data points that
are used by this control and EventDetailsPage.
I can confirm that the gesture does work itself, but the call to PushAsync() does nothing.
I have tried manipulating the statement in ways so that a NavigationPage is used (such that it becomes myNavigationPage.Navigation.PushAsync(new EventDetailsPage(src));).
I have also tried creating a constructor that takes a Page and uses it in away similar to the above point.
My control's constructor:
public EventControl() {
InitializeComponent();
GestureRecognizers.Add(new TapGestureRecognizer() {
Command = new Command(() => Navigation.PushAsync(new EventDetailsPage(src)))
});
}
Typically, asking a new question on StackOverflow is my last resort when nothing else that I've tried solved my problem. Any help is appreciated.
Change your App.cs like this
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());//Very important add this..
//MainPage = new MainPage(); //not like this
}
I found the problem. Keith Rome lead me to look through EventDetailsPage and I commented out a line of XAML, and that solved the problem. That line was adding a custom control to the page (not related to EventControl). I don't know if the control's definition or its custom render's definition was causing the strange behavior, but removing that line solved the issue.
Same problem for different reasons...
I got this problem, after installing updates in Visual Studio, including Xamarin.
After updating all my nuget packages, by the following (the first command to show which ones have updates), I was able to resolve the problem:
Get-Package -updates
Update-Package <package>
I suspect, removing the AppData\local\Xamarin folder (which seems to act as a cache) and letting it repopulate could also solve the problem.
I implemented AppShell class which has one input string, in order to set correct page:
public partial class AppShell : Shell
{
public AppShell(string startUpPageString = null)
{
InitializeComponent();
Page startUpPage;
if (startUpPageString == nameof(SignUpPage))
{
startUpPage = new SignUpPage();
}
else if (startUpPageString == nameof(LoginPinPage))
{
startUpPage = new LoginPinPage();
}
else
{
startUpPage = new SignUpPage();
}
ShellSection shellSection = new ShellSection();
shellSection.Items.Add(new ShellContent() { Content = startUpPage });
CurrentItem = shellSection;
RegisterRoutes();
}
private void RegisterRoutes()
{
...removed for clarity
}
}
among that, in my XAML I added some tabs (postlogin section) which are called though GoToAsync(postlogin) method:
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:login="clr-namespace:XXX.Views.Login"
xmlns:postlogin ="clr-namespace:XXX.XXX.Views.PostLogin"
x:Class="XXX.AppShell"
FlyoutBehavior="Disabled">
<ShellItem Route="postlogin">
<ShellSection Route="activity_section" Title="Activity" Icon="Activity_Menu_Icon.png">
<ShellContent Route="page1"
Title="Page1"
Icon="Pag1.png"
ContentTemplate="{DataTemplate postlogin:Page1}" />
</ShellSection>
<ShellContent Route="page2"
Title="Page2"
Icon="Pag2.png"
ContentTemplate="{DataTemplate postlogin:Page2}" />
</ShellSection>
<ShellContent Route="page3"
Title="Page3"
Icon="Page3.png"
ContentTemplate="{DataTemplate postlogin:Page3}" />
</ShellSection>
<ShellContent Route="page4"
Title="Page4"
Icon="Pag4.png"
ContentTemplate="{DataTemplate postlogin:Page4}" />
</ShellSection>
</ShellItem>
<FlyoutItem Route="others"
Title="OTHERS"
FlyoutDisplayOptions="AsMultipleItems">
<Tab>
<ShellContent Route="cats"
Title="Cats"
Icon="next_7.png"
/>
<ShellContent Route="dogs"
Title="Dogs"
Icon="next_7.png"
/>
</Tab>
</FlyoutItem>
</Shell>
So, my problem is as follows: Once I got to for example, SignUpPage nad I click a button, and that button executes:
await Shell.Current.Navigation.PushAsync(new SentEmailPage()); it goews to catch part with an message: Object reference not set to an instance of an object.at Xamarin.Forms.ShellSection.OnPushAsync
this solved my problem
Device.InvokeOnMainThreadAsync(() =>
{
Navigation.PushAsync(new CameraPage());
});
I am using Entity Framework to retrieve records from the database. My user control page crashes every time I run it, but after I comment out the lines in BindLstBox method; my user control page runs well. Is there anything wrong with this code? (DAOActivity is a class file which have CRUD codes in it. I suppose there is nothing wrong there.) It shows this error when i try to run :
'The invocation of the constructor on type 'iStellar.home' that
matches the specified binding constraints threw an exception.' Line
number '5' and line position '14'.
Heres the screenshot of the error :
DAO.DAOActivity daoActivity = new DAO.DAOActivity();
public home()
{
InitializeComponent();
BindListBox();
}
public void BindListBox()
{
listBox1.ItemsSource = daoActivity.GetAll();
listBox1.DisplayMemberPath = "ActivityName";
listBox1.SelectedValuePath = "ActivityID";
}
My XAML :
<ListBox Height="534" HorizontalAlignment="Left" Margin="218,415,0,0"
Name="listBox1" VerticalAlignment="Top" Width="512" />
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() =>
{
listBox1.ItemsSource = daoActivity.GetAll();
listBox1.DisplayMemberPath = "ActivityName";
listBox1.SelectedValuePath = "ActivityID";
}));
I hope this will help.
I am trying to migrate a small prototype application I made in WinForms to WPF. I'm having some issues with a combobox in WPF not changing values when I select a different value from the drop-down. Initially, I tried just copying the code that I used in my WinForms app to populate the combobox and determine if a new index had been selected. This is how my WinForms code looked like:
private void cmbDeviceList_SelectedIndexChanged(object sender, EventArgs e)
{
var cmb = (Combobox) sender;
var selectedDevice = cmb.SelectedItem;
var count = cmbDeviceList.Items.Count;
// find all available capture devices and add to drop down
for(var i =0; i<count; i++)
{
if(_deviceList[i].FriendlyName == selectedDevice.ToString())
{
_captureCtrl.VideoDevices[i].Selected = true;
break;
}
}
}
Earlier in the code, I am populating the _deviceList List and the combo box (in Form1_Load to be specific) by looping over the available devices and adding them. I tried the same approach in WPF and could only populate the combo box. When I selected a new value, for some reason the same exact value (the initial device) was being sent into the event code (cmbCaptureDevices_SelectionChanged in my WPF app). I looked around for some tutorials in WPF and found that maybe data binding was my issue, and I tried that out instead. This is my combobox in my XAML file:
<ComboBox ItemsSource="{Binding Devices}" Name="cmbCaptureDevices"
IsSynchronizedWithCurrentItem="True" SelectedItem="{Binding CurrentDevice,
Mode=TwoWay}" Se;ectionChanged="cmbCapturedDevices_SelectionChanged" />
There's more to that XAML definition, but it's all arbitrary stuff like HorizontalAlignment and whatnot. My VideoDevicesViewModel inherits from INotifyPropertyChanged, has a private List<Device> _devices and a private Device _currentDevice. The constructor looks like:
public VideoDevicesViewModel()
{
_devices = GetCaptureDevices();
DevicesCollection = new CollectionView(_devices);
}
GetCaptureDevices simply is the loop that I had in my WinForms app which populates the list with all avaialble capture devices on the current machine. I have a public CollectionView DevicesCollection { get; private set; } for getting/setting the devices at the start of the application. The property for my current device looks like:
public Device CurrentDevice
{
get { return _currentDevice; }
set
{
if (_currentDevice = value)
{
return;
}
_currentDevice = value;
OnPropertyChanged("CurrentDevice");
}
}
OnPropertyChanged just raises the event PropertyChanged if the event isn't null. I'm new to WPF (and pretty new to C# in general, honestly) so I'm not sure if I'm missing something elementary or not. Any idea as to why this combobox won't change values for me?
Discovered the answer on my own here. The unexpected behavior was a result of using the Leadtools Device class. It's a COM component and apparently was not playing nicely with my application. I honestly don't understand why exactly it worked, but I wrapped the Device class in another class and used that instead. As soon as I was using the wrapper class, the combo box functioned as it should.
You are using the assignment operator '=' instead of the equality operator '=='
Change
if (_currentDevice = value)
to
if (_currentDevice == value)
Try the following
if _currentDevice == value ...