In my project I have a list view, now listening to SelectedItem change is easy, every tutorial has that, but I can't find anything on using ItemTapped event.
What do I bind the event to in the modelPage?
Thanks,
Mike
Since ItemTapped is an event and not a Command (or BindableProperty at all) you cannot use it directly from you PageModel.
They have invented something like Behaviors for this. With Behaviors you can turn an Event to a Command.
While there are third party plugins which do this like Corcav's one, it is also built into Xamarin.Forms now.
Let me explain it by the Corcav one, other implementations should be similar. Also I'm assuming you're using XAML.
First of all, install the NuGet and don't forget to include the right namespace into your page, which means adding something like: xmlns:behaviors="clr-namespace:Corcav.Behaviors;assembly=Corcav.Behaviors"
Now under your ListView declare your Behaviors like so:
<!-- ... more XAML here ... -->
<ListView IsPullToRefreshEnabled="true" RefreshCommand="{Binding RefreshDataCommand}" IsRefreshing="{Binding IsBusy}" IsVisible="{Binding HasItems}" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" CachingStrategy="RecycleElement">
<behaviors:Interaction.Behaviors>
<behaviors:BehaviorCollection>
<behaviors:EventToCommand EventName="ItemSelected" Command="{Binding ItemSelectedCommand}" />
</behaviors:BehaviorCollection>
</behaviors:Interaction.Behaviors>
<!-- ... more XAML here ... -->
Note that is is a collection, so you could add more if you want (also in other scenarios).
Also note that I did in fact user the SelectedItem as well. This is probably what you want because else the item you tapped will stay selected. So the SelectedItem property doesn't do much more than set it back to null (hence the TwoWay). But you can also take the actual selected item from there.
So now in your PageModel declare a Command and assign it with something like this:
private void ItemSelected()
{
// Open the article page.
if (_selectedItem != null)
{
CoreMethods.PushPageModel<GroupArticlePageModel>(_selectedItem, false, true);
}
}
_selectedItem being the property to which the tapped item is assigned.
Of course you could do it even better and supply the behavior with a CommandParameter in which you put the tapped item reference.
Related
I have a React application where a react-leaflet#2.7.0 + react-leaflet-draw#0.19.0 map is displaying multiple FeatureGroup components containing polygons.
On the right end side of the app is a clickable list of the feature groups names.
The groups can be selectively "activated" by clicking the names, so if a feature group is active, the EditControl therein is rendered. Only one group can be active at a time.
My problem
when I switch from one group to another and then click the edit button in EditControl, the onEditStart/Stop events of the previously active group still fire, along with the new ones. The more I switch between groups, the more events are fired.
Update:
The issue seems to be that EditControl is never unmounted, even if upon the parent state change it doesn't get rendered. If I add a random key prop to it, the issue is resolved, but then other issues occur.
I don't yet have a minimal code example to share, but to give you an idea of the implementation here is a schema:
<App>
<Map>
<MapContent id="foo">
<FeatureGroup>
<EditControl />
</FeatureGroup>
</MapContent>
<MapContent id="bar">
<FeatureGroup>
<EditControl />
</FeatureGroup>
</MapContent>
</Map>
<GroupSelector />
</App>
Except App (root component), MapContent (very basic wrapper: checks if the group is active) and GroupSelector (clickable list), all other components are from react-leaflet and react-leaflet-draw.
Flow
On click, GroupSelector updates the state of App to set an activeGroupID (i.e. "foo") and MapContent will render its EditControl only if its id matches the activeGroupID
I hope this description makes sense. Any help would be greatly appreciated!
situation:
2 diferent tables, 1 view;
want to achieve:
I've got to change the table when I change the selectedKey in a select.
I already tried to create fragments with each table and call the one I want when I change the select.
fragment1 - table 1
fragment2 - table 2
select - oEvent (onChangeSelect) - getSelectedKey - if(key === 1) - call fragment1, if(key === 2) - call fragment2, but it gives me an error.
I don't know if this is correct and I'm not finding any infos related online.
If someone can show some links or give me just an idea on how can I do this, it would be perfect.
You could use the concept of binding to achieve this without coding. assume you have something like that in your view:
<Select><!-- ... --></Select>
<core:Fragment ... /><!-- table 1 -->
<core:Fragment ... /><!-- table 2 -->
First, add a model to your app, which will contain the information about the status of the app (which of the fragments is visible currently). Easiest way is to start with an empty JSON model. If you are familiar with manifest.json config, add it there. if not, you could add this in your Components init method:
this.setModel(new JSONModel(), "config");
This way, the new empty model is available under the name "config" in all the views of the app.
Now, we will add a visibility flag, which will control the visibility of your fragments (tables), and can be changed by the select:
<Select selectedKey="{config>/selectedFragmentKey}">
<core:Item key="" text="Please choose one" />
<core:Item key="showFragment1" text="Show Fragment 1" />
<core:Item key="showFragment2" text="Show Fragment 2" />
</Select>
This new version will store the key of the selected item in the config model in the path /selectedFragment. Add visibility flag to fragments (tables):
<!-- table 1 is only visible if "Show Fragment 1" was selected -->
<Table visible="{= ${config>/selectedFragmentKey} === 'showFragment1'}" .../>
<!-- table 2 is only visible if "Show Fragment 2" was selected -->
<Table visible="{= ${config>/selectedFragmentKey} === 'showFragment2'}" .../>
and you're done.
Main lesson here: learn to use bindings.
If I understood the question correctly, you have a selection control (which has 2 entries), and a table in the view. Based on the selection item, you want to display ONE of the tables at any given point of time. I am also assuming here, that the tables are purely used in the current view only & not really a "re-useable" thing - which gets used in different places of the app (which then renders the use of fragments an overkill).
I am assuming, both tables are bound (although to different data sets).
In that case, consider using the "visible" property of the appropriate table to "false".
Remember - all UI components, including tables inherit from sap.ui.core.Control - which has the "visible" property.
In API reference for table : https://sapui5.hana.ondemand.com/#/api/sap.ui.table.Table%23methods/Summary
If you scroll down to "Borrowed from:" section, you will see the "setVisible" and "getVisibe" methods.
So, in the event fired during selection change, you can grab the table control & set the visibility using the aforementioned methods.
Alternatively, you can also set an expression binding directly on the table control in XML, which evaluates to "true" or "false" (perhaps using a formatter).
Let me know if you need further info on this.
Best Regards,
Gopal Nair.
I'm trying to bind a DataTriggerBehavior to a Property on my ViewModel, but it doesn't ever fire.
I've used DataTriggerBehaviors bound to various Properties of Controls with no trouble but can't get the VM binding to work.
DataContext is set to the VM.
I can see the binding value in debug but nothing triggers.
I've tested the InvokeCommandAction by changing the DataTriggerBehavior to an EventTriggerBehavior so that works fine.
<AppBarButton Icon="Library">
<i:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding HelpPhase}" ComparisonCondition="Equal" Value="Add" >
<core:InvokeCommandAction Command="{Binding DataContext.StoreRateCommand, ElementName=LayoutRoot}"/>
</core:DataTriggerBehavior>
</i:Interaction.Behaviors>
</AppBarButton>
In VM (inherits VMBase that implements IPCN)
Private mHelpPhase As String
Public Property HelpPhase() As String
Get
Return Settings.HelpPhase
End Get
Set(value As String)
SetProperty(Settings.HelpPhase, value)
End Set
End Property
The EventTriggerBehavior listens for a specific event on its source and executes an action when the event is fired. It is different from the DataTriggerBehavior.
The DataTriggerBehavior performs an action when the data the behaviors is bound to meets a specified condition. In your question, when the bound data of the HelpPhase's value change to "Add", the behavior triggers an action to fire the command.
You should be able check if you have bind the HelpPhase to the DataTriggerBehavior and set the "Add" to the HelpPhase. You can bind the HelpPhase to Text property of TextBlock if the TextBlock show "Add".
There is an official DataTriggerBehavior sample, please refer the XamlBehaviors sample.
I'm automating the windows application. I tried to access the pane element(which has text box, combo box controls) under tab control, but it's not accessible. White returns null.
I tried other techniques like UI automation TreeWalker (Rawview, Control view, content view), but nothing helps.
refer images in below links:
https://dl.dropboxusercontent.com/u/68446125/Tab.png
https://dl.dropboxusercontent.com/u/68446125/General%20Pane.png
As in picture 1, tab control is retrieved properly by White/UI Automation, but the child element General* Pane is not returned and it's controls are not accessible (Refer pic 2 highlighted), the first accessible child element is "General* tab Item".
Strange thing is, these controls are accessible in Inspect.exe (in windows SDK). I tried following methods to retrieve controls, but General* Pane is never accessible through White/UI Automation.
var tab = Window.Get<Tab>(SearchCriteria.ByControlType(ControlType.Tab).AndByClassName("TwoPageControl")); // Tab control is retrieved properly
var pane = tab.GetElement(SearchCriteria.ByControlType(ControlType.Pane).AndByText("General*")); // this line returns NULL
var pane1 = revWindow.GetElement(SearchCriteria.ByControlType(ControlType.Pane).AndByText("General*")); // this line returns NULL
var pane2 = revWindow.Get<Panel>(SearchCriteria.ByControlType(ControlType.Pane).AndByText("General*"));// throws exception "Failed to get ControlType=pane,Name=General*,ControlType=pane"
Tried windows UI automation code as well, but no luck.
System.Windows.Automation.Condition cd1 = new AndCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Tab),
new PropertyCondition(AutomationElement.ClassNameProperty, "TwoPageControl"));
AutomationElement a = window.FindFirst(TreeScope.Descendants, cd1); // Tab control is returned properly here
TreeWalker rawViewWalker = TreeWalker.RawViewWalker;
AutomationElement cc = rawViewWalker.GetFirstChild(a); // General * Pane is not returned, instead General* Tab item is returned, though it's not the first child.
var cd = rawViewWalker.GetNextSibling(cc); // this returns next pane element available, not General * Pane.
Please help me how to access General * Pane and it's children under tab control. Any help is much appreciated.
I had exactly the same problem with my application. I use Inspect and open source UIAVerify where Pane element was visible as children of tab element. But when i compile Verify as a .Net 4.5 project, Pane element was not seen as part of tab. It only appears when I pointed it directly. I also search for my Pane element in Descendants of main window but there was nothing. I think it has something to do with dynamically creation of that pane content (i mean there is different content when you choose different tabItem).
I think you can't get access to that element from tree point of view.
My solution was to use AutomationElement.FromPoint Method.
http://msdn.microsoft.com/en-us/library/system.windows.automation.automationelement.frompoint(v=vs.110).aspx
I also think this shoud help if you have conntact with people developing program.
Some controls on a page are not visible for MS UI Automation
You can break up the TabControl to just the TabPanel, but without its ContentPresenter. You create your own ContentPresenter which used the content of the selected TabItem.
This way, White will be able to discover the controls in the ContentPresenter.
This is a workaround and it is a shame you have to change your WPF code just because "something goes wrong with UIA". But this is the best I could do.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TabControl x:Name="uiTab" Grid.Row="1">
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<TabPanel IsItemsHost="True" VerticalAlignment="Stretch"/>
</ControlTemplate>
</TabControl.Template>
<TabItem Header="first">
<Button>FIRST</Button>
</TabItem>
<TabItem Header="second">
<Button>SECOND</Button>
</TabItem>
</TabControl>
<ContentPresenter Content="{Binding SelectedItem.Content, ElementName=uiTab}"/>
</Grid>
I have a custom activity, with a single in argument which is a string. However, rather than allow the designer to enter an arbitrary string, I want the designer to be presented with a Combobox with the list of options, the options are dynamic and are loaded from a database into a List<> collection.
My problem is I have no clue how to bind the Combobox in the designer to this list and have the selection set to the in argument of the activity. Visually I have the activity designer working, it is just this one step.
Normally, I would write the activity with a property rather than an InArgument. This simplifies the scenario:
<ComboBox ItemsSource="{Binding Path=ValidOptions}"
SelectedValue="{Binding Path=ModelItem.MyStringProperty, Mode=TwoWay}"/>
(here ValidOptions is some Collection property on your ActivityDesigner class. MyStringProperty is some public get/set/ property on the underlying activity such as:
public string MyStringProperty { get; set; }
)
The problem you will have if you add InArgument to the mix is that the string values from the combo box cannot be directly assigned to a ModelItem expecting InArgument<string>. This is fixable using a custom IValueConverter in your binding.
The previous answers are helpful but were not enough for me. Eventually I found a terrific article from 2012, in Microsoft's .Net 4.5 Developer Guide: Binding a custom activity property to a designer control. That article was almost the full answer - except for a minor bug in the custom converter class, and a major flaw: that technique will save a value from the ComboBox, but it will not restore it when you re-open your workflow.
Microsoft's Ron Jacobs has another answer for custom activity designers. I ended up combining the two to get a working solution.
Custom Designer
The ModelToObjectValueConverter was an incredibly helpful resource, allowing me to skip creating my own IValueConverter. In the ObjectDataProvider you see me loading a list of strings by calling a static method, People.GetPeople(). The ComboBox binds to that provider as the item source, but binds the selected value to the Person property on the custom Activity (below)
<sap:ActivityDesigner x:Class="ActivityLibrary1.ComboBoxActivityDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
xmlns:sapc="clr-namespace:System.Activities.Presentation.Converters;assembly=System.Activities.Presentation"
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
xmlns:c="clr-namespace:ActivityLibrary1">
<sap:ActivityDesigner.Resources>
<ResourceDictionary>
<sapc:ModelToObjectValueConverter x:Key="ModelToObjectValueConverter" />
<ObjectDataProvider x:Key="people" ObjectType="{x:Type c:People}" MethodName="GetPeople"/>
</ResourceDictionary>
</sap:ActivityDesigner.Resources>
<Grid>
<Label Content="Person" HorizontalAlignment="Left" VerticalAlignment="Top" />
<ComboBox HorizontalAlignment="Left"
Margin="66,0,0,0"
VerticalAlignment="Top"
Width="120"
SelectedValue="{Binding Path=ModelItem.Person, Mode=TwoWay, Converter={StaticResource ModelToObjectValueConverter} }"
ItemsSource="{Binding Source={StaticResource people}}">
</ComboBox>
</Grid>
</sap:ActivityDesigner>
Custom Code Activity
Note that this uses a property rather than an InArgument, which makes binding the ComboBox easier.
[Designer(typeof(ComboBoxActivityDesigner))]
public class CodeActivity1 : CodeActivity
{
public string Person { get; set; }
protected override void Execute(CodeActivityContext context)
{
// Just to demonstrate that it worked
MessageBox.Show(Person);
}
}
Workflow
Now the custom activity, CodeActivity1, can be dragged onto a workflow. When you make a selection, the selected value will appear in the properties pane. Save the workflow. Close and re-open. The previously-selected value will persist as desired.
One way to solve it is to define your own ComboBoxEditor which derives from UITypeEditor.
Expose the collection you want to bind this combobox in the activity class and decorate your bindable property in Activity class with following attribute :
[EditorAttribute(typeof(CustomListBoxEditor), typeof(System.Drawing.Design.UITypeEditor))]
Also in the custom comboboxEditor you will have to modify your
EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) method to get the collection and bind it to the combobox.