I have a Static Resource that provides for a list of genders to be used as a DataSource to the ComboBox. When the combo box renders, there is no text/content in any of the ComboBoxItem's.
public class Gender
{
public string code;
public string description;
}
public class LookupTables
{
public List<Gender> GenderList
{
get
{
List<Gender> g = new List<Gender>();
g.Add(new Gender{code="M",description="Male"});
g.Add(new Gender{code = "F", description = "Female" });
return g;
}
}
}
This is the App.xaml entry making this a Applicaton wide static resource
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Assets/Styles.xaml"/>
<ResourceDictionary>
<res:ResourceWrapper x:Key="ResourceWrapper" />
<app:NotOperatorValueConverter x:Key="NotOperatorValueConverter" />
<hlpr:LookupTables x:Key="LookupTables"/>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Here is the ComboBox Snippet that used this Static Resource
<ComboBox Height="23" Name="comboBox1" Width="120"
ItemsSource="{Binding Source={StaticResource LookupTables},Path=GenderList}"
SelectionChanged="comboBox1_SelectionChanged"
DisplayMemberPath="description"/>
I figured it out. It's because the Gender class need to have corresponding properties setters
public class Gender
{
public string code{get;set;}
public string description{get;set;}
}
Related
I am trying to create a contentview which has a listview xamarinforms.
I want to set a property of the contentview that is then used to bind the data to the listview.
Unfortunately I am not able to populate it.
I broke down the example to get a poc. The desired result should be a contentpage with all the names.
Any helb appreciated. Thx in advance!
The example consists of:
Contentpage:
Adds the contentview.
It also has a bindingcontext to a viewmodel - CompanyVM.
Sets Property PersonList of contentview to PersonVM.Personlist. (Unsure if correct)
Contentview.XAML:
XAML of contentview
Bindings for listview (unsure if correct)
Contentview.cs
XAML code-behind
Property Settings for contentview
CompanyVM:
Viewmodel used
Company & Person & Mockup
Simple classed to work with
Example
ContentMainPage
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cv="clr-namespace:ContentViewExample.XAML"
xmlns:vm="clr-namespace:ContentViewExample.ViewModel"
xmlns:local="clr-namespace:ContentViewExample"
x:Class="ContentViewExample.MainPage"
x:Name="mainpage">
<ContentPage.BindingContext>
<vm:CompanyVM/>
</ContentPage.BindingContext>
<StackLayout>
<cv:personlistCV Company="{x:Reference mainpage }"/>
<!--Is this correct?-->
</StackLayout>
</ContentPage>
```
Contentview.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"
x:Class="ContentViewExample.XAML.personlistCV"
x:Name="cvPersons">
<ContentView.Content>
<StackLayout>
<ListView x:Name="lstPerson"
ItemsSource="{Binding Company.Persons}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Path=Name}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentView.Content>
</ContentView>
Contentview.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ContentViewExample.XAML
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class personlistCV : ContentView
{
public personlistCV ()
{
InitializeComponent ();
}
//CompanyProperty
public static readonly BindableProperty CompanyProperty =
BindableProperty.Create(
"Company",
typeof(CompanyVM),
typeof(personlistCV),
null);
public personlistCV Company
{
set { SetValue(CompanyProperty, value); }
get { return (personlistCV)GetValue(CompanyProperty); }
}
}
}
CompanyVM
namespace ContentViewExample.ViewModel
{
public class CompanyVM: ViewModelBase
{
ObservableCollection<Person> persons;
string companyname;
public CompanyVM()
{
companyname = "Test Company";
persons = new ObservableCollection<Person>();
foreach (Person item in MockData.GetPeople())
persons.Add(item);
}
public string Company
{
set { SetProperty(ref companyname, value); }
get { return companyname; }
}
public ObservableCollection<Person> Persons
{
set { SetProperty(ref persons, value); }
get { return persons; }
}
}
}
Company & Person
public class Company
{
public string Name { get; set; }
}
public class Person
{
public string Name{get;set;}
}
public static class MockData
{
public static List<Person> GetPeople()
{
List<Person> tmp = new List<Person>
{
new Person
{
Name="Ted"
},
new Person
{
Name="Jennifer"
},
new Person
{
Name="Andy"
},
new Person
{
Name="Oscar"
}
};
return tmp;
}
}
You have tried to bind personlistCV.Company the following way
<StackLayout>
<cv:personlistCV Company="{x:Reference mainpage }"/>
<!--Is this correct?-->
</StackLayout>
I see several issues here:
Bindings are set with the XAML extension Binding
The Company is set to the mainpage, which is of Type MainPage.
It should rather be set to mainpage.BindingContext (this is a CompanyCV object)
Furthermore the personlistCV.Company is of type personlistCV, which does not really make sense. The field should be of type CompanyVM, since we'd like to bind the viewmodel (and personlistCV does not even have a Persons (bindable) property).
Having said that, the following code should work:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cv="clr-namespace:ContentViewExample.XAML"
xmlns:vm="clr-namespace:ContentViewExample.ViewModel"
xmlns:local="clr-namespace:ContentViewExample"
x:Class="ContentViewExample.MainPage"
x:Name="mainpage">
<ContentPage.BindingContext>
<vm:CompanyVM/>
</ContentPage.BindingContext>
<StackLayout>
<cv:personlistCV Company="{Binding Path=BindingContext, Source={x:Reference mainpage}}"/> <!-- Bind the element to `mainpage.BindingContext` -->
</StackLayout>
</ContentPage>
Maybe
<cv:personlistCV Company="{Binding .}"/>
could work, too, since . usually binds to the BindingContext of the view and the BindingContext of the page is passed down to the views (unless another BindingContext is set for the views explicitly).
And for the companyCV
public partial class personlistCV : ContentView
{
public personlistCV ()
{
InitializeComponent ();
}
//CompanyProperty
public static readonly BindableProperty CompanyProperty =
BindableProperty.Create(
"Company",
typeof(CompanyVM),
typeof(personlistCV),
null);
public CompanyVM Company
{
set { SetValue(CompanyProperty, value); }
get { return (personlistCV)GetValue(CompanyProperty); }
}
}
How I can add : with string at my label in xaml in xamarin.forms. I have a text coming from app resource file by (i18n:Translate Text=Supplier). Now with this text I also add : after this text. I don't want to add : in app resource with text. I want to do that it on xaml only. I tried with StringFormat but don't know how I can do it.
You can use a Value Converter to change the value on your Binding, but it's tricky because you can't easily add a converter while using i18n:Translate. But I still see three possible solutions to your problem:
1. Property without Value Converter
The easiest way would be to create a Property which gets the translated text and then adds a colon to your text:
ViewModel:
public string Supplier
{
get { return AppResources.Supplier + ":"; }
}
XAML:
<Label Text="{Binding Supplier}"/>
2. Property with Value converter
Another way is to create a property which gets the translated text and then add the colon via a Value Converter:
ViewModel:
public string Supplier
{
get { return AppResources.Supplier; }
}
Converter class:
public class ColonConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value += ":";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString().Remove(value.ToString().Length - 1);
}
}
XAML:
<ContentPage.Resources>
<ResourceDictionary>
<local:ColonConverter x:Key="ColonConverter" />
</ResourceDictionary>
</ContentPage.Resources>
...
<Label Text={Binding Supplier, Converter={StaticResource ColonConverter}}"/>
3. Create your own Translate Extension and Value Converter
I didn't test this, but I found this SO answer which gives an example on how to achieve that. This way you don't need to add properties to your ViewModel, so you only have to adjust your XAML once you set up the Translate Extension and Converter. But it's needs some work to write your own Translate Extension.
Custom TranslateExtension:
[ContentProperty("Text")]
public class TranslateExtension : IMarkupExtension
{
const string ResourceId = "Project.Resources.AppResources";
public string Text { get; set; }
public IValueConverter Converter { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Text == null)
return null;
ResourceManager resourceManager = new ResourceManager(ResourceId, typeof(TranslateExtension).GetTypeInfo().Assembly);
string translatedText = resourceManager.GetString(Text, CultureInfo.CurrentCulture);
if (this.Converter != null)
{
translatedText = Converter.Convert(translatedText, typeof(string), null, CultureInfo.CurrentCulture).ToString() ?? translatedText;
}
return translatedText;
}
}
XAML:
xmlns:strings="clr-namespace:Project.Utils;assembly=Project"
<ContentPage.Resources>
<ResourceDictionary>
<converters:ColonSpaceConverter x:Key="ColonSpaceConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<Label Text="{strings:Translate Money, Converter={StaticResource ColonSpaceConverter}}" />
This can be achieved in many ways, drafting two of them: (Value of Label will be changed on sliding the Slider)
Method 1
<Label Text="Slide to change Value"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"
x:Name="lblSliderValue" FontSize="Title" Margin="60"></Label>
<Slider ValueChanged="Slider_ValueChanged"></Slider>
And in CodeBehind file,
private void Slider_ValueChanged(object sender, ValueChangedEventArgs e)
{
lblSliderValue.Text = e.NewValue.ToString("0.00");
lblSliderValue.BackgroundColor = Color.Black;
lblSliderValue.TextColor = Color.White;
}
Method 2 (No need of code in CodeBehind file)
<Label Text="Slide to change Value"
VerticalOptions="Center"
HorizontalOptions="Center"
Text="{Binding Source={x:Reference sldExample}, Path=Value, StringFormat='{0:F2}'}"></Label>
<Slider x:Name="sldExample" BackgroundColor="Yellow" ThumbColor="Violet"></Slider>
I am new to Xamarin. I have a requirement where I have to implement a ListView or say tableView that have multiple different type-size cells.
And I also have to add Header for a particular section of cells, and some of my custom cells have a horizontal scroll in it.
I have done this thing in iOS native UITableView before, but don't know how this done in Xamarin cross platform, can anyone help me out this?
You are looking for DataTemplateSelector, which is very well documented in the official Xamarin.Forms documentation.
The basics are that you create your own DataTemplateSelector class:
public class MyDataTemplateSelector : DataTemplateSelector
{
}
In that class you override OnSelectTemplate:
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
}
By checking the type of the item argument, you should be able to figure out which template to return.
So lets say you have a ViewModel for Dog and one for Cat and want to show a different DataTemplate for each of those. You would do something like:
public class DogCatTemplateSelector : DataTemplateSelector
{
public DataTemplate DogTemplate { get; set; }
public DataTemplate CatTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
if (item is DogViewModel)
return DogTemplate;
return CatTemplate;
}
}
Then you can consume this in your XAML:
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="dogTemplate">
<ViewCell>
... <---- define your look of dog template here
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="catTemplate">
<ViewCell>
... <---- define your look of cat template here
</ViewCell>
</DataTemplate>
<local:DogCatTemplateSelector x:Key="dogCatTemplateSelector"
DogTemplate="{StaticResource dogTemplate}"
CatTemplate="{StaticResource catTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
Then simply set the ItemTemplate to your dogCatTemplateSelector instance you've defined in the resources on your ListView:
<ListView ItemsSource="{Binding DogsCatsCollection}" ItemTemplate="{StaticResource dogCatTemplateSelector}" />
Your ViewModel would then look something like:
public class Animal : INotifyPropertyChanged
{
}
public class DogViewModel : Animal
{
}
public class CatViewModel : Animal
{
}
public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Animal> DogsCatsCollection { get; }
= new ObservableCollection<Animal>();
}
Then you just populate DogsCatsCollection with instances of dogs and cats.
Is it possible to make a ListViewin a ListView and if yes is it even possible to bind data in both of them? I already tried it but it wont work the first List is shown as i want but the second is not shown.
The Code Looks kind of like this:
<ListView ItemSource="{Binding Item}" x:Name="ListA">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Day}"/>
<ListView x:Name="ListB" ItemSoruce="{Binding Item}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Time}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C#
listA.ItemsSource = timebooking.TimeBookingDataPeriod.TimeBookingData;
Hope sombody can help me :)
First of all, you should either go for a binding ItemSource={Binding Item} or setting the ItemSource in code listA.ItemsSource = ... as one overwrites the other. So I'll ignore the fact that you're trying to set it in code and go with the binding (which means you have a datacontext set on your view).
<ListView ItemSource="{Binding WeekDays}" x:Name="ListA">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Day}"/>
<ListView x:Name="ListB" ItemSource="{Binding TimesOfDay}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Time}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I've changed your first ItemSource to WeekDays and the second ItemSource to TimesOfDay for clarity. This to explain the way how binding and datacontexts work.
The outer ListView (ListA) inherits its DataContext from the outer items, most likely your page / usercontrol.
In your ListViewItem, the datacontext is changed to a single item in the WeekDays collection. => every property bound under this tree has to be in a WeekDay object.
The inner ListView (ListB) inherits its DataContext from the ListViewItem.
In the ListViewItem, the DataContext is changed to a single item in the TimesOfDay collection.
Every ListView is a new level of objects. So for my sample to work, you need following class structure, not that I'll simplify the code and just use properties (while you should correctly implement INotifyPropertyChanged for UI updates).
public class SampleViewModel : INotifyPropertyChanged
{
public ObservableCollection<WeekDay> WeekDays { get; set; } // first listview
}
public class WeekDay
{
public string Day { get; set; }
public ObservableCollection<TimeOfDay> TimesOfDay { get; set; } // second listview
}
public class TimeOfDay
{
public string Time { get; set; }
}
If you want to use 2 properties from the page's viewmodel, you'll have to use an element binding in ListB. But this is only for scenarios were the items in the inner listview are not correlated to the ones in the outer listview.
{Binding ElementName=ListA, Path=DataContext.ItemsForB}
ItemsSource not ItemSource
Here is an example:
public class ClassB
{
public string Name { get; set; }
public ObservableCollection<ClassA>classAList { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ClassB"/> class.
/// </summary>
public ClassB()
{
Name = String.Empty;
classAList = new ObservableCollection<ClassA>();
}
}
public class ClassA
{
public string Caption { get; set; }
}
Create list:
ObservableCollection<ClassB> list = new ObservableCollection<ClassB>();
Init list:
ClassB item1 = new ClassB() { Name = "listAItem1" };
item1.classAList.Add(new ClassA { Caption = "listBItem1" });
item1.classAList.Add(new ClassA { Caption = "listBItem2" });
item1.classAList.Add(new ClassA { Caption = "listBItem3" });
item1.classAList.Add(new ClassA { Caption = "listBItem4" });
item1.classAList.Add(new ClassA { Caption = "listBItem5" });
list.Add(item1);
ClassB item2 = new ClassB() { Name = "listAItem1" };
item2.classAList.Add(new ClassA { Caption = "listBItem1" });
item2.classAList.Add(new ClassA { Caption = "listBItem2" });
item2.classAList.Add(new ClassA { Caption = "listBItem3" });
item2.classAList.Add(new ClassA { Caption = "listBItem4" });
item2.classAList.Add(new ClassA { Caption = "listBItem5" });
list.Add(item1);
this.DataContext = list;
and XAML:
<ListView ItemsSource="{Binding}" x:Name="ListA">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="Day"/>
<ListView x:Name="ListB" ItemsSource="{Binding classAList}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Caption}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
It seems you want to create a grouped list, in this case is better to use gridview with groups:
Good step by step tutorial for grouped gridview
I am binding a model to my Xaml code and have a question about how to bind to a Property.
Let's assume my View Model looks like
internal class LogsVM
{
private List<Log> logList;
public List<Log> LogList
{
get; set;
}
public LogsVM()
{
}
public LogsVM(List<Logging.Log> logs)
{
logList = logs;
}
}
and assume my Log class looks like
internal class Log
{
public string Title { get;set; }
public List<MoreDetails> moreDetails;
public Log()
{
moreDetails= new List<MoreDetails>();
}
}
In Xaml, how do I bind to the Title within a TreeView?
My Xaml so far looks like
<Window x:Class="BackUps.Logging.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myData ="clr-namespace:BackUps.Logging.ViewModel"
Title="Logging Results" Height="350" Width="525">
<Grid>
<Grid.Resources>
<myData:LogsVM x:Key="Vm" />
</Grid.Resources>
<Grid.DataContext>
<Binding Source="{StaticResource Vm}"></Binding>
</Grid.DataContext>
<TreeView>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type myData:LogsVM}" ItemsSource="{Binding LogList}">
<TextBlock Text="{Binding Title}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type myData:LogsVM}">
<TextBlock Text="{Binding moreDetails.Staus}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
And my MainWindow code behind
public MainWindow(List<Log> logs)
{
InitializeComponent();
LogsVM logVm = new LogsVM(logs);
this.DataContext = logVm;
}
As you can see in the above code, I'm trying to bind the Title property but my screen doesn't display an text at all.
So, my 2 questions are:
Is it enough to use my ViewModel class alone or do I also need to tell the Xaml each internal class of the ViewModel (in this case, the Log class)? EG
xmlns:myData ="clr-namespace:BackUps.Logging.ViewModel"
xmlns:moreData = "clr-namespace:BackUps.Logging.Logs"
What do I need to do to bind the Title?
Binding is not complicated as you might think, Your are just not mastering the Treeview's HierarchicalDataTemplate stuff and exposing properties to the XAML,
set your all your domain classes public cause they are used in public properties.
myData should reference the domain classes namespace.for ex: in my case xmlns:myData="clr-namespace:WpfApplication3" MoreDetails has to be a public property in Log class.
<TreeView ItemsSource="{Binding LogList}" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type myData:Log}" ItemsSource="{Binding MoreDetails}">
<TextBlock Text="{Binding Title}" />
<HierarchicalDataTemplate.ItemTemplate >
<DataTemplate DataType="{x:Type myData:MoreDetails}" >
<TextBlock Text="{Binding Status}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
public class Log
{
public string Title { get; set; }
public List<MoreDetails> MoreDetails { get; set; }
public Log()
{
MoreDetails = new List<MoreDetails>();
}
}
public class MoreDetails
{
public string Status { get; set; }
}
public class YourVM
{
public YourVM() // in my case i've just run it fast in code behind
{
LogList = new List<Log>
{
new Log{Title = "Hichem", MoreDetails = new List<MoreDetails>{ new MoreDetails{Status = "OK"}}},
new Log{Title = "Hichem"},
new Log{Title = "Hichem"},
new Log{Title = "Hichem"},
};
}
public List<Log> LogList { get; set; }
}