I have a page in my Xamarin.Forms application where I'm attempting to bind a relative size (25%, 50%, etc) to the basis of an item within a FlexLayout, but can't get it to work successfully.
My XAML:
...
<FlexLayout x:Name="Flex"
BindableLayout.ItemsSource="{Binding Path=SelectedSideBarItem.Items}"
AlignItems="Start"
JustifyContent="Start"
Direction="Row"
AlignContent="Start"
Wrap="Wrap">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Button FlexLayout.Basis="{Binding ItemBasis}"
Title="{Binding Name}"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
...
I can't seem to find any examples, and the documentation doesn't seem to mention anything about binding to a Basis property from a view model. My initial attempt was attempting to bind a string to the to the FlexLayout.Basis. Like so:
...
private string _itemBasis;
public string ItemBasis
{
get => _itemBasis;
set
{
_itemBasis = value;
OnPropertyChanged(nameof(ItemBasis));
}
}
...
public void SetBasis()
{
ItemBasis = "25%"; // or any other percentage.
// Does not work!
}
...
The above doesn't work. It appears that the basis is never set, so the default is used.
I should note that binding to a basis does works when using an absolute size (250, 500, etc) (i.e. it's not an issue with my bindings). Like so:
...
private int_itemBasis;
public int ItemBasis
{
get => _itemBasis;
set
{
_itemBasis = value;
OnPropertyChanged(nameof(ItemBasis));
}
}
...
public void SetBasis()
{
ItemBasis = 250; // or any other absolute value.
// Works!
}
...
Any guidance would be appreciate.
Thanks!
The type for the Basis property is FlexBasis
https://github.com/xamarin/Xamarin.Forms/blob/bd31e1e9fc8b2f9ad94cc99e0c7ab058174821f3/Xamarin.Forms.Core/FlexEnums.cs#L58
If you bind it to a string is not going to work (unless using a converter).
Try to modify your bindable property to FlexBasis and assign the value like this:
ItemBasis = new FlexBasis(.5f, true);
The first parameter is the length, the second is the "relative" flag. Setting it to true means that you are using a percentage value.
XAML TypeConverters are not called when a value is set via binding, so the default FlexBasis TypeConverter is not applied when you try to bind the string "25%".
Fortunately the FlexBasisTypeConverter class is public so you can use it in your own converter class like so:
public class StringToFlexBasisConverter: IValueConverter
{
private readonly FlexBasis.FlexBasisTypeConverter _converter = new FlexBasis.FlexBasisTypeConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue)
{
return _converter.ConvertFromInvariantString(stringValue);
}
return new FlexBasis();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Related
I am working with Xamarin.Forms(Version 5.0.0.2012) Shell app for Android and iOS. In the app, I have a concept in which I have to show Shell Search box if an user signed in, else have to hide the search box. I am following the standard MVVM pattern.
<Shell.SearchHandler>
<helper:ProductSearchHandler Placeholder="Search by Title, ISBN, Publisher" TextColor="{StaticResource Primary}" FontSize="Micro"
ShowsResults="true" SearchBoxVisibility="{Binding TopSearchVisibility}" DisplayMemberName="Name"
ItemTemplate="{StaticResource ProductSearchTemplate}" />
</Shell.SearchHandler>
The above is the code for placing Search in my ContentPage.
TopSearchVisibility is my bindable property to which I bind the value from viewmodel which inherits BaseViewModel as usual.
ProductSearchHandler is the search handler class.
In my viewmodel,
// Declaration with default value
public string topSearchVisibility = "Collapsible";
public string TopSearchVisibility
{
get { return topSearchVisibility; }
set
{
topSearchVisibility = value;
OnPropertyChanged();
}
}
After getting user data from API service call, I am doing the process of show or hide the search box based on UserId, as below
long userID = ... from API;
if (userID > 0)
TopSearchVisibility = "Hidden";
else
TopSearchVisibility = "Collapsible";
After page loaded, SearchBoxVisibility property of Shell SearchHandler always binds only its default value Expanded. Due to that, page has the expanded view of Shell search. Shell search doesn't take the bindable property value. How to achieve my requirement with Shell search?
You could use IValueConverter Interface to convert the string to SearchBoxVisibility property.
like:
create SearchVisibleConvert:
public class SearchVisibleConvert : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
SearchBoxVisibility searchBoxVisibility = SearchBoxVisibility.Expanded;
if (value !=null)
{
switch (value)
{
case "Hidden":
searchBoxVisibility = SearchBoxVisibility.Hidden;
break;
case "Collapsible":
searchBoxVisibility = SearchBoxVisibility.Collapsible;
break;
default:
searchBoxVisibility = SearchBoxVisibility.Expanded;
break;
}
}
return searchBoxVisibility;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
use in your xaml:
...
<ContentPage.Resources>
<ResourceDictionary>
<controls:SearchVisibleConvert x:Key="visibleConvert" />
</ResourceDictionary>
</ContentPage.Resources>
<Shell.SearchHandler>
<helper:ProductSearchHandler Placeholder="Search by Title, ISBN, Publisher" TextColor="{StaticResource Primary}" FontSize="Micro"
ShowsResults="true" SearchBoxVisibility="{Binding TopSearchVisibility, Converter={StaticResource visibleConvert}}" DisplayMemberName="Name"
ItemTemplate="{StaticResource ProductSearchTemplate}" />
</Shell.SearchHandler>
I am trying to bind an ObservableCollection, which is filled in a Background thread, to a charting Control in the UI.
Therefore i have a static class "Core", which have a member "DataState". This DataState class owns the ObservableCollection "SensorData", which is filled in the aforementioned Background Task.
As there is no possibility to do UWP XAML Binding for static properties i wrote a wrapper class "DataWrapper" which is nonstatic and refers to the static Core.DataState.SensorData-object.
Here's some of my Code:
Core.cs:
public static partial class Core
{
private static DataState m_DataState;
public static DataState DataState
{
get
{
return m_DataState;
}
set
{
if (value != null)
{
m_DataState = value;
}
}
}
}
DataState.cs:
public class DataState
{
private ObservableCollection<SensorData> m_SensorData = new ObservableCollection<SensorData>();
public ObservableCollection<SensorData> SensorData
{
get
{
return m_SensorData;
}
set
{
if (value != null)
m_SensorData = value;
}
}
DataWrapper.cs:
public class DataWrapper
{
public ObservableCollection<SensorData> SensorData
{
get
{
return Core.DataState.SensorData;
}
}
}
XAML:
<Charting:LineSeries Name="MySeries" Title="Title" IndependentValuePath="X" DependentValuePath="Y" ItemsSource="{x:Bind DataWrapper.SensorData}"></Charting:LineSeries>
where X and Y are the variables contained in the SensorData-Object.
So, if i wait to Show the Charting-Control until there's some data in the ObservableCollection this data is nicely plotted into my Control. But after that recently added data is not plotted anymore.
Therefore i am looking for a way to route the PropertyChanged-Event from Core.DataState.SensorData somehow to DataWrapper.SensorData. Is there any possibility to do this?
Is the structure of this Problem clear to you? I think my descriptions sounds a bit confusing...
Thank you in advance for any help :-)
I am defining this in my designer:
<sap:WorkflowItemPresenter>
<statements:Assign DisplayName="Assign"/>
</sap:WorkflowItemPresenter>
I thought it would simply work if i add the Assign there but i was wrong.
[Browsable(false)]
public Activity Body { get; set; }
protected override void Execute(NativeActivityContext context)
{
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete));
}
/// <summary>
/// Called from Execute when Condition evaluates to true.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="instance">The instance.</param>
public void OnExecuteComplete(NativeActivityContext context, ActivityInstance instance)
{
//to be added
}
This is the code from the base class.
I don't need to alter the Assign activity at all, i just want to get access to the NativeActivityContext. In fact i am trying to wrap it up and do some checks on the context's properties in the OnExecuteComplete method. Is there a way to accomplish this?
EDIT 1:
DotNetHitMan suggested and shown me on WF 4 Rehosted Designer - get foreach InArgument Value how to work with those trackings and i indeed succeeded to work this out with his solution:
if (trackingEventArgs.Activity is Assign)
{
Assign ass = trackingEventArgs.Activity as Assign;
if (ass.To.Expression != null)
{
dynamic vbr = null;
if ((ass.To.Expression is VisualBasicReference<int>))
{
//vbr.ExpressionText will hold the value set in the To section of the Assign activity, one of the variables will reside here
vbr = ass.To.Expression as VisualBasicReference<int>;
}
else if ((ass.To.Expression is VisualBasicReference<string>))
{
vbr = ass.To.Expression as VisualBasicReference<string>;
}
ActivityStateRecord activityStateRecord = null;
if (trackingEventArgs.Record != null)
activityStateRecord = trackingEventArgs.Record as ActivityStateRecord;
if (activityStateRecord != null)
{
if (activityStateRecord.Arguments.Count > 0)
{
//checking if the variable defined in the To section is to be displayed in the watch window
GlobalFunctions.WatchWindowViewModel.VariableDefinition existingVariable = GlobalFunctions.WatchWindowViewModel.Instance.VariableExists(vbr.ExpressionText);
if (existingVariable != null)
{
foreach (KeyValuePair<string, object> argument in activityStateRecord.Arguments)
{
if (argument.Key.Equals("Value"))
{
Application.Current.Dispatcher.Invoke(
() =>
{
existingVariable.VariableValue.Clear();
existingVariable.VariableValue.Add(
argument.Value.ToString());
});
}
}
}
}
}
}
}
I still face something a bit ugly. When checking the arguments for the Assign activity i get the key "Value". But if i define a variable named "i" and want to see its changes as this Assign executes i have to take a look at that VisualBasicReference<> to check the name of the variable declared there just like in the code above. This way of doing it works indeed and i managed to cover ints and strings which is fine for now .. but is there any shortcut that can be used in my code ?
EDIT 2
I got a new idea today and put it to work:
Here is the library code:
public sealed class CustomAssign : NativeActivity, IActivityTemplateFactory
{
[Browsable(false)]
public Activity Body { get; set; }
protected override void Execute(NativeActivityContext context)
{
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete));
}
/// <summary>
/// Called from Execute when Condition evaluates to true.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="instance">The instance.</param>
public void OnExecuteComplete(NativeActivityContext context, ActivityInstance instance)
{
//to be added
}
Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
return new CustomAssign
{
Body = new Assign()
};
}
}
And the designer:
<sap:ActivityDesigner x:Class="ARIASquibLibrary.Design.CustomAsignDesigner"
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:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
xmlns:statements="http://schemas.microsoft.com/netfx/2009/xaml/activities" Collapsible="False" BorderThickness="20" BorderBrush="Transparent">
<sap:ActivityDesigner.Template>
<ControlTemplate TargetType="sap:ActivityDesigner">
<Grid>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</sap:ActivityDesigner.Template>
<DockPanel LastChildFill="True">
<sap:WorkflowItemPresenter Item="{Binding Path=ModelItem.Body, Mode=TwoWay}"/>
</DockPanel>
</sap:ActivityDesigner>
So, in a few words: i've hosted the Assign activity in my custom activity and changed the ControlTemplate in order to keep only the ContentPresenter, which in turn will be the Assign. Now, by dragging it to the designer, you will have exactly the original appearance but with the ability to write code and check the execution steps in the :
protected override void Execute(NativeActivityContext context)
or
public void OnExecuteComplete(NativeActivityContext context, ActivityInstance instance)
Why is that? Through the context.DataContext you can get to all the variables and arguments in the scope where this activity resides in order to develop a watch window.
Rather than dealing with each variable type just convert the expression to its base interface.
ITextExpression vbr = ass.To.Expression as ITextExpression;
You can then just access the expression text property without caring about the type of variable assigned to the expression.
GlobalFunctions.WatchWindowViewModel.VariableDefinition existingVariable = GlobalFunctions.WatchWindowViewModel.Instance.VariableExists(vbr.ExpressionText);
This should cater for (I hope) all variable types that can be applied.
I have a textblock bound to an object. The 2-way binding works well and as expected.
In the code-behind:
txtNumberOfPlayers.DataContext = tournament.ChipSet;
In the .xaml:
<toolkit:NumericUpDown x:Name="txtNumberOfPlayers" Value="{Binding NumberOfPlayers, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" />
In the Chipset class I raise a change notification when the NumberOfPlayers is set (OnPropertyChanged("NumberOfPlayers");)
But... when I completely reassign the object it does not update the UI unless I call the datacontext assignment again. For example, lets say I load a different chipset object.
Chipset newChipSet = LoadChipset();
tournament.ChipSet = newChipSet;
This does not update the txtNumberOfPlayers when the assignement is made. It only works if I do this:
Chipset newChipSet = LoadChipset();
tournament.ChipSet = newChipSet;
//have to call this again which seems redundant
txtNumberOfPlayers.DataContext = tournament.ChipSet;
So I thought, maybe I have to put the change notification on the Chipset object like this:
private Chipset chipset;
public Chipset ChipSet
{
get { return chipset; }
set
{
if (chipset != value)
{
chipset = value;
OnPropertyChanged("ChipSet");
}
}
}
but that does not work.
So my questions is - how do I get the UI to update when I assign a new object to the old one without rebinding the datacontext.
Thanks!
You should specify RelativeSource to your Binding:
Value={Binding NumberOfPlayers, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type YourNamespace:YourTypeContainingChipsetProperty}}}
EDIT
Example of DependencyProperty in your case. Change YourCustomControl to class name of your control:
public static DependencyProperty ChipsetProperty =
DependencyProperty.Register("Chipset", typeof(Chipset),
typeof(YourCustomControl),
new FrameworkPropertyMetadata
(null,
FrameworkPropertyMetadataOptions
.
BindsTwoWayByDefault, ChipsetPropertyChangedCallback));
public Chipset Chipset
{
get { return (Chipset)GetValue(ChipsetProperty); }
set { SetValue(ChipsetProperty, value); }
}
private static void ChipsetPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var yourCustomControl = d as YourCustomControl;
if (yourCustomControl != null)
{
//your logic on property changed goes here; don't raise OnPropertyChanged!
}
}
I have an ASPxGridView from DevExpress fed with data from ObjectDataSource. My data row objects expose properties such ParameterName, ParameterType and ParameterValue.
//Properties, constructor and private fields code omitted for clarity
public class InputParameterDescription
{
public string ParameterName;
public Type ParameterType;
public int ParameterPrecision;
public string ParameterDescription;
}
ParameterValue is always an object of type indicated by ParameterType property. In fact, I use few types – Int32, Double, String or Boolean. When I display values in a grid and user clicks “Edit” a ParameterValue is always edited with TextBox. Is it possible to change editor for this column according to ParameterType? I want my users to use SpinEdit for integers, checkbox for Boolean, etc.
In fact, this is the way people have been working with DevExpress Delphi grids - TdxGrid and TcxGrid (OnGetProperties event). I have asked this question in DevExpress forum, but haven’t got any answer :(
You could create a template on that column that would do the switch for you. Something like:
public class SwitchTemplate : ITemplate
{
public void Instantiate(Control container)
{
GridViewDataItemTemplateContainer cnt = (GridViewDataItemTemplateContainer) container;
switch( GetStringParameterTypeFromDataItem(cnt.DataItem) )
{
case "Int32":
container.Controls.Add( new ASPxSpinEdit() { ... } );
break;
case "DateTime":
container.Controls.Add( new ASPxDateEdit() { ... } );
break;
case "String":
container.Controls.Add( new ASPxTextBox() { ... } );
break;
...
}
}
}
Then you just need to specify this template as the EditItemTemplate of the column:
myGrid.Columns["MyColumnName"].EditItemTemplate = new SwitchTemplate()