XAML Xamarin 5 - MacOS
XAML:
<Picker x:Name="StylesPicker" Title="Select stylesheet" HorizontalOptions="FillAndExpand" SelectedIndexChanged="StylesPicker_SelectedIndexChanged" SelectedItem="{Binding Path=vrrData.Style, Mode=TwoWay}"/>
Code behind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AppKit;
using VisitsRota.ViewModels;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace VisitsRota.Views
{
public partial class ElderlyInfirmPage : ContentPage
{
public ObservableCollection<string> StylesList { get; set; }
public ElderlyInfirmPage()
{
InitializeComponent();
BindingContext = new ElderlyInfirmViewModel();
string[] stylesArray = Directory.GetFiles("/Users/Shared/VisitsRota.MacOS/Styles", "ElderlyInfirm-Schedule-*.xsl")
.Select(file => Path.GetFileName(file)).ToArray<string>();
StylesList = new ObservableCollection<string>(stylesArray);
StylesPicker.ItemsSource = StylesList;
}
async private void InstallStyleButton_Clicked(object sender, EventArgs e)
{
var customFileType =
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{DevicePlatform.macOS, new[] {"xsl"} }
});
var pickResult = await FilePicker.PickAsync(new PickOptions
{
FileTypes = customFileType,
PickerTitle = "Select template to install"
});
if(pickResult != null)
{
StylesList.Add(pickResult.FileName);
}
}
void StylesPicker_SelectedIndexChanged(System.Object sender, System.EventArgs e)
{
var picker = (Picker)sender;
int selectedIndex = picker.SelectedIndex;
if(selectedIndex != -1)
{
string x = (string)picker.ItemsSource[selectedIndex];
}
}
}
}
View Model:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
namespace VisitsRota.ViewModels
{
public class ElderlyInfirmViewModel : BaseViewModel
{
public List<string> StylesList { get; set; }
public List<string> ListMonths { get; }
public VisitsRotaData vrrData { get; set; }
public ElderlyInfirmViewModel()
{
vrrData = DeserializeFromXml<VisitsRotaData>("/Users/Shared/VisitsRota.MacOS/VisitsRotaData.xml");
// Should this list be private?
ListMonths = DateTimeFormatInfo.CurrentInfo.MonthNames.TakeWhile(m => m != String.Empty).ToList();
StylesList = Directory.GetFiles("/Users/Shared/VisitsRota.MacOS/Styles", "ElderlyInfirm-Schedule-*.xsl")
.Select(file => Path.GetFileName(file)).ToList<string>();
}
public T DeserializeFromXml<T>(string filePath)
{
try
{
if (!System.IO.File.Exists(filePath))
throw new ArgumentNullException(filePath + " not Exists");
using (System.IO.StreamReader reader = new System.IO.StreamReader(filePath))
{
System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
T ret = (T)xs.Deserialize(reader);
return ret;
}
}
catch (Exception ex)
{
return default(T);
}
}
}
[XmlType(TypeName="VisitsRotaData")]
public class VisitsRotaData : BaseViewModel
{
private ObservableCollection<string> _listelders;
private ObservableCollection<string> _listpublishers;
private string _style;
[XmlArray("Elders")]
[XmlArrayItem("Elder")]
public ObservableCollection<string> ListElders
{
get { return _listelders; }
set {
_listelders = value;
OnPropertyChanged("ListElders");
}
}
[XmlArray("Publishers")]
[XmlArrayItem("Publisher")]
public ObservableCollection<string> ListPublishers
{
get { return _listpublishers; }
set
{
_listpublishers = value;
OnPropertyChanged("ListPublishers");
}
}
[XmlElement]
public string Style
{
get { return _style; }
set
{
_style = value;
OnPropertyChanged("Style");
}
}
}
}
XML Data:
<?xml version="1.0" encoding="UTF-8" ?>
<VisitsRotaData>
<Elders>
<Elder>Elder 1</Elder>
<Elder>Elder 2</Elder>
<Elder>Elder 3</Elder>
<Elder>Elder 4</Elder>
<Elder>Elder 5</Elder>
<Elder>Elder 6</Elder>
<Elder>Elder 7</Elder>
<Elder>Elder 8</Elder>
</Elders>
<Publishers>
<Publisher>Publisher 1</Publisher>
<Publisher>Publisher 2</Publisher>
<Publisher>Publisher 3</Publisher>
<Publisher>Publisher 4</Publisher>
<Publisher>Publisher 5</Publisher>
<Publisher>Publisher 6</Publisher>
<Publisher>Publisher 7</Publisher>
<Publisher>Publisher 8</Publisher>
<Publisher>Publisher 9</Publisher>
<Publisher>Publisher 10</Publisher>
</Publishers>
<Style>ElderlyInfirm-Schedule-v2.xsl</Style>
</VisitsRotaData>
The Picker populates with with the files from the folder. That is fine. But when the window displays it has the first item from the picker selected and not the second (which is the value of Styles.
Expected behaviour:
Bind the value of the Picker to the Styles property. Picker defaults to the existing value.
It looks like I have worked it out. 😊 This answer here pointed me in the right direction.
I changed the Binding
in the XAML like this for the Picker
:
<Picker x:Name="StylesPicker" Title="Select stylesheet"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding StylesList}"
SelectedItem="{Binding Path=vrrData.Style, Mode=TwoWay}"/>
As you can see, I have also removed the StylesPicker_SelectedIndexChanged
handler.
I removed the public ObservableCollection<string> StylesList { get; set; }
from this class.
I pasted in the aforementioned StylesList
property and populated it in the ElderlyInfirmViewModel
constructor.
The Style
property remained unchanged in the VisitsRotaData
class.
Now, when my window displays, the Picker
defaults to the value from my XML file:
I think I was complicating it by having the ItemSource
in the code behind and the SelectedItem
in the view model. By keeping it all in the one place (the BindingContext of the Page) it works.