StaticResource not found for key TextToBoolConverter - xaml

I have a problem with a converter I need to use on a Comment text.
I get: "StaticResource not found for key TextToBoolConverter".
Converter:
namespace myMood.Helpers
{
public class TextToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value != null)
if (!(value is string)) return true;
return string.IsNullOrWhiteSpace(value as string) ? false : true;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
View:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myMood.Views.Entries"
Icon="ic_view_headline_white_24dp.png"
xmlns:converters="clr-namespace:myMood.Helpers"
xmlns:viewModels="clr-namespace:myMood.ViewModels">
...
<Label Text="{Binding Comment}"
IsVisible="{Binding Comment, Converter={StaticResource TextToBoolConverter}}">

Unless you have added it as an App resource, you should declare the converter as a local resource on every page you want to use it.
Just change your XAML to:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myMood.Views.Entries"
Icon="ic_view_headline_white_24dp.png"
xmlns:converters="clr-namespace:myMood.Helpers"
xmlns:viewModels="clr-namespace:myMood.ViewModels">
<ContentPage.Resources>
<ResourceDictionary>
<converters:TextToBoolConverter x:Key="TextToBoolConverter" />
</ResourceDictionary>
</ContentPage.Resources>
...
<Label Text="{Binding Comment}"
IsVisible="{Binding Comment, Converter={StaticResource TextToBoolConverter}}"/>
...
</ContentPage>

Related

How to display a list of class objects in a Collection View?

I want to display a list (observable collection) of BleDevice type in my view. I'm using mvvm pattern in .net maui (.net 7).
Model:
public class BleDevice
{
public BleDevice(){}
public BleDevice(string name, string mac)
{
Name = name;
MacAddress = mac;
}
public string Name { get; set; }
public string MacAddress { get; set; }
}
ViewModel:
public partial class MainViewModel: ObservableObject
{
public MainViewModel()
{
devices = new ObservableCollection<BleDevice>();
devices.Add(new BleDevice("Mystronics Winder", "00:00:00:00:00"));
devices.Add(new BleDevice("Living Room TV", "25:e7:aa:05:84"));
}
[ObservableProperty]
ObservableCollection<BleDevice> devices;
}
View(xaml): (Edited)
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp2.MainPage"
xmlns:viewmodel="clr-namespace:MauiApp2.ViewModel"
xmlns:model="clr-namespace:MauiApp2.Model"
x:DataType="viewmodel:MainViewModel">
<VerticalStackLayout>
<CollectionView ItemsSource="{Binding Devices}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{model:BleDevice}">
<Grid>
<Label Text="{Binding Name}"/>
<Label Text="{Binding MacAddress}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
Error:
XFC0045 Binding: Property "Name" not found on "MauiApp2.ViewModel.MainViewModel". MauiApp2 \source\repos\MauiApp2\MauiApp2\View\MainPage.xaml
Why it does recognize the "{Binding Devices}" but not "{Binding Name}" and "{Binding MacAddress}"?
ViewModel Remove the [ObservableProperty] attribute and change to this:
public partial class MainViewModel : ObservableObject
{
public MainViewModel()
{
Devices = new ObservableCollection<BleDevice>();
Devices.Add(new BleDevice("Mystronics Winder", "00:00:00:00:00"));
Devices.Add(new BleDevice("Living Room TV", "25:e7:aa:05:84"));
}
public ObservableCollection<BleDevice> Devices { get; set; }
}
View(xaml) Remove the x:DataType="xxx":
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp2.MainPage"
xmlns:viewmodel="clr-namespace:MauiApp2.ViewModel"
xmlns:model="clr-namespace:MauiApp2.Model"
>
<VerticalStackLayout>
<CollectionView ItemsSource="{Binding Devices}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding MacAddress}"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>

XAML Data Binding value not refreshing inside two ContentPages

I am trying to output a value from my object to two different entries. Both entries are on the same view but in different ContentPages as follows:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myApp.Views.ViewTabs.ViewHome"
xmlns:localTabs="clr-namespace:myApp.Views.ViewTabs"
xmlns:localObjPages="clr-namespace:myApp.Objects"
>
<ContentPage Title="PageOne">
<ContentPage.BindingContext>
<localObjPages:PagesObj/>
</ContentPage.BindingContext>
<ScrollView>
<StackLayout>
<Entry
x:Name="EntryOne" Text="{Binding BananaCount}"/>
<Entry
x:Name="EntryTwo" Text="{Binding BananaCount}"/>
</StackLayout>
</ScrollView>
</ContentPage>
<ContentPage Title="PageTwo">
<ContentPage.BindingContext>
<localObjPages:PagesObj/>
</ContentPage.BindingContext>
<ScrollView>
<StackLayout>
<Entry
x:Name="EntryThree" Text="{Binding BananaCount}"/>
</StackLayout>
</ScrollView>
</ContentPage>
</TabbedPage>
My Model:
public string BananaCount
{
get { return _bananaCount; }
set
{
if (_bananaCount != value)
{
_bananaCount = value;
NotifyPropertyChanged("BananaCount");
}
}
}
The object is updated and returned in EntryOne or in EntryTwo when I change it either in EntryOne or in EntryTwo. However, it is not updated in EntryThree. Why is this? Am I Binding this correctly? Thank you.
The object is updated and returned in EntryOne or in EntryTwo when I change it either in EntryOne or in EntryTwo. However, it is not updated in EntryThree. Why is this? Am I Binding this correctly?
Do one sample about TabbedPage, assign datasource for TabbedPage, not contentpage, that you can take a look:
<TabbedPage
x:Class="FormsSample.tabbedpage.TabbedPage6"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FormsSample.tabbedpage">
<!--Pages can be added as references or inline-->
<ContentPage Title="PageOne">
<ScrollView>
<StackLayout>
<Entry x:Name="EntryOne" Text="{Binding str}" />
<Entry x:Name="EntryTwo" Text="{Binding str}" />
<Button
x:Name="btn1"
Clicked="btn1_Clicked"
Text="change data" />
</StackLayout>
</ScrollView>
</ContentPage>
<ContentPage Title="PageTwo">
<ScrollView>
<StackLayout>
<Entry x:Name="EntryThree" Text="{Binding str}" />
</StackLayout>
</ScrollView>
</ContentPage>
public partial class TabbedPage6 : TabbedPage
{
public tabclass tabc { get; set; }
public TabbedPage6()
{
InitializeComponent();
tabc = new tabclass();
this.BindingContext = tabc;
}
private void btn1_Clicked(object sender, EventArgs e)
{
tabc.str = "this is test!";
}
}
public class tabclass:ViewModelBase
{
private string _str;
public string str
{
get { return _str; }
set
{
_str = value;
RaisePropertyChanged("str");
}
}
}
The ViewModel is one class that implement INotifyPropertyChanged.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Retrieving ItemsSource from a BindableLayout

I have a FlexLayout with a BindableLayout.
<FlexLayout BindableLayout.ItemsSource="{Binding CardItems}" x:Name="SourceLayout" Background="green"
Direction="Row" Wrap="Wrap">
<BindableLayout.ItemTemplate>
<DataTemplate>
<ContentView>
<Frame CornerRadius="20" Padding="0" WidthRequest="150" Margin="10"
HeightRequest="150"
BackgroundColor="{Binding .,
Converter={StaticResource AlternateColorConverter},
ConverterParameter={x:Reference SourceLayout}}">
<StackLayout>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.CardItemNavCommand, Source={x:Reference SourceLayout}}"
CommandParameter="{Binding NavTarget}"/>
</StackLayout.GestureRecognizers>
<Label Text="{Binding Text}" TextColor="Black" FontSize="20" VerticalOptions="FillAndExpand"/>
<Label Text="{Binding Text}" TextColor="Black" FontSize="20" VerticalOptions="FillAndExpand"/>
</StackLayout>
</Frame>
</ContentView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
Is it possible to get the index of the current item inside the converter so I can change the color accordingly? I know this can be achieved with a ListView because I can access the items source property but I can't access the resource from the BindableLayout.
Is it possible to get the index of the current item inside the converter so I can change the color accordingly
BindableLayout is a static class, so we cannot get it from the layout to get the itemsSource.
For this function ,try to create an 'Identifier' property in the model class and set binding for the backgroundColor. Then get the value in the converter class to obtain the index of the current item from the data collection. Sepcify the background color according to the index.
Check the code:
App.xaml.cs
public partial class App : Application
{
public static TestPageViewModel viewModel;
public App()
{
InitializeComponent();
viewModel = new TestPageViewModel();
MainPage = new NavigationPage(new TestPage());
}
}
Page.xaml
<StackLayout BindableLayout.ItemsSource="{Binding DataCollection}" ...>
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid Padding="0,2,3,0" BackgroundColor="{Binding Identifier, Converter={StaticResource _converter}}">
...
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
Page.xaml.cs
public partial class TestPage : ContentPage
{
public TestPage()
{
InitializeComponent();
BindingContext = App.viewModel;
}
}
Model class
public class TestPageModel
{
public string Content { get; set; }
public string Identifier { get; set; }
}
ValueConverter class
public class TestPageValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var index = GetValue(value);
switch (index)
{
case 1:
return Color.LightBlue;
break;
case 2:
return Color.LightPink;
break;
case 3:
return Color.LightYellow;
break;
default:
break;
}
return Color.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return -1;
}
double GetValue(object value)
{
var viewModel = App.viewModel;
foreach (var item in viewModel.DataCollection)
{
if (item.Identifier == (string)value)
{
return viewModel.DataCollection.IndexOf(item) + 1;
}
}
return -1;
}
}

Reusable ContentView Binding for an ActivityIndicator

I have an ActivityIndicator which I have in a number of different pages like this:
<ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
Color="DarkBlue" IsVisible="{Binding IsLoading}" IsRunning="{Binding IsLoading}">
</ActivityIndicator>-
I'm trying to make this component more easily reusable by creating a 'ContentView', which looks like this:
Loading.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="Framework.Controls.Loading" x.Name="Loading">
<ContentView.Content>
<ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Color="DarkBlue"
IsVisible="{Binding Source={x:Reference Name=Loading},Path=IsLoading}"
IsRunning="{Binding Source={x:Reference Name=Loading},Path=IsLoading}">
</ActivityIndicator>
</ContentView.Content>
</ContentView>
Which I am trying to consume with code like this:
<controls:Loading IsLoading="{Binding IsLoading}"></controls:Loading>
The problem I'm having is that I am not sure how to create a binding so that I can set the IsVisible and IsRunning properties on the ActivityIndicator.
My code-behind for the component looks like this:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
public static readonly BindableProperty LoadingIndicatorProperty =
BindableProperty.Create(
propertyName: nameof(IsLoading), typeof(bool),
typeof(Loading), default(string), BindingMode.OneWayToSource);
public bool IsLoading
{
get => (bool)GetValue(LoadingIndicatorProperty);
set => SetValue(LoadingIndicatorProperty, value);
}
public Loading()
{
InitializeComponent();
}
}
With the current code, I get build errors, so something obviously isn't right here.
The errors are as follows:
In the Loading.xaml file, I get a message that says for x.Name=Loading that the 'Type x not found in xmls http://xamarin.com/schemas/2014/forms and also that The attachable property 'Name' was not found in type 'x'.
On the page where I am trying to consume the control I get an error: No property, bindable property or event found for 'IsLoading', or mismatching type between value and property.
How do I correctly create a binding that will apply to the IsVisible and IsRunning properties on my Activity Indicator? And why doesn't the compiler like the ContentView 'x.Name' property?
First, you have a typo:
It is x:Name and you have x.Name
So, in your View:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="Framework.Controls.Loading"
x:Name="Loading">
<ContentView.Content>
<ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Color="DarkBlue"
IsVisible="{Binding IsLoading"
IsRunning="{Binding IsLoading">
</ActivityIndicator>
</ContentView.Content>
</ContentView>
then, in code-behind you can just:
XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
public static readonly BindableProperty LoadingIndicatorProperty =
BindableProperty.Create(
propertyName: nameof(IsLoading), typeof(bool),
typeof(Loading), default(string));
public bool IsLoading
{
get => (bool)GetValue(LoadingIndicatorProperty);
set => SetValue(LoadingIndicatorProperty, value);
}
public Loading()
{
InitializeComponent();
BindingContext=this;
}
}
I created a Xamarin.Forms control that does exactly that, and more (handle errors and empty state as well).
You can find it here:
https://github.com/roubachof/Sharpnado.Presentation.Forms#taskloaderview
The blog post explaining it in details is here:
https://www.sharpnado.com/taskloaderview-async-init-made-easy/

ListView not triggering ItemTapped

I've been working on a school assignment, where you need to load data into a ListView. According to the course manual, you need to use 'ObservableCollection' which I figured out. However, I can't seem to get the ItemTapped to work.
I'm creating the following table using SQLite.
public class Settings
{
[Primarykey]
public string Name { get; set; }
[MaxLength(255)]
public string Value { get; set; }
}
Then on the OnAppearing I initialise the database and add a row of data.
public partial class SQL : ContentPage
{
private SQLiteAsyncConnection _connection;
private ObservableCollection<Settings> _settings;
public SQL()
{
InitializeComponent();
_connection = DependencyService.Get<ISQLiteDb>().GetConnection();
}
protected override async void OnAppearing()
{
await _connection.CreateTableAsync<Settings>();
var settings_name = new Settings { Name = "Meaning of Life", Value = "42" };
await _connection.InsertAsync(settings_name);
await DisplayAlert("Alert", "Value Added to database!", "OK");
var settings = await _connection.Table<Settings>().ToListAsync();
_settings = new ObservableCollection<Settings>(settings);
ListView.ItemsSource = _settings;
base.OnAppearing();
}
void MyItemTapped (object sender, System.EventArgs e)
{
DisplayAlert("Alert", "You Pressed Something!", "OK");
}
void MyItemSelected (object sender, System.EventArgs e)
{
DisplayAlert("Alert", "You Selected Something!", "OK");
}
}
In my XAML file, I have the following, with the ItemTapped going to the function above.
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="BudgetBuddy.SQL">
<ContentPage.Content>
<ListView x:Name="ListView">
<ListView.ItemTemplate ItemTapped="MyItemTapped" ItemSelected="MyItemSelected">
<DataTemplate>
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
I can't figure out what I'm doing wrong. Why doesn't my ItemTapped and ItemSelected work? Also, what would be the best way to access the Value associated with the Name I pressed in the ListView from the ObservableCollection
The reason your ItemTapped and ItemSelected aren't working is because they are in the wrong location.
This is what you have:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="BudgetBuddy.SQL">
<ContentPage.Content>
<ListView x:Name="ListView">
<ListView.ItemTemplate ItemTapped="MyItemTapped" ItemSelected="MyItemSelected">
<DataTemplate>
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
This is what you should do:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="BudgetBuddy.SQL">
<ContentPage.Content>
<ListView x:Name="ListView" ItemTapped="MyItemTapped" ItemSelected="MyItemSelected">
<ListView.ItemTemplate >
<DataTemplate>
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>