Bind string[] to XAML using TwoWay - c#

I've a Model and I receive a property called birth in this format
birthday, birthplace
I've set this inside the Model
private string _birth;
/// <summary>
/// Birth information in format: birthday, birthplace
/// </summary>
[Column("birth")]
[JsonProperty("birth")]
public string Birth
{
get { return this._birth; }
set
{
this._birth= value;
OnPropertyChanged();
OnPropertyChanged("BirthData");
}
}
Also I've set a public modifier like this
/// <summary>
/// Split the birth data if exists
/// </summary>
public string[] BirthData
{
get { return this.Birth?.Split(','); }
}
In my XAML file I've setup everything correctly for my Model, except that I can't understand how I can use TwoWay binding on something like this. Below you will find the extract of the XAML for this particular property
<Label x:Name="BirthdayLabel" Content="Nato Il" />
<DatePicker x:Name="BirthdayDateTimePicker" SelectedDate="{Binding selectedModel.BirthData[0], Mode=TwoWay}"></DatePicker>
<Label x:Name="BirthplaceLabel" Content="Nato A"/>
<TextBox x:Name="BirthplaceTextBox" Text="{Binding selectedModel.BirthData[1], Mode=TwoWay}"/>
Of course this is not working properly because I'll ended up with selectedModel using the old information about the property even though the binding works as expected, I think that the TwoWay binding doesn't work with an Array of data like that.
I cannot change the DataSource and I've to find a method to use one textbox and one date picker and resemble together inside my ViewModel, which doesn't have much except a single method called Update() which take the current selectedModel.

ViewModel is about preparing the data for the View
So I suggest that you split and parse the data in the View Model and expose two properties for BirthDate and BirthPlace :
class Person
{
private string _birth;
public string Birth{
get { return this._birth; }
set
{
this._birth = value;
SplitBirthIntoBirthDayAndBirthPlace();
OnPropertyChanged();
}
}
private DateTime _birthday;
public DateTime Birthday{
get { return _birthday; }
set
{
_birthday = value;
ReComputeBirth();
OnPropertyChanged();
}
}
// Same for Birthplace ...
private void ReComputeBirth(){
// ... Format the data as expected ...
_birth = _birthday + ", " + _birthplace;
}
private void SplitBirthIntoBirthDayAndBirthPlace()
{
String[] values = _birth.Split(',', ' ');
// ... really make the parse here to fill _birthplace and _birthdate...
}
// ....
}
And the binding is simpler:
<DatePicker x:Name="BirthdayDateTimePicker"
SelectedDate="{Binding selectedModel.Birthday, Mode=TwoWay}"/>
<TextBox x:Name="BirthplaceTextBox"
Text="{Binding selectedModel.Birthplace, Mode=TwoWay}"/>

Related

Binding DatePicker to string property of class Model in C# Xamarin.Forms

I am trying to set DatePicker default date to some current date and bind DateSelected to string property of my object. I get error like this:
Unable to cast object of type 'Xamarin.Forms.Xaml.ElementNode' to type 'Xamarin.Forms.Xaml.ValueNode'
I store dates in SQLite as strings. Here is the Model class part:
public string Arrivo { get; set; }
public string Partenza { get; set; }
This is the xaml part of the view:
<DatePicker x:Name="pickArrivalDate" DateSelected="{Binding Arrivo}"/>
<DatePicker x:Name="pickDepartureDate" DateSelected="{Binding Partenza}"/>
This is the ViewModel:
public string Arrivo
{
get => _tavolo.Arrivo;
set
{
_tavolo.Arrivo = value;
NotifyPropertyChanged("Arrivo");
}
}
public string Partenza
{
get => _tavolo.Partenza;
set
{
_tavolo.Partenza = value;
NotifyPropertyChanged("Partenza");
}
}
I guess I should do the conversion but not sure how to achieve this.
Any ideas?
You have to set Date property because DateSelected is an event:
<DatePicker x:Name="pickArrivalDate" Date="{Binding Arrivo}"/>
<DatePicker x:Name="pickDepartureDate" Date="{Binding Partenza}"/>
Note: Properties Arrivo and Partenza, have to be DateTime, so if they are string, you have to use a ValueConverter.

XAML GridView, ObservableCollection and changing an item's properties

I have a class, derived from BindableBase, which contains two properties:
private string sourceCallNumber;
public string SourceCallNumber
{
get { return sourceCallNumber; }
set { SetProperty(ref sourceCallNumber, value); }
}
private string sourceMediaType;
public string SourceMediaType
{
get { return sourceMediaType; }
set { SetProperty(ref sourceMediaType, value); }
}
I have an ObservableCollection that contains a number of items using that class.
I have a GridView for which I set the ItemsSource to point to the ObservableCollection.
My problem is that if I change the value of, say, SourceMediaType on one of the items, the display does not update. I have put debugging in and can confirm that changing the value causes OnPropertyChanged to fire for that property.
I've read quite a few SO questions and answers around similar problems and I'm getting quite confused as to what I need to do in order to get this to work.
My understanding was that although ObservableCollection itself doesn't do anything when a property is changed, if the item itself triggers an OnPropertyChanged, that should get the display to update.
(There was one answer I read that proposed the use of code provided called TrulyObservableCollection but the problem I've got there is that everything refreshes rather than just the one item that has been updated).
What am I missing or misunderstanding, please?
Thanks.
C# apps should implement INotifyCollectionChanged and System.Collections.IList (not IList Of T).
public class NameList : ObservableCollection<PersonName>
{
public NameList() : base()
{
Add(new PersonName("Willa", "Cather"));
Add(new PersonName("Isak", "Dinesen"));
Add(new PersonName("Victor", "Hugo"));
Add(new PersonName("Jules", "Verne"));
}
}
public class PersonName
{
private string firstName;
private string lastName;
public PersonName(string first, string last)
{
firstName = first;
lastName = last;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
Look at GridView.
#RodrigoSilva put me on the correct path ... the XAML that references the values was this:
<StackPanel>
<TextBlock Text="{Binding DisplayCallNumber}" Style="{StaticResource TitleTextBlockStyle}" Visibility="{Binding GotCallNumber, Converter={StaticResource DisplayIfTrue}}" Margin="0,0,0,10"/>
<TextBlock Text="{Binding DisplayMediaType}" Style="{StaticResource ItemTextStyle}" Visibility="{Binding GotMediaType, Converter={StaticResource DisplayIfTrue}}" Margin="0,0,0,10"/>
</StackPanel>
which doesn't directly reference the underlying properties SourceCallNumber and SourceMediaType. As a result, although OnPropertyChanged is correctly firing for SourceCallNumber and SourceMediaType, that isn't causing the display to update because that isn't what the XAML is pointing at.
Explicitly changing the call to SetProperty to this:
SetProperty(ref sourceCallNumber, value, "DisplayCallNumber");
fixes the problem but is not a GOOD fix because some other part of the app may actually be binding to SourceCallNumber and won't get a property update after this change. The GOOD fix is to use a converter as explained in http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh464965.aspx.

Binding to properties does not update the related property

I have on the server side class Person with some properties, one of them is PhoneNumber (this is edmx auto generated - from DB). In the UI I have 2 textboxes, one is prefix, and the other is postfix, so I wanted to extend Person class and make it partial with 2 more properties, prefix and postfix and in the UI bind them to this properties which will update the PhoneNumber property, but somehow, prefix and postfix has value but PhoneNumber does not.
Here is my code:
Server Side:
Public partial class Person : EntityObject
{
//Auto generated from edmx
...
...
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
public global::System.String PhoneNumber
{
get
{
return _PhoneNumber;
}
set
{
OnPhoneNumberChanging(value);
ReportPropertyChanging("PhoneNumber");
_PhoneNumber = StructuralObject.SetValidValue(value, true, "PhoneNumber");
ReportPropertyChanged("PhoneNumber");
OnPhoneNumberChanged();
}
}
}
public partial class Person
{
[DataMember]
public string PhoneNumberPrefix
{
get { return PhoneNumber.Substring(0, 3); }
set { PhoneNumber = value + PhoneNumber.Substring(3); }
}
[DataMember]
public string PhoneNumberPostfix
{
get { return PhoneNumber.Substring(3); }
set { PhoneNumber = PhoneNumber.Substring(0, 3) + value; }
}
}
Client Side:
DataContext is the Person.
<TextBox Grid.Column="0" MaxLength="3"
Text="{Binding PhoneNumberPrefix, Mode=TwoWay}" />
<TextBlock Grid.Column="1" Text="-" />
<TextBox Grid.Column="2"
Text="{Binding PhoneNumberPostfix, Mode=TwoWay}" />
Final Result:
After I add values to those textboxes I get the values in Prefix and Postfix but the PhoneNumber is still null. Print screen in debug mode:
I have tried to add UpdateSourceTrigger or Implement INotifyPropertyChanged and no help, can someone help me please?
Thanks in advance !
Instead of PhoneNumber.Substring(0, 3) try this:
PhoneNumber != null ? PhoneNumber.Substring(0, 3) : string.Empty;
Or ask manually if(PhoneNumber != null)...

Windows Phone 8 Map, Databinding Map modes only works one time

I have a model with properties:
/// <summary>
/// List of available map modes
/// </summary>
public Array MapModes { get; private set; }
/// <summary>
/// The current cartographic mode of the map
/// </summary>
public MapCartographicMode MapMode
{
get { return _mapMode; }
set
{
if (value == _mapMode) return;
_mapMode = value;
OnPropertyChanged();
}
}
/// <summary>
/// List of available map color modes
/// </summary>
public Array MapColorModes { get; private set; }
//The current color mode of the map
public MapColorMode MapColorMode
{
get { return _mapColorMode; }
set
{
if (value == _mapColorMode) return;
_mapColorMode = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
And My XAML looks like this:
<maps:Map x:Name="MainMap"
Height="{Binding MapHeight}"
ColorMode="{Binding MapColorMode, Converter={StaticResource MapTestConverter}}">
The properties are updated on another page.
<toolkit:ListPicker Header="Map mode"
ItemsSource="{Binding MapModes}"
SelectedItem="{Binding Path=MapMode, Mode=TwoWay}"/>
<toolkit:ListPicker Header="Map color mode"
ItemsSource="{Binding MapColorModes}"
SelectedItem="{Binding Path=MapColorMode, Mode=TwoWay}"/>
Now the binding of the ListPickers works fine the value in the model always represents what was last picked here.
The Map binding also works, it gets the initial value and also updates the first time I change a property.
BUT that's it. after the first property change it refuses to update. (The dummy IValueConverter isn't called).
The model still nicely raises Property changed events, and the property has the correct value in the model (manually assigning it for example at page load works flawlessly)
Since it seems the Binding is getting "broken" I tried recreating it each time the property was updated
Binding b = new Binding("MapMode");
BindingOperations.SetBinding(MainMap, Map.CartographicModeProperty, b);
This works. I am beginning to think there is a bug or something in the wp8 map implementation. (Or I may just miss something completely obvious^^)
I had the same.
The solution is to provide Mode=TwoWay to the bindings. I have no idea why that works, but it seems.
On this blog I have seen exactly this solution:
http://dotnetbyexample.blogspot.ch/2012/10/introducing-new-windows-8-map-control.html

User selects something in combobox, something else is sent to dependency property

In my application the user can select how a date is displayed. Most of the standard datetime format strings can be selected. My problem now is that the average user doesnt understand the difference between "m" and "D". What I want to do is change this so that, as excel does, rather than showing the format string, I show how an arbitrary date would look like using that format.
The WPF combobox SelectedItem is bound to a dependency property in the date format picker clas, which the other control containing this date picker also binds to.
Example:
The user selects "January, 15" so the dependency property is set to "m".
Through code behind, the value of the dependency property is set to "D", the combobox is updated to display "Thursday, 15 January 1970" as the selected item.
I tried using converters but ConvertBack was impossible since I cant extract a format string used to create a certain date.
You could create a class DateFormatChoice that contains a property for the format code (e.g., "m" or "D") and a property for the current date formatted in that way.
public class DateFormatChoice {
public string FormatCode { get; private set; }
public string CurrentDateExample {
get { return DateTime.Now.ToString( FormatCode ) }
}
public DateFormatChoice( string standardcode ) {
FormatCode = standardcode;
}
}
You bind your ComboBox to a collection of these using CurrentDateExample in either your DataTemplate or as the ComboBox's DisplayMemberPath. You can either use these objects directly with your date format picker class and the DatePicker binds to the FormatCode property of the chosen DateFormatChoice object, or you can set the ValueMemberPath property on the original ComboBox to the FormatCode property and use SelectedValue on the ComboBox for getting/setting what is chosen. Not using ValueMember might be a little easier.
Here's a more full example. It uses the DateFormatChoice class above.
First, a data collection.
public class DateFormatChoices : List<DateFormatChoice> {
public DateFormatChoices() {
this.Add( new DateFormatChoice( "m" ) );
this.Add( new DateFormatChoice( "d" ) );
this.Add( new DateFormatChoice( "D" ) );
}
}
Then I made simple ViewModel for the Window:
public class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged = ( s, e ) => {
}; // the lambda ensures PropertyChanged is never null
public DateFormatChoices Choices {
get;
private set;
}
DateFormatChoice _chosen;
public DateFormatChoice Chosen {
get {
return _chosen;
}
set {
_chosen = value;
Notify( PropertyChanged, () => Chosen );
}
}
public DateTime CurrentDateTime {
get {
return DateTime.Now;
}
}
public ViewModel() {
Choices = new DateFormatChoices();
}
// expression used to avoid string literals
private void Notify<T>( PropertyChangedEventHandler handler, Expression<Func<T>> expression ) {
var memberexpression = expression.Body as MemberExpression;
handler( this, new PropertyChangedEventArgs( memberexpression.Member.Name ) );
}
}
I didn't have a date picker control that accepted the standard string format codes, so I made a quite dumb UserControl (with many corners cut) just to demonstrate its receipt of the format code. I gave it a dependency property called DateFormatProperty of type string and specified a value changed callback in the UIPropertyMetadata.
<Grid>
<TextBlock Name="datedisplayer" />
</Grid>
The callback:
private static void DateFormatChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e ) {
var uc = obj as UserControl1;
string code;
if ( null != ( code = e.NewValue as string ) ) {
uc.datedisplayer.Text = DateTime.Now.ToString( code );
}
}
And this is how I tied it all together in the Window.
<StackPanel>
<StackPanel.DataContext>
<local:ViewModel />
</StackPanel.DataContext>
<ComboBox
ItemsSource="{Binding Choices}" DisplayMemberPath="CurrentDateExample"
SelectedItem="{Binding Chosen, Mode=TwoWay}"/>
<local:UserControl1
DateFormatProperty="{Binding Chosen.FormatCode}" />
</StackPanel>

Categories

Resources