I have a simple entry in Xamarin Forms:
<Entry Text="{Binding Number, Mode=TwoWay}" Placeholder="Number" Keyboard="Numeric" />
In ViewModel there is property:
private int? _number;
public int? Number
{
get { return _number; }
set
{
if (SetProperty(ref _number, value))
{
OnPropertyChange(nameof(Number));
}
}
}
I enter number in Entry and press button, but in button clicked procedure - Number is still null. What am I doing wrong?
You can bind an int to an Entry but you can't bind a nullable int. You can either add another property which converts the number to a string, or you can easily create a value converter like this...
class NullableIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var nullable = value as int?;
var result = string.Empty;
if (nullable.HasValue)
{
result = nullable.Value.ToString();
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var stringValue = value as string;
int intValue;
int? result = null;
if (int.TryParse(stringValue, out intValue))
{
result = new Nullable<int>(intValue);
}
return result;
}
... and use it in your page like this...
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:IntBinding"
x:Class="IntBinding.DemoPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:NullableIntConverter x:Key="NullableIntConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Entry Text="{Binding Number, Mode=TwoWay, Converter={StaticResource NullableIntConverter}}" Placeholder="Number" Keyboard="Numeric" />
<Label Text="{Binding Number, Converter={StaticResource NullableIntConverter}}" />
</StackLayout>
</ContentPage>
Entry accept a string. If you would like to bind a int property you should use a IValueConverter but I think the best solution is to use a String property than convert the value from String to Int
public string StrNumber{
get {
if (Number == null)
return "";
else
return Number.ToString();
}
set {
try {
Number = int.Parse(value);
}
catch{
Number = null;
}
}
}
public string MP { get; set; }
public float MainPrice => (float.TryParse(MP, out float mp)) ? mp : 0;
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 would like to know how to add image to the behaviour, actually i am trying to write the email behaviour where i am capable of changing the entry background but along with that i would like to add an image for it.
My xaml:
<StackLayout>
<Entry Placeholder="Enter a System.Double">
<Entry.Behaviors>
<local:CustomBehavior />
</Entry.Behaviors>
</Entry>
</StackLayout>
My Behaviour class:
public class CustomBehavior: Behavior<Entry>
{
private const string digitRegex = #"^(?("")("".+?(?<!\\)""#)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])#))" +
#"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs e)
{
Entry entry;
bool isValid;
entry =(Entry)sender;
isValid = Regex.IsMatch(e.NewTextValue, digitRegex);
entry.BackgroundColor = isValid ? Color.Default : Color.Red;
}
}
1st. For your request you have to add a separate Image to your layout, in your case:
<StackLayout
Orientation="Horizontal">
<Entry Placeholder="Enter a System.Double">
<Entry.Behaviors>
<local:CustomBehavior x:Name="customValidator"/>
</Entry.Behaviors>
</Entry>
<Image
HeightRequest="24"
WidthRequest="24"
Aspect="AspectFit"
IsVisible="{Binding Source={x:Reference customValidator},
Path=IsVisible}"
Style="{Binding Source={x:Reference customValidator},
Path=IsValid,
Converter={StaticResource boolToStyleImage}}"/>
</StackLayout>
Pay attention to the x:Name="" attribute as that is necessary to be able to reference that custom behavior within this xaml file
2nd. Create BindableProperties on your 'CustomBehavior' for two fields on which you'll bind the status of your image for your entry
public class CustomBehavior: Behavior<Entry>
{
private const string digitRegex = #"^(?("")("".+?(?<!\\)""#)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])#))" +
#"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
static readonly BindablePropertyKey IsValidPropertyKey = BindableProperty.CreateReadOnly ("IsValid", typeof (bool), typeof (CustomBehavior), false);
public static readonly BindableProperty IsValidProperty = IsValidPropertyKey.BindableProperty;
static readonly BindablePropertyKey IsVisiblePropertyKey = BindableProperty.CreateReadOnly ("IsVisible", typeof (bool), typeof (CustomBehavior), false);
public static readonly BindableProperty IsVisibleProperty = IsVisiblePropertyKey.BindableProperty;
private const string FRIEND = "friend";
private const string FRIENDS = "friends";
public bool IsValid
{
get { return (bool)base.GetValue (IsValidProperty); }
private set { base.SetValue (IsValidPropertyKey, value); }
}
public bool IsVisible
{
get { return (bool)base.GetValue (IsVisibleProperty); }
private set { base.SetValue (IsVisiblePropertyKey, value); }
}
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs e)
{
if (e.NewTextValue.Length > 0)
{
IsVisible = true;
Entry entry =(Entry)sender;
IsValid = Regex.IsMatch(e.NewTextValue, digitRegex);
if(IsValid) // Check only if we have a valid email
{
// Here we validate if the email contains our requirements
String email = entry.Text;
int pos = email.IndexOf("#"); // Exclude the domain
string username = email.Substring(0, pos);
if(username.Contains(FRIEND) || username.Contains(FRIENDS))
{
IsValid = true;
}else
IsValid = false;
}
}
}else
IsVisible = false;
}
}
3d. Create a simple ValueConverter class which will convert a boolean value to one of our objects of type 'T' depending on what we'll append this converter to.
namespace YourApp
public class BooleanToObjectConverter<T> : IValueConverter
{
public T FalseObject { set; get; }
public T TrueObject { set; get; }
public object Convert (object value, Type targetType,
object parameter, CultureInfo culture)
{
return (bool)value ? this.TrueObject : this.FalseObject;
}
public object ConvertBack (object value, Type targetType,
object parameter, CultureInfo culture)
{
return ((T)value).Equals (this.TrueObject);
}
}
4th. Add this style to ResourceDictionary tag on your App.xaml file which will declare the TrueObject(Style for a valid email) and the FalseObject(Style for an invalid email).
Replace "your_wrong_image_here.png" and "your_correct_image_here.png" to your desired images
<Application
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="YourApp.App"
xmlns:statics="clr-namespace:YourApp">
<Application.Resources>
<!-- Application resource dictionary -->
<ResourceDictionary>
<statics:BooleanToObjectConverter x:Key="boolToStyleImage"
x:TypeArguments="Style">
<statics:BooleanToObjectConverter.FalseObject>
<Style TargetType="Image">
<Setter Property="HeightRequest" Value="24" />
<Setter Property="Source"
Value="your_wrong_image_here.png" />
</Style>
</statics:BooleanToObjectConverter.FalseObject>
<statics:BooleanToObjectConverter.TrueObject>
<Style TargetType="Image">
<Setter Property="HeightRequest" Value="24" />
<Setter Property="Source"
Value="your_correct_image_here.png" />
</Style>
</statics:BooleanToObjectConverter.TrueObject>
</statics:BooleanToObjectConverter>
</ResourceDictionary>
</Application.Resources>
</Application>
This should fulfill your needs, just be careful from copy-paste errors as you have different class names on your project!
i want to bind a list of images to a stackpanel which is inside a DataGrid.RowDetailsTemplate.
My class structure is as follows:
public class A
{
private List<MyImage> _images = new List<MyImage>();
public List<MyImage> Images { get; set; }
public string Name { get; set; }
public void AddImage(byte[] src) { ... }
}
public class MyImage
{
public BitmapImage Image { get; set; }
public byte[] RawData { get; set; }
}
In my main class i have a list of A:
public List<A> AList { get; set; }
dataGrid1.ItemsSource = AList;
dataGrid1.DataContext = AList;
All i want to do is to display the Name property of an element in a DataGridTextColumn and all images stored in the Images property in the RowDetails.
My xaml is:
<DataGrid name="dataGrid1">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Path=Name}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel DataContext="{Binding Path=Images}">
<Image Source="{Binding Path=RawData}"/>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
All i get to see is just one image although there are some more stored in Images. Any ideas?
Ok, so the solution of this problem was the use of ContentPresenter combined with a converter.
Now my XAML looks like this:
<DataGrid name="dataGrid1">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Path=Name}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Images, Converter={StaticResource ImageCollectionConverter}}"/>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
And the corresponding converter class:
public class ImageCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
List<MyImage> images = value as List<MyImage>;
if (images != null)
{
StackPanel stack = new StackPanel();
stack.Orientation = Orientation.Horizontal;
foreach (DesignImage img in images)
{
Image image = new Image();
image.Source = img.Image;
stack.Children.Add(image);
}
return stack;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Db class propertys
[Serializable]
[EnableClientAccess()]
public class DbPersonelJobDetail
{
public DbPersonelJobDetail()
{
}
[Key]
public Guid PersonelID { get; set; }
public Guid JobID { get; set; }
public string JobName { get; set; }
public string Adi { get; set; }
}
DomainServices Linq Query
public IQueryable<DTO.DbPersonelJobDetail> GetPersonelJobTreeList()
{
IQueryable<DTO.DbPersonelJobDetail> result = from p in ObjectContext.SPA_PersonelJobDetail
join c in ObjectContext.SPA_PersonelJob on p.PersonelJobID equals c.ID
select new DTO.DbPersonelJobDetail()
{
JobID=p.PersonelJobID,
JobName = c.JobName,
PersonelID=p.ID,
Adi=p.Adi
};
return result.AsQueryable();
}
BindTreeList methot
public void BindTreeList()
{
loadOP = context.Load(context.GetPersonelJobTreeListQuery(), false);
loadOP.Completed += loadOP_Completed;
}
void loadOP_Completed(object sender, EventArgs e)
{
treeListPersonel.ItemsSource = loadOP.Entities;
}
I'm Treeview of binding BindTreeList() methot.
The following, as in the picture. HierarchicalDataTemplate Itemsource binding howto?
Could you make an example?
I could not :(
Waiting for your ideas...
Pucture
Load first lavel nodes.
In HierarchicalDataTemplate bind ItemsSource to LoadChildsConverter
<riaControls:DomainDataSource x:Name="MyData" QueryName="GetFirstLavel"
AutoLoad="True" LoadSize="50">
<riaControls:DomainDataSource.DomainContext>
<web:AdvDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
<sdk:TreeView ItemsSource="{Binding}" DataContext="{Binding ElementName=MyData, Path=Data}">
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate
ItemsSource="{Binding Converter={StaticResource TreeViewCollectionConverter}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CODE}" />
<TextBlock Text="{Binding DESC}" />
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
TreeViewCollectionConverter.cs
public class TreeViewR5OBJECTCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ObservableCollection<Node> nodeList = new ObservableCollection<Node>();
if (value != null)
{
AdvDomainContext ctx = new AdvDomainContext();
Node parentNode = (Node)value;
ctx.Load(ctx.GetChildsQuery(parentNode), iop =>
{
foreach (var o in iop.Entities)
nodeList.Add(o);
}, null);
}
return nodeList;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
In AdvDomainService.cs
must have
public IQueryable<Node> GetFirstLavel()
to return first level nodes
and
public IQueryable<Node> GetChilds(Node ParentNode)
to return childs of ParentNode
So I have a simple RSS-reader, that has a feed that gets updated when the app is started. How can I add functionality that keeps the new unread items in a different color? I would like to make it visible for the user which posts are new since last time he/she opened the app.
Presuming you have a model something like;
public class RSSItem {
public bool IsUnread { get; set; }
public string Title { get; set; }
}
You'll want to bind the ForegroundColor of a TextBlock to your IsUnread property using a IValueConverter that takes a bool and returns a Color. So your XAML might look like;
<phone:PhoneApplicationPage.Resources>
<converters:UnreadForegroundConverter x:Key="UnreadForegroundConverter" />
</phone:PhoneApplicationPage.Resources>
<ListBox x:Name="RSSItems">
<DataTemplate>
<TextBlock Text="{Binding Title}" Foreground="{Binding IsUnread, Converter={StaticResource UnreadForegroundConverter}}" />
</DataTemplate>
</ListBox>
Don't forget to add the xmlns:converters attribute to your Page's tag.
You'll then want to implement your IValueConverter to do the boolean to colour conversion;
public class UnreadForegroundConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if ((bool)value == true) {
return Application.Current.Resources["PhoneAccentColor"];
}
return Application.Current.Resources["PhoneForegroundColor"];
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
And obviously you'll need to bind the listbox, RSSItems, to a collection of RSSItem. Eg.
ObservableCollection<RSSItem> items = new ObservableCollection<RSSItem>();
// populate items somehow
RSSItems.ItemsSource = items;
Hope that helps.