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.
Related
I try to use EventTriggerBehavior with ScrollViewer's ViewChanged event:
<ScrollViewer x:Name="scrollViewer">
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ViewChanged">
<core:InvokeCommandAction Command="{Binding AddNextCommand}"
CommandParameter="{Binding ElementName=scrollViewer}"/>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</ScrollViewer>
But it get an exception:
Windows.UI.Xaml.Markup.XamlParseException: 'The text associated with this error code could not be found.
Cannot add instance of type 'Microsoft.Xaml.Interactions.Core.EventTriggerBehavior' to a collection of type 'Microsoft.Xaml.Interactivity.BehaviorCollection'.
How to fix it?
I want to automate add new elements to view when ScrollViewer scrolls to bottom, are there other ways to do it? Thanks!
Cannot add instance of type 'Microsoft.Xaml.Interactions.Core.EventTriggerBehavior' to a collection of type 'Microsoft.Xaml.Interactivity.BehaviorCollection'.
Firstly, this error indicates that the element does not have an event called ViewChanged. This is caused by you didn't allocate SourceObject for EventTriggerBehavior that the behavior is attached to the wrong element which is not scrollViewer. You should set SourceObject like follows:
<core:EventTriggerBehavior EventName="PointerPressed" SourceObject="{Binding ElementName=scrollViewer}">
But even with this, you may get another error since ViewChanged may not be supported with WindowsRuntimeMarshal.AddEventHandler. Please try to invoke the ScrollViewer.ViewChanged event directly.
I want to automate add new elements to view when ScrollViewer scrolls to bottom, are there other ways to do it?
Looks like ISupportIncrementalLoading could help. Please try it.
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.
When I bind I normally just enter the name of the property which exists in the DataContext
EG
Text = {Binding MyProp}"
When I use relative source, I have to use DataContext
Text = "{Binding RelativeSource={RelativeSource AncestorLevel=1, AncestorType={x:Type UserControl}, Mode=FindAncestor}, Path=DataContext.MyProp }" />
I must be missing some basic understanding as I don't see why I need to include the word DataContext in the path: Path=DataContext.MyProp, I had assumed it has implicit. If I remove DataContext from the string, then it won't bind.
RelativeSource will change the target to, in this instance, UserControl. This way you can bind to elements on the UserControl, such as its Width/Height/etc. In these cases having an implicit DataContext would be counter-productive.
Is there a way to "combine" the SelectOneManu and autoComplete feature?
When the form is loaded I'd like that input field to display the current value of the bean property, plus ability to select a new value with autoComplete.
Primefaces already provides this already in the autocomplete component. Just look on the demo site. By adding the dropdown="true" on the autocomplete menu, you enable support for a dropdown. Concretely, follow the following steps to get your results
Set dropdown="true" on your autocomplete menu. Then set the completeMethod to correspond to a method on your backing bean that returns a list of the items you want to show up in the dropdown menu.
To preset the value on the autocomplete component, simply initialise the value in the backing bean to whatever you want. Take the following as an example. If you have
<p:autoComplete id="dd" dropdown="true" value="#{yourBackingBean.myVariable}" completeMethod="#{yourBackingBean.loadOptions}" />
In your backing bean, you initialise the myVariable type during it's declaration
String myVariable = "Desired Value";
If you're going to be populating the dropdown list with a list of complex/POJO types (and as a result, bind the value attribute to a complex type in the backing bean), you'll need to use the converter based autocomplete component implementation
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.