Xamarin Forms XAML - Boolean attribute set from XAML - c#

I don't understand something about how to set object attribute for xaml about boolean..
I have a MainPage.xaml like this where I set ProportionalSize to true:
<ContentPage.Resources>
<ResourceDictionary>
<converter:BooleanConverter x:Key="Boolean"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<!-- Background during loading of start -->
<AbsoluteLayout>
<local:CustomImage Source="{extension:ImageResource HomeBG.png}"
ProportionalWidth="100" ProportionalHeight="100" ProportionalSize="{True, Converter={StaticResource Boolean}}"
AbsoluteLayout.LayoutBounds="0.5, 0.5, 1, 1"
AbsoluteLayout.LayoutFlags="All"/>
</AbsoluteLayout>
</ContentPage.Content>
I use a customImage for some reason, this is the class
public class CustomImage : Image
{
private bool _ProportionalSize;
public bool ProportionalSize
{
get { return this._ProportionalSize; }
set
{
this._ProportionalSize = value;
TrySize();
}
}
}
Because neither true nor True works, I made a BooleanConverter
public class BooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value;
}
}
However, it still doesn't work...
Additional information: Position 19:75. MarkupExtension not found for true
ProportionalSize="{True, Converter={StaticResource Boolean}}"
Does I'm doing something wrong?

Just set the value, you don't need to use markup extension syntax (those "{}" brackets) or a converter:
ProportionalSize="True"

If you're not actually binding to a value that will change... Don't use a converter or property. It looks like you just want to set true one time in XAML. You can use the x:Arguments attribute for that.
<x:Arguments>
<x:Boolean>True</x:Boolean>
</x:Arguments>
Bigger example from a DataTrigger.
Usecase - The grid has a different value binded to IsVisible, but we want to override that if the administrator is logged in. So in addition to the regular binding, we can put in a datatrigger that uses the x:Arguments of x:Boolean to set it to true. - Without a converter... without a property.
<Grid.Triggers>
<DataTrigger Binding="{Binding IsAdmin}"
TargetType="{x:Type Grid}"
Value="True">
<Setter Property="IsVisible">
<Setter.Value>
<x:Arguments>
<x:Boolean>True</x:Boolean>
</x:Arguments>
</Setter.Value>
</Setter>
</DataTrigger>
</Grid.Triggers>

Try to use BindableProperty:
public static readonly BindableProperty ProportionalSizeProperty =
BindableProperty.Create(nameof(ProportionalSize),
typeof(bool),
typeof(CustomImage),
default(bool),
propertyChanged: OnProportionalSizeChanged);
public bool ProportionalSize
{
get { return (bool)GetValue(ProportionalSizeProperty); }
set { SetValue(ProportionalSizeProperty, value); }
}
private static void OnProportionalSizeChanged(BindableObject bindable, object oldValue, object newValue)
{
var customImage = bindable as CustomImage;
if (customImage != null)
{
customImage.TrySize();
}
}

Related

How do you use a converter to enable/disable a Xaml control in Xamarin Forms?

As a starting point, my test project is a Xamarin Forms Tab project - from the Xamarin templates.
I have a converter:
using System;
using System.Collections;
using System.Globalization;
using Xamarin.Forms;
namespace TabExample.Converters
{
public class HaveItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value is ICollection)
{
return ((ICollection)value).Count > 0;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I have added it to App.xaml
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converters="clr-namespace:TabExample.Converters"
x:Class="TabExample.App">
<Application.Resources>
<ResourceDictionary>
<!-- Converters -->
<converters:HaveItemsConverter x:Key="HaveItemsConverter"/>
<!--Global Styles-->
<Color x:Key="NavigationPrimary">#2196F3</Color>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{StaticResource NavigationPrimary}" />
<Setter Property="BarTextColor" Value="White" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
I've updated the ListView in ItemsPage.xml to add IsEnabled, using the converter.
<ListView x:Name="ItemsListView"
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
RefreshCommand="{Binding LoadItemsCommand}"
IsPullToRefreshEnabled="true"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"
CachingStrategy="RecycleElement"
ItemSelected="OnItemSelected"
IsEnabled="{Binding Items, Mode=OneWay, Converter={StaticResource HaveItemsConverter}, Source={x:Reference BrowseItemsPage}}">
In ItemsPage.xaml.cs I added ItemsProperty:
public List<Item> Items
{
get { return (List<Item>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly BindableProperty ItemsProperty =
BindableProperty.Create("Items", typeof(List<Item>), typeof(ItemsPage), null, BindingMode.OneWay);
This does not work. The converter receives null. What I need is the converter to use the Items ObservableCollection from the ItemsViewModel:
public ObservableCollection<Item> Items { get; set; }
How do I property hook up binding in the Xaml to use the HaveItemsConverter to retrieve the list from ItemsViewModel and return a bool that is used to enable or disable the list?
Cause:
Source={x:Reference BrowseItemsPage}
When you set the Source , the BindingContext will be changed at same time;
Solution:
What is the BrowseItemsPage? If it is your viewmodel , you should set the BindingContext in contentPage .Otherwise just use
IsEnabled="{Binding Items, Mode=OneWay , Converter={StaticResource HaveItemsConverter}}
I didn't get the full scope your code, that is why I am providing the Simple and quick solution, add one more property like ItemsAvailable,
bool _itemsAvailable;
Public bool ItemsAvailable
{get {return _itemsAvailable;}}
{set {_itemsAvailable=value; RaisePropert....}}
And set the above bool variable under your Observablecollection Set like below,
public ObservableCollection<Item> _items;
public ObservableCollection<Item> Items
{
get
{
return _items;
}
set
{
_items = value;
if(_items!=null && _items.Count>0)
{
ItemsAvailable = true;
}
}
}
And bind this ItemsAvailable property for your Visible Property and remove converter which is not required.
Happy coding :)

C# XAML Setter value to True depending on string property value

I am having trouble with what seems like a fairly straightforward task. I have a treeview with nodes, and I would like to set the TreeViewItem 'IsExpanded' property to True for the whole treeview if a specific string property 'SearchTerm' of my View Model is not empty. In other words, if string property is not null, IsExpanded value should be True. I have already done this in codebehind but I prefer to do this in XAML for cleanness.
To describe the code below, I created a converter which will convert a null string to 'False' and non-null to 'True'. In my XAML I call this converter when I attempt to bind the string value from the viewmodel in the TreeView ItemContainerStyle. It appears that the converter is never even fired.
My XAML (simplified):
<UserControl.Resources>
<cv:ExpandNodesIfSearchConverter x:Key="ExpandAll">
</cv:ExpandNodesIfSearchConverter>
</UserControl.Resources>
<TreeView Grid.Row="2" x:Name="myTreeView"
ItemsSource="{Binding Sponsors}"
SelectedItemChanged="TreeView_SelectedItemChanged" >
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- if SearchTerm is not null, use converter to set value to true and expand all nodes -->
<Setter Property="IsExpanded" Value="{Binding Path=SearchTerm, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource ExpandAll}}" />
</Style>
</TreeView.ItemContainerStyle>
<!-- TreeView data -->
</TreeView>
My View Model:
public class TreeViewVM : INotifyPropertyChanged
{
private string _searchterm;
public string SearchTerm
{
get
{
return _searchterm;
}
set
{
_searchterm = value;
OnPropertyChanged("SearchTerm");
}
}
}
My Converter:
class ExpandNodesIfSearchConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//if searchterm is not null, return true to expand all items, otherwise return false
string searchterm = value.ToString();
if (string.IsNullOrEmpty(searchterm))
return false;
else
return true;
}
public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I solved my issue by using the ElementName in the Setter tag rather than the viewmodel property. searchTxt being the name of the TextBox which SeachTerm was bound to.
<Setter Property="IsExpanded" Value="{Binding ElementName=searchTxt, Path=Text, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource ExpandAll}}" />

XAML - Converter within Style

I would like to include a Converter for TextBlock' Text property within a Style. I have the following code:
<Page.Resources>
<converters:StringFormatConverter x:Key="StringFormatConverter" />
<Style x:Key="StringFormatStyle" TargetType="TextBlock">
<Setter Property="Text">
<Setter.Value>
<Binding>
<Binding.Converter>
<converters:StringFormatConverter />
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
...
<TextBlock Text="some text" Style="{StaticResource StringFormatStyle}" />
<TextBlock Text="{Binding AppName}" Style="{StaticResource StringFormatStyle}" />
The problem is that the Convert method of my Converter isn't being called. The style is applied (100% sure of that).
What could be the problem of the converter not being applied to the TextBlock' Text property?
Long story short: I have several double properties in my ViewModel and I would like to display them formatted using a Converter.
PS: My StringFormatConverter looks like this:
public class StringFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
{
return null;
}
if (parameter == null)
{
return value;
}
return string.Format((string)parameter, value);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Well, you can't.
In style you set text to some invalid binding with only a converter. And later you set it to static text.
If you want to add specific handling to a value and want to avoid code duplicates, use attached variable.
Also note that you're creating two converters actually. One in style and one in resources. So just reference the one from resources, don't need to create it in style again.
Here's attached property sample.
public class TextConv: DependencyObject
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.RegisterAttached(
"Text",
typeof(string),
typeof(TextConv),
new PropertyMetadata(null, OnValueChanged)
);
public static void SetText(UIElement element, string value)
{
element.SetValue(TextProperty, value);
}
public static string GetText(UIElement element)
{
return (string)element.GetValue(TextProperty);
}
private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
obj.SetValue(TextBlock.TextProperty, args.NewValue.ToString() + "Hello!");
}
}
And in xaml:
<TextBlock local:TextConv.Text="some text" />
It might not work out of the box but you get the general idea.
If you set the Text property explicitly, that will override the property setter in the style.
Use your converter in the Binding instead of the Style.
Text="{Binding AppName, Converter={StaticResource StringFormatConverterKey}}"

Is there a way to bind the Enabled property of a button to a checkbox's Checked property?

I want to bind IsEnabled of Button in WPF as follows:
WPF Code:
<Button Content="TestButton" IsEnabled="{Binding ??}" />
C# code:
private MyObjectClass _Checked;
public MyObjectClass Checked
{
get { return _Checked; }
set
{
_Checked = value;
RaisePropertyChanged("Checked");
}
}
In WPF code above, I want the button to be enabled only when Checked object is not null. I know one way is to have a bool property in C# code which tells me whether the Checked object is null or not, and then bind it to IsEnabled property. I want to know if there is a way I can bind IsEnabled to Checked object directly?
Use DataTrigger and check for {x:Null} value of binding:
<Button Content="TestButton">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Checked}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Also, you can use IValueConverter which will return false if value is null otherwise true.
public class ObjectToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
bool returnValue = true;
if (value == DependencyProperty.UnsetValue || value == null)
{
returnValue = false;
}
return returnValue;
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
return Binding.DoNothing;
}
}
and bind in XAML:
<Button Content="TestButton"
IsEnabled="{Binding Checked,
Converter={StaticResource ObjectToBoolConverter}}" />
Ofcourse you need to declare instance of converter in your XAML for this.
You can use a converter to convert an object into a bool. Look into IValueConverter.
public class IsNotNullToBoolConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("Two-way binding not supported by IsNotNullToBoolConverter");
}
}
And your xaml would look like this:
<Window.Resources>
<local:IsNotNullToBoolConverter x:Key="IsNotNull" />
</Window.Resources>
...
<Button IsEnabled="{Binding Converter={StaticResource IsNotNull}}" />
I know this is an old issue but you can do this without an extra code. Just add the "TargetNullValue=false" to the binding.
IsEnabled="{Binding SomeProperty, TargetNullValue=false}"

Binding a property to a style/resourcedictionary of a view

One of my views consists of 5 UserControls that each display data about a certain object. Let's say for example that the view displays the cows our company has, and on the screen cows 1 through 5 are displayed (each in their own UserControl).
What I want to do (but not sure is possible) is to bind the status of a cow to the style used in its respective UserControl. So we have a property status that could be ok, hungry, dead for example. In case the cow is ok I want to display a 'normal' style, if it's hungry I want the background to be red and if it's dead I want the text to be black and the fontsize increased.
I've added a simplified version of what I'm trying to achieve. My knowledge of WPF styles/resource dictionaries is still somewhat limited though.
What I basically want in code
A ViewModel with a Status property
class CowInfoViewModel : Screen
{
public string Name { get; set; }
public string Status { get; set; } //"ok", "hungry", "dead"
}
A View that retrieves a style or resourcedictionary
<UserControl x:Class="WpfModifyDifferentView.Views.CowInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- A reference to a ResourceDictionary with styles, that is bound to the 'Status' property -->
<StackPanel>
<TextBlock x:Name="Name" Text="Cow Name"/>
<TextBlock x:Name="Status" Text="Ok" />
</StackPanel>
</UserControl>
EDIT - Solution:
I did the following using Vale's answer:
In the xaml (reference to the converter):
<UserControl.Resources>
<Converters:CowStyleConverter x:Key="styleConverter" />
</UserControl.Resources>
In the xaml (elements):
<TextBlock x:Name="Name" Text="Cow Name" Style="{Binding Path=Style, ConverterParameter='TextBlockCowName', Converter={StaticResource styleConverter}}" />
The converter (note I left out the checks):
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var status = value.ToString();
var styleName = parameter.ToString();
_resourceDictionary.Source = new System.Uri(string.Format("pack://application:,,,/Resources/ScreenI2Style{0}.xaml", status));
return _resourceDictionary[styleName];
}
Then I created multiple ResourceDictionaries with styles such as:
<Style x:Key="TextBlockCowName" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource SomeBrush}" />
</Style>
You can bind UserControl Style property to Status and use a converter.
<UserControl x:Class="WpfModifyDifferentView.Views.CowInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfModifyDifferentView"
Style="{Binding Path=Status, Converter={StaticResource myConverter}}">
<UserControl.Resources>
<local:MyConverter x:Key="myConverter" />
</UserControl.Resources>
I assume that your converter is in WpfModifyDifferentView directly.
Converter will look like this:
public class MyConverter : IValueConverter {
private ResourceDictionary dictionary;
public MyConverter() {
if (dictionary == null) {
dictionary = new ResourceDictionary();
dictionary.Source = new Uri("pack://application:,,,/WpfModifyDifferentView;Component/Resources/Styles.xaml");
}
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
switch (value.ToString()) {
case "ok":
return dictionary["myKeyForOkStyle"] as Style;
case "hungry":
return dictionary["myKeyForHungryStyle"] as Style;
case "dead":
return dictionary["myKeyForDeadStyle"] as Style;
default:
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
You need to specify the correct URI of course.

Categories

Resources