I'm new winphone. I have a issue with binding data.
I have a class weather with 2 properties Temp_C, and Temp_F. I want to bind temperature to a textblock.
I also have a switch to choose C and F.
How can I do to when I switch to C => text block binding Temp_C when I switch to F => text block binding Temp_F.
A solution could be to just have two text block and just toggle there visibility:
<TextBlock Text="{Binding Temp_C}" Visibility="{Binding Checked,ElementName=temperatureTogled,Converter={StaticResource boolToVisibilityConverter}}"/>
<TextBlock Text="{Binding Temp_F}" Visibility="{Binding Checked,ElementName=temperatureTogled,Converter={StaticResource boolToNotVisibilityConverter}}"/>
<toolkit:ToggleSwitch x:Name="temperatureTogled" .. />
Or otherwise another solution is just to add a Temp property and a IsCelsus property in your viewModel, do the switch between the two value inside your Temp property and bind the Temp property:
public class CurrentCondition : INotifyPropertyChanged
{
private bool isC;
public bool IsC
{
get { return isC; }
set
{
if (isC != value)
{
isC = value;
this.RaisePropertyChanged("IsC");
this.RaisePropertyChanged("TempShow");
}
}
}
private string temp_C;
public string Temp_C
{
get { return temp_C; }
set
{
if (temp_C != value)
{
temp_C = value;
this.RaisePropertyChanged("Temp_C");
this.RaisePropertyChanged("TempShow");
}
}
}
private string temp_F;
public string Temp_F
{
get { return temp_F; }
set
{
if (temp_F != value)
{
temp_F = value;
this.RaisePropertyChanged("Temp_F");
this.RaisePropertyChanged("TempShow");
}
}
}
private string tempShow;
public string TempShow
{
get
{
if (this.isC == true)
{
return temp_C + "°C";
}
else
{
return temp_F + "°F";
}
return tempShow;
}
}
}
<TextBlock Text="{Binding TempShow}"/>
<toolkit:ToggleSwitch x:Name="temperatureTogled" Checked="{Binding IsC,Mode=TwoWay}" />
Xaml
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock TextAlignment="Center" FontSize="{StaticResource PhoneFontSizeExtraExtraLarge}" Grid.ColumnSpan="2" Grid.Row="0" x:Name="txtTemperature" />
<RadioButton Checked="CentTempChecked" GroupName="Temperature" Grid.Column="0" Grid.Row="1" Content="C" />
<RadioButton Checked="FarTempChecked" GroupName="Temperature" Grid.Column="1" Grid.Row="1" Content="F" />
</Grid>
Code Behind
using System.Globalization;
public partial class MainPage : PhoneApplicationPage
{
private TemperatureUnitType temperatureUnitType = TemperatureUnitType.Celsius;
public MainPage()
{
InitializeComponent();
BindTemperature();
}
private void CentTempChecked(object sender, RoutedEventArgs e)
{
this.temperatureUnitType = TemperatureUnitType.Celsius;
BindTemperature();
}
private void FarTempChecked(object sender, RoutedEventArgs e)
{
this.temperatureUnitType = TemperatureUnitType.Fahrenheit;
BindTemperature();
}
private void BindTemperature()
{
var temperature = new Temperature();
txtTemperature.Text =
this.temperatureUnitType == TemperatureUnitType.Celsius ?
temperature.Celsius.ToString(CultureInfo.InvariantCulture) : temperature.Fahrenheit.ToString(CultureInfo.InvariantCulture);
}
}
public enum TemperatureUnitType
{
Celsius = 0,
Fahrenheit = 1,
}
public class Temperature
{
public double Celsius { get { return 45.3d; } }
public double Fahrenheit { get { return 96.3d; } }
}
In a publication app, you should probably use the toggle control present in the WP toolkit. You should also be persisting the temperature preference in the IsolatedStorage DB.
Related
So I have a label and I want to set the text colour from a mvvm variable.
VM
[ObservableProperty]
private string col = "White";
XAML
<Label Text="{Binding Name}"
FontSize="20"
TextColor="{Binding Col}">
So in general TextColor="White" works fine
I've tried using the Color object https://learn.microsoft.com/en-us/dotnet/maui/user-interface/graphics/colors
e.g.
[ObservableProperty]
private Color col = Colors.White;
but I can't get it to work.
I had hoped that a simple string would work...oh for my vain hopes...
Thanks, G.
Edit: I should add that my label is in a CollectionView?
BIG EDIT:
IT WORKS for a standalone label
i.e.
[ObservableProperty]
private Color col = Colors.White;
So the issue is if the label is in a CollectionView. I wonder why?
EDIT: Because the CollectionView is bound to the ItemsSource - doh what a dummy!
If you want to bind color(which type is string) to your view, you can use Binding value converters to achieve this.
I created a demo to achieve this , you can refer to the following code:
MyModel.cs
public class MyModel: INotifyPropertyChanged
{
string name;
public string Name
{
set
{
if (name != value)
{
name = value;
OnPropertyChanged("Name");
}
}
get
{
return name;
}
}
string _value;
public string Value
{
set
{
if (_value != value)
{
_value = value;
OnPropertyChanged("Value");
}
}
get
{
return _value;
}
}
private string _textColor = "Green";
public string TextColor
{
get { return _textColor; }
set
{
_textColor = value;
OnPropertyChanged("TextColor");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MyViewModel.cs
public class MyViewModel
{
public ObservableCollection<MyModel> dataList { get; set; }
public ICommand ColorChangeCommand { protected set; get; }
public MyViewModel()
{
dataList = new ObservableCollection<MyModel>();
dataList.Add(new MyModel() { Name = "test1", Value = "1" });
dataList.Add(new MyModel() { Name = "test2", Value = "2" });
dataList.Add(new MyModel() { Name = "test3", Value = "3" });
ColorChangeCommand = new Command<MyModel>(async (key) =>
{
key.TextColor = "Red";
});
}
}
StringToColorConverter.cs
public class StringToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var color = value.ToString();
switch (color)
{
case "Green":
return Colors.Green;
case "Red":
return Colors.Red;
default:
return Colors.Yellow;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
A usage:
<?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="MauiApp0706.Tab1"
xmlns:local="clr-namespace:MauiApp0706"
Title="Tab1">
<ContentPage.Resources>
<ResourceDictionary>
<local:StringToColorConverter x:Key="ColorConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<VerticalStackLayout>
<CollectionView
ItemsSource="{Binding dataList}"
x:Name="mylistview"
>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Column="0">
<Label Text="{Binding Name}" TextColor="{Binding TextColor, Converter = {StaticResource ColorConverter}}"/>
</Grid>
<Grid Column="1">
<Label Text="{Binding Value}" TextColor="{Binding TextColor, Converter = {StaticResource ColorConverter}}"/>
</Grid>
<Grid Column="2">
<Button Text="change" Command="{Binding BindingContext.ColorChangeCommand, Source={x:Reference Name=mylistview} }" CommandParameter="{Binding .}"></Button>
</Grid>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
Not to detract from the answer already given, but a couple of points to note for people running into this...
"Color col = Colors.White" - Color and Colors aren't the same thing, and within "Color" there is System.Drawing.Color and Microsoft.Maui.Graphics.Color, so be careful you're not accidentally mixing types.
If you do your UI in C# rather than XAML, then you can just bind directly to a Color to begin with and get rid of all the string-converting.
I want to change the data of one object in my ObservableRangeCollection, and that works but the corresponding 'CollectionView` does not update the changed data.
'CollectionView`
<RefreshView Grid.Row="1"
Grid.RowSpan="2"
Command="{Binding RefreshCommand}"
IsRefreshing="{Binding IsBusy, Mode=OneWay}">
<CollectionView x:Name="Collection"
ItemsSource="{Binding Locations}"
SelectionMode="Single"
BackgroundColor="Transparent"
ItemsLayout="VerticalList">
<CollectionView.EmptyView>
<StackLayout Padding="12">
<Label HorizontalOptions="Center" Text="Keine Daten vorhanden!" TextColor="White"/>
</StackLayout>
</CollectionView.EmptyView>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:MainModel">
<Frame HeightRequest="260">
<Grid>
<Image Source="{Binding Image}"
Aspect="AspectFill"/>
<Label Text="{Binding Name}"
FontSize="30"
TextColor="White"/>
</Grid>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
ViewModel
public class MainViewModel : VeiwModelBase
{
public ObservableRangeCollection<MainModel> Locations { get; set; } = new ObservableRangeCollection<MainModel>();
public ICommand RefreshCommand { get; }
public MainViewModel()
{
RefreshCommand = new AsyncCommand(Refresh);
}
public override void VModelActive(Page sender, EventArgs eventArgs)
{
base.VModelActive(sender, eventArgs);
var locs = new MainModel() { Image = "https://club-l1.de/wp-content/uploads/2019/11/dsc08645-1200x800.jpg", Name = "Test" };
Locations.Add(locs);
foreach (MainModel loc in Locations)
{
loc.Name = "Update";
}
}
private async Task Refresh()
{
IsBusy = true;
var locs = new MainModel() { Image = "https://club-l1.de/wp-content/uploads/2019/11/dsc08645-1200x800.jpg", Name = "Test"};
Locations.Add(locs);
foreach (MainModel loc in Locations)
{
loc.Name = "Update";
}
IsBusy = false;
}
}
The Project: https://github.com/Crey443/CollectionViewUpdate
MainModel needs to implement INotifyPropertyChanged, and any properties you want to bind (ie, Name) need to call PropertyChanged in their setters
public class MainModel
{
public string Name { get; set; }
public string Image { get; set; }
}
I am trying to populate listview with database table in xamarin forms app
I am getting null pointer exception
Below is XAML for listview
<ListView x:Name="_listView"
ItemsSource="{Binding itemsInList}"
Grid.Column="0"
Grid.Row="0"
SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" Grid.Column="0" Grid.Row="0" />
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Below is xaml.cs(code behind)
public List <ServiceProvider> itemlist;
public List <ServiceProvider> itemsInList
{
get {return itemlist;}
}
protected override void OnAppearing()
{
base.OnAppearing();
ExpensesDatabase dbcon = new ExpensesDatabase(completePath);
itemlist = dbcon.GetItems(completePath);
// _listView.ItemsSource = itemlist;
}
Below is db file
public class ExpensesDatabase
{
readonly SQLiteConnection database;
public ExpensesDatabase(string dbPath)
{
database = new SQLiteConnection(dbPath);
database.CreateTable < ServiceProvider > ();
}
public List < ServiceProvider > GetItems(string dbPath)
{
return database.Table < ServiceProvider > ().ToList();
}
}
Data is not displayed in listview
If you want the ListView to automatically update as items are added, removed and changed in the underlying list, you'll need to use an ObservableCollection. ObservableCollection is defined in System.Collections.ObjectModel and is just like List, except that it can notify ListView of any changes:
public ObservableCollection<ServiceProvider> itemsInList { get; set; }
Then make sure you have set the right bindingContext and initialized the ObservableCollection:
public MainPage()
{
InitializeComponent();
itemsInList = new ObservableCollection<ServiceProvider>();
BindingContext = this;
}
I write a sample to test and it works on my side, you can have a look at the full code:
public partial class MainPage : ContentPage
{
public ObservableCollection<ServiceProvider> itemsInList { get; set; }
public MainPage()
{
InitializeComponent();
itemsInList = new ObservableCollection<ServiceProvider>();
BindingContext = this;
}
protected override void OnAppearing()
{
base.OnAppearing();
itemsInList.Add(new ServiceProvider() { Name= "a"});
}
}
public class ServiceProvider : INotifyPropertyChanged
{
string name;
public event PropertyChangedEventHandler PropertyChanged;
public ServiceProvider()
{
}
public String Name
{
set
{
if (name != value)
{
name = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
get
{
return name;
}
}
}
Feel free to ask me any question if you still can't solve it.
I'm new to Xamarin Forms, and I've met my first challenge. I want a Frame around my Stacklayout within a Listview. When the user selects an item in the Listview I want some controls to appear. This works fine without the Frame, but the Frame does not expand when the controls appear. How can I change or get around this behavior?
Code below.
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MVVMTest"
x:Class="MVVMTest.MainPage">
<StackLayout>
<ListView HasUnevenRows="True" SelectedItem="{Binding SelectedViewItem, Mode=TwoWay}" ItemsSource="{Binding Items}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame BackgroundColor="White" BorderColor="Black">
<StackLayout>
<Label Text="{Binding Name}"></Label>
<Entry Text="{Binding Details}" IsVisible="{Binding ShowDetails}"></Entry>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
XAML.cs
namespace MVVMTest
{
public partial class MainPage : ContentPage
{
public MainPage()
{
BindingContext = new ViewModel()
{
Items = new List<ViewModelItem>()
{
new ViewModelItem()
{
Name = "Test",
Details = "details"
},
new ViewModelItem()
{
Name = "Test2",
Details = "details2"
}
}
};
InitializeComponent();
}
}
}
Model:
namespace MVVMTest
{
public class ViewModel : INotifyPropertyChanged
{
private ViewModelItem _selectedViewItem;
private List<ViewModelItem> _items;
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ViewModelItem SelectedViewItem
{
get
{
return _selectedViewItem;
}
set
{
_selectedViewItem = value;
OnPropertyChanged();
if (value != null)
{
value.ShowDetails = !value.ShowDetails;
SelectedViewItem = null;
}
}
}
public List<ViewModelItem> Items
{
get
{
return _items;
}
set
{
_items = value;
OnPropertyChanged();
}
}
public ViewModel()
{
}
}
public class ViewModelItem : INotifyPropertyChanged
{
private bool _showDetails;
private string _details;
private string _name;
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged();
}
}
public bool ShowDetails
{
get
{
return _showDetails;
}
set
{
_showDetails = value;
OnPropertyChanged();
}
}
public string Details
{
get
{
return _details;
}
set
{
_details = value;
OnPropertyChanged();
}
}
}
}
I ended up using the PropertyChanged event to react to when the ListView was displayed or hidden. In the eventhandler, I set the HeightRequest of the Frame and this forces it to resize itself.
Alternative solution/help can be found here:
https://forums.xamarin.com/discussion/comment/366577
XAML:
<StackLayout>
<ListView HasUnevenRows="True" SelectedItem="{Binding SelectedViewItem, Mode=TwoWay}" ItemsSource="{Binding Items}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame BackgroundColor="White" BorderColor="Black" Margin="2" Padding="2" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<StackLayout>
<Label Text="{Binding Name}"></Label>
<ListView HasUnevenRows="True" Margin="2" ItemsSource="{Binding DetailObjects}" IsVisible="{Binding ShowDetails}" PropertyChanged="ListView_PropertyChanged">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Entry Text="{Binding Details}"></Entry>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Code behind:
public partial class MainPage : ContentPage
{
public MainPage()
{
BindingContext = new ViewModel()
{
Items = new List<ViewModelItem>()
{
new ViewModelItem()
{
Name = "Test",
DetailObjects = new List<ViewModelItemDetails>
{
new ViewModelItemDetails
{
Details = "details1"
},
new ViewModelItemDetails
{
Details = "details2"
}
}
},
new ViewModelItem()
{
Name = "Test2",
DetailObjects = new List<ViewModelItemDetails>
{
new ViewModelItemDetails
{
Details = "details1"
},
new ViewModelItemDetails
{
Details = "details2"
}
}
}
}
};
InitializeComponent();
}
private void ListView_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (!(sender is ListView list)) return;
if (e.PropertyName == ListView.IsVisibleProperty.PropertyName)
{
Element parent = list;
Frame frame = null;
while (frame == null && parent != null)
{
if (parent is Frame) frame = parent as Frame;
parent = parent.Parent;
}
if (list.IsVisible)
{
list.HeightRequest = list.ItemsSource.Cast<ViewModelItemDetails>().Count() * 50;
if (frame != null) frame.HeightRequest = list.HeightRequest + 50;
}
else
{
if (frame != null) frame.HeightRequest = 50;
}
}
}
}
This is the code I have in my custom UserControl:
generic.xaml:
<UserControl x:Class="SoundControl.SoundClass"
x:Name="Uc"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel HorizontalAlignment="Stretch">
<Grid x:Name="mygrid"
Background="Transparent"
Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="70*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="350*" />
<ColumnDefinition Width="70*" />
</Grid.ColumnDefinitions>
<Button x:Name="SoundButton"
Content="{Binding MyName, ElementName=Uc}"
Grid.Column="0"
Grid.Row="0"
Click="RingtoneButton_Click" />
<Button x:Name="RingtoneButton"
Grid.Column="1"
Grid.Row="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Click="RingtoneButton_Click">
<Image Source="/Images/note.png"
Stretch="Fill"
Height="30"
Width="30" />
</Button>
<MediaElement x:Name="SoundContainer"
Source="{Binding MySound, ElementName=Uc}"
AutoPlay="False" />
</Grid>
</StackPanel>
</UserControl>
SoundControl.cs
namespace SoundControl
{
public partial class SoundClass : UserControl
{
SaveRingtoneTask saveRingtoneChooser;
public SoundClass()
{
// For ringtone
saveRingtoneChooser = new SaveRingtoneTask();
saveRingtoneChooser.Completed += new EventHandler<TaskEventArgs>(saveRingtoneChooser_Completed);
}
public static readonly DependencyProperty SoundSourceProperty =
DependencyProperty.Register("MySound", typeof(MediaElement), typeof(SoundClass), null);
public static readonly DependencyProperty SoundNameProperty =
DependencyProperty.Register("MyName", typeof(string), typeof(SoundClass), null);
public MediaElement MySound
{
get { return (MediaElement)this.GetValue(SoundSourceProperty); }
set { base.SetValue(SoundSourceProperty, value); }
}
public string MyName
{
get { return (string)this.GetValue(SoundNameProperty); }
set { base.SetValue(SoundNameProperty, value); }
}
public SoundClass()
{
InitializeComponent();
}
private void SoundButton_Click(object sender, RoutedEventArgs e)
{
SoundContainer.Play();
}
private void RingtoneButton_Click(object sender, RoutedEventArgs e)
{
saveRingtoneChooser.Source = new Uri(SoundContainer.Source.AbsoluteUri);
saveRingtoneChooser.DisplayName = MyName;
saveRingtoneChooser.Show();
}
void saveRingtoneChooser_Completed(object sender, TaskEventArgs e)
{
switch (e.TaskResult)
{
//Logic for when the ringtone was saved successfully
case TaskResult.OK:
MessageBox.Show("Ringtone saved.");
break;
//Logic for when the task was cancelled by the user
case TaskResult.Cancel:
MessageBox.Show("Save cancelled.");
break;
//Logic for when the ringtone could not be saved
case TaskResult.None:
MessageBox.Show("Ringtone could not be saved.");
break;
}
}
}
}
and my MainPage.xaml
<SoundControl:SoundClass
HorizontalAlignment="Left"
Grid.Row="0"
VerticalAlignment="Top"
Grid.ColumnSpan="2"
Width="456"
MyName="TEST123"
MySound="/project/test.mp3"
/>
The problem is the XAML in Mainpage. The MySound property is giving me the error
The TypeConverter for "MediaElement" does not support converting from a string.
Any help you can provide will be greatly appreciated!
The Source property of the MediaElement type takes a Uri, not another MediaElement object. Change the type of the dependency property MySound to Uri, not MediaElement. (Don't forget also to replace typeof(MediaElement) with typeof(Uri) within SoundSourceProperty.)
Incidentally, I'd also recommend renaming the two dependency property identifiers you have (SoundSourceProperty and SoundNameProperty) to MySoundProperty and MyNameProperty respectively. There is a convention that the name of the dependency property's identifier (i.e. the public static readonly field) is the name of the property that uses it (in your case MySound or MyName) followed by Property. Some tools will expect this convention to be followed.