I have two pages that use the same form so I created a content view of form and apply bindable properties like this:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ListContact.Extension.EditForm">
<StackLayout Padding="20">
<Label Text="Edit Contact Forms"
HorizontalOptions="Center"
FontSize="20"
TextColor="Blue"
VerticalOptions="Start"/>
<Label Text="Name"/>
<Entry x:Name="txtName" Text="{Binding NameText}" Placeholder="Name"/>
<Label Text="Phone number"/>
<Entry x:Name="txtPhoneNumber" Text="{Binding PhoneNumberText}" Placeholder="Phone number" Keyboard="Telephone"/>
<Label Text="Email"/>
<Entry x:Name="txtEmail" Text="{Binding EmailText}" Placeholder="Email" Keyboard="Email"/>
<Label Text="Description"/>
<Editor x:Name="txtDescription" Text="{Binding DescriptionText}" Placeholder="Description"
HeightRequest="70"/>
</StackLayout>
</ContentView>
This is the code behind:
using ListContact.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ListContact.Extension
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class EditForm : ContentView
{
private static readonly BindableProperty NameProperty = BindableProperty.Create("NameText", typeof(object), typeof(EditForm));
public string NameText { get => (string)GetValue(NameProperty); set => SetValue(NameProperty, value); }
private static readonly BindableProperty PhoneProperty = BindableProperty.Create("PhoneNumberText", typeof(string), typeof(EditForm));
public string PhoneNumberText { get => (string)GetValue(PhoneProperty); set => SetValue(PhoneProperty, value); }
private static readonly BindableProperty EmailProperty = BindableProperty.Create("EmailText", typeof(string), typeof(EditForm));
public string EmailText { get => (string)GetValue(EmailProperty); set => SetValue(EmailProperty, value); }
private static readonly BindableProperty DescriptionProperty = BindableProperty.Create("DescriptionText", typeof(string), typeof(EditForm));
public string DescriptionText { get => (string)GetValue(DescriptionProperty); set => SetValue(DescriptionProperty, value); }
public EditForm()
{
InitializeComponent();
BindingContext = this;
}
}
}
And in the view I call back this form that I created before and bind data to the bindable property like below:
This is the xaml file:
<ContentPage xmlns:local="clr-namespace:ListContact.Extension"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ListContact.View.ListContactAddingForm">
<local:EditForm NameText="{Binding Name, Mode=TwoWay}" PhoneNumberText="egwebwev" EmailText="ưewevefqwf" DescriptionText="ewebe"/>
<ContentPage.Content>
<Button Text="Save"
HorizontalOptions="Center"
TextColor="White"
Command="{Binding AddContactCommand}"
BackgroundColor="#0A7CFF"/>
</ContentPage.Content>
</ContentPage>
And this is the code behind:
namespace ListContact.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ListContactAddingForm : ContentPage
{
private SQLiteAsyncConnection connection;
public ListContactAddingForm()
{
connection = new SQLiteAsyncConnection(BaseConnection.DatabasePath);
ViewModel = new ContactViewModel(new PageService());
InitializeComponent();
}
private ContactViewModel ViewModel
{
get => BindingContext as ContactViewModel;
set => BindingContext = value;
}
}
}
`
This is my view model:
namespace ListContact.ViewModel
{
public class ContactViewModel : BaseViewModel
{
public int Id { get; set; }
private string name;
public string Name
{
get => name;
set
{
SetValue(ref name, value);
}
}
private string description;
public string Description
{
get => description;
set
{
SetValue(ref description, value);
}
}
private string phoneNumber;
public string PhoneNumber
{
get => phoneNumber;
set
{
SetValue(ref phoneNumber, value);
}
}
private string email;
public string Email
{
get => email;
set
{
SetValue(ref email, value);
}
}
public ICommand AddContactCommand { get; private set; }
private IPageService pageService;
public object Alert { get; private set; }
public ContactViewModel(IPageService pageService)
{
this.pageService = pageService;
AddContactCommand = new Command(async () => await AddContacts());
}
private async Task AddContacts()
{
var newContact = new Contact()
{
Name = Name,
PhoneNumber = PhoneNumber,
Email = Email,
Description = Description
};
var result = await connection.InsertAsync(newContact);
if (result == 1)
await App.Current.MainPage.DisplayAlert("Successfully", "", "OK");
await pageService.PopAsycn();
}
}
}
But when I run this code I got the error:
No property, bindable property, or event found for "NameText", or mismatching type between value and property
My code was okay before I separated the form into another content view and call it back from the view and got this problem
So my questions are that:
Is the way that I create form and bindable property correct?
Could I bind data to the bindable property in the form? How to do it if it could?.
And how to fix the above error?
I use MVVM to build this code
Btw, sorry for my bad English
From Xamarin.Forms Bindable Properties, a bindable property can be created by declaring a public static readonly property of type BindableProperty.
public static readonly BindableProperty NameTextProperty = BindableProperty.Create
("NameText", typeof(string), typeof(EditForm),"",BindingMode.OneWay,propertyChanged:NamePropertychanged);
Then you need take care of BindableProperty Name, for example, PropertyNameProperty
public static readonly BindableProperty NameTextProperty = BindableProperty.Create
("NameText", typeof(string), typeof(EditForm),"",BindingMode.OneWay,propertyChanged:NamePropertychanged);
public string NameText
{
get => (string)GetValue(NameTextProperty);
set => SetValue(NameTextProperty, value);
}
Finally, you don't need to use Binding in EditForm, just using propertyChanged event to notify property value has changed.
private static void NamePropertychanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (EditForm)bindable;
control.txtName.Text = (string)newValue;
}
Do one sample that you can take a look:
EditForm xaml.
<ContentView.Content>
<StackLayout Padding="20">
<Label
FontSize="20"
HorizontalOptions="Center"
Text="Edit Contact Forms"
TextColor="Blue"
VerticalOptions="Start" />
<Label Text="Name" />
<Entry x:Name="txtName" Placeholder="Name" />
<Label Text="Phone number" />
<Entry
x:Name="txtPhoneNumber"
Keyboard="Telephone"
Placeholder="Phone number" />
<Label Text="Email" />
<Entry
x:Name="txtEmail"
Keyboard="Email"
Placeholder="Email" />
<Label Text="Description" />
<Editor
x:Name="txtDescription"
HeightRequest="70"
Placeholder="Description" />
</StackLayout>
</ContentView.Content>
public partial class EditForm : ContentView
{
public static readonly BindableProperty NameTextProperty = BindableProperty.Create
("NameText", typeof(string), typeof(EditForm),"",BindingMode.OneWay,propertyChanged:NamePropertychanged);
public string NameText
{
get => (string)GetValue(NameTextProperty);
set => SetValue(NameTextProperty, value);
}
public static readonly BindableProperty PhoneNumberTextProperty = BindableProperty.Create
("PhoneNumberText", typeof(string), typeof(EditForm),"",BindingMode.OneWay,propertyChanged:PhoneNumberchanged);
public string PhoneNumberText
{
get => (string)GetValue(PhoneNumberTextProperty);
set => SetValue(PhoneNumberTextProperty, value);
}
public static readonly BindableProperty EmailTextProperty = BindableProperty.Create
("EmailText", typeof(string), typeof(EditForm),"",BindingMode.OneWay,propertyChanged:Emailpropertychanged);
public string EmailText
{
get => (string)GetValue(EmailTextProperty);
set => SetValue(EmailTextProperty, value);
}
public static readonly BindableProperty DescriptionTextProperty = BindableProperty.Create
("DescriptionText", typeof(string), typeof(EditForm), "", BindingMode.OneWay, propertyChanged: Descriptionchanged);
public string DescriptionText
{
get => (string)GetValue(DescriptionTextProperty);
set => SetValue(DescriptionTextProperty, value);
}
private static void NamePropertychanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (EditForm)bindable;
control.txtName.Text = (string)newValue;
}
private static void PhoneNumberchanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (EditForm)bindable;
control.txtPhoneNumber.Text = (string)newValue;
}
private static void Emailpropertychanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (EditForm)bindable;
control.txtEmail.Text = (string)newValue;
}
private static void Descriptionchanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (EditForm)bindable;
control.txtDescription.Text = (string)newValue;
}
public EditForm()
{
InitializeComponent();
}
}
<StackLayout>
<local:EditForm
DescriptionText="{Binding option}"
EmailText="{Binding Email}"
NameText="{Binding Name}"
PhoneNumberText="{Binding Phone}" />
</StackLayout>
public partial class Page5 : ContentPage
{
public Contact contact1 { get; set; }
public Page5()
{
InitializeComponent();
contact1 = new Contact() { Name = "cherry", Phone = "1234567", Email = "xxxxxxxx", option = "this is test" };
this.BindingContext = contact1 ;
}
}
public class Contact:ViewModelBase
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
private string _phone;
public string Phone
{
get { return _phone; }
set
{
_phone = value;
RaisePropertyChanged("Phone");
}
}
private string _email;
public string Email
{
get { return _email; }
set
{
_email = value;
RaisePropertyChanged("Email");
}
}
private string _option;
public string option
{
get { return _option; }
set
{
_option = value;
RaisePropertyChanged("option");
}
}
}
The ViewModelBase implementing INotifyPropertyChanged
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The screenshot:
When we want to extend any Controls properties we use Bindable Properties. For example for Label Control we need Border and Padding. In Xamarin 4.+ we do not have it. So we can go with Bindable Properties.
I will show you this with Padding. Create a class ExLabel in Shared project.
public class ExLabel : Label
{
public ExLabel()
{
}
public static readonly BindableProperty TitlePaddingProperty = BindableProperty.CreateAttached("Padding", typeof(Thickness), typeof(ExLabel), default(Thickness));
public Thickness TitlePadding
{
get { return (int)base.GetValue(TitlePaddingProperty); }
set { base.SetValue(TitlePaddingProperty, value); }
}
public static Thickness GetTitlePadding(BindableObject view)
{
return (Thickness)view.GetValue(TitlePaddingProperty);
}
}
In android level project implement Custom renderer to use ExLabel class.
[assembly: ExportRenderer(typeof(ExLabel), typeof(ExLabelRenderer))]
namespace Chemicals.Droid.Renderers
{
public class ExLabelRenderer : LabelRenderer
{
Context context;
public ExLabelRenderer(Context context) : base(context)
{
this.context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null)
{
ExLabel exLabel = (ExLabel)Element;
var padding = ExLabel.GetTitlePadding(exLabel);
Control.SetPadding((int)padding.Left, (int)padding.Top, (int)padding.Right, (int)padding.Bottom);
}
}
}
}
In xaml use padding like this
<local:ExLabel IsBorder="False"
TitlePadding="40,0,0,0"
Text="TR01"/>
DONE.
Similarly we can add other Bindable Properties, say along with padding you want border also for Label and need border for few Labels, not for all the ExLabel. Then we can have Bindable Properties like
public static readonly BindableProperty IsBorderProperty
public bool IsBorder
{
get { return (bool)GetValue(IsBorderProperty); }
set { SetValue(IsBorderProperty, value); }
}
public static readonly BindableProperty ShowBorderProperty
public Color ShowBorder
{
get { return (bool)GetValue(ShowBorderProperty); }
set { SetValue(ShowBorderProperty, value); }
}
From xaml we can pass IsBorder true false and ShowBorder Color. In custom renderer file we can check if IsBorder is true then set Color for border otherwise skip it.
if(ExLabel.GetBorder(exLabel))
Control.SetBackground(set resource here);
Related
I have a problem where I can't seem to disable a button which has IsEnabled bound to a property.
I have the following XAML:
<Button Text="Skip" IsEnabled="{Binding SkipEnabled}" ... />
It is being bound to this BindableProperty:
public static readonly BindableProperty SkipEnabledProperty = BindableProperty.Create(
propertyName: nameof(SkipEnabled), returnType: typeof(bool?), declaringType: typeof(WizardMasterPage), propertyChanged: SetSkipEnabled,
defaultValue: null, defaultBindingMode: BindingMode.TwoWay);
public bool? SkipEnabled
{
get { return (bool?)GetValue(SkipEnabledProperty); }
set { SetValue(SkipEnabledProperty, value); }
}
The problem is the button is always enabled.
The propertyChanged event isn't firing on the pages where the property is set to false - presumably because it is false when the page is created, so doesn't think it has changed and hence the binding is not updated. I notice that it does fire on the pages where I set the property to True.
Has anyone else seen this? Are there any workarounds?
Note: I tried just bool as well and that didn't work either.
Thanks
Is this Button included in a ContentView, right?
If yes, then you can refer to the following code.
I achieved this function and I added a Button on the current page to reset the value, then the UI could refresh automatically.
ChildView.xaml.cs
public partial class ChildView : ContentView
{
public String YourName
{
get
{
String value = (String)GetValue(YourNameProperty);
return value;
}
set
{
SetValue(YourNameProperty, value);
}
}
public static readonly BindableProperty YourNameProperty = BindableProperty.Create(nameof(YourName)
, typeof(String)
, typeof(ChildView), defaultBindingMode: BindingMode.TwoWay, propertyChanged: OnYourNameChanged);
public bool SkipEnabled
{
get
{
bool value = (bool)GetValue(SkipEnabledProperty);
return value;
}
set
{
SetValue(YourNameProperty, value);
}
}
public static readonly BindableProperty SkipEnabledProperty = BindableProperty.Create(nameof(SkipEnabled)
, typeof(bool)
, typeof(ChildView), defaultBindingMode: BindingMode.TwoWay, propertyChanged: OnYourNameChanged);
static void OnYourNameChanged(BindableObject bindable, object oldValue, object newValue)
{
Console.WriteLine("-----------------> " + newValue);
}
public ChildView()
{
InitializeComponent();
}
}
ChildView.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiContentViewApp.ChildView"
x:Name="TestControlView"
>
<VerticalStackLayout>
<Label Text="{Binding Source={x:Reference TestControlView}, Path=YourName}"
VerticalOptions="Center"
HorizontalOptions="Center" />
<Button Text="Skip" IsEnabled="{Binding Source={x:Reference TestControlView}, Path=SkipEnabled}" />
</VerticalStackLayout>
</ContentView>
MainPage.xaml
<?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"
xmlns:mauiapp="clr-namespace:MauiContentViewApp"
x:Class="MauiContentViewApp.MainPage">
<ContentPage.BindingContext>
<mauiapp:MyViewModel></mauiapp:MyViewModel>
</ContentPage.BindingContext>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<mauiapp:ChildView YourName="{Binding TestName}" SkipEnabled="{Binding TestBoolvalue}" ></mauiapp:ChildView>
<Button Text="reset" Command="{Binding ResetValueCommand}"/>
</VerticalStackLayout>
</ContentPage>
MyViewModel.cs
public class MyViewModel: INotifyPropertyChanged
{
private string _testName;
public string TestName
{
set { SetProperty(ref _testName, value); }
get { return _testName; }
}
private bool _testBoolValue;
public bool TestBoolvalue
{
set { SetProperty(ref _testBoolValue, value); }
get { return _testBoolValue; }
}
public ICommand ResetValueCommand => new Command(resetMethod);
private void resetMethod(object obj)
{
TestName = "reset test string";
TestBoolvalue = !TestBoolvalue;
}
public MyViewModel()
{
TestName = "test string";
TestBoolvalue = false;
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I have this XAML
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
...
x:Class="MyProj.LoginPageView"
local:ViewModelLocator.AutoWireViewModel="True">
...cut for brevity...
<Entry MinimumWidthRequest="60" Grid.Row="0" Grid.Column="2" VerticalOptions="Center" Text="{Binding Username, Mode=TwoWay}"/>
<Entry Grid.Row="1" Grid.Column="2" VerticalOptions="Center" Text="{Binding Password, Mode=TwoWay}"/>
<Button Text="Login" Command="{Binding LoginCommand}"/>
<Button Text="Register" Command="{Binding RegisterCommand}"/>
Attached using autowiring to this viewmodel:
public class LoginPageViewModel: BindableObject
{
public ICommand LoginCommand { get; set; }
public ICommand RegisterCommand { get; set; }
private string username;
private string password;
public string Username { get { return username; } set { username = value; OnPropertyChanged("Username"); } }
public string Password { get { return password; } set { password = value; OnPropertyChanged("Password"); } }
public LoginPageViewModel()
{
LoginCommand = new Command(() =>
Login());
RegisterCommand = new Command(() =>
Register());
}
private void Login()
{
}
private void Register()
{
Console.WriteLine("sdfsd");
}
}
The command bindings are working fine and the Register and Login methods get called when I click the button. In the autowiring the binding context of the view is set to the viewmodel as follows:
static ViewModelLocator()
{
_container = new TinyIoCContainer();
//register viewmodels
_container.Register<LoginPageViewModel>();
}
public static readonly BindableProperty AutoWireViewModelProperty =
BindableProperty.CreateAttached("AutoWireViewModel", typeof(bool), typeof(ViewModelLocator), default(bool), propertyChanged: OnAutoWireViewModelChanged);
public static bool GetAutoWireViewModel(BindableObject bindable)
{
return (bool)bindable.GetValue(ViewModelLocator.AutoWireViewModelProperty);
}
public static void SetAutoWireViewModel(BindableObject bindable, bool value)
{
bindable.SetValue(ViewModelLocator.AutoWireViewModelProperty, value);
}
private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = bindable as Element;
if (view == null)
{
return;
}
var viewType = view.GetType();
var viewName = viewType.FullName.Replace(".Views.", ".ViewModels.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var viewModelName = string.Format(
CultureInfo.InvariantCulture, "{0}Model, {1}", viewName, viewAssemblyName);
var viewModelType = Type.GetType(viewModelName);
if (viewModelType == null)
{
return;
}
var viewModel = _container.Resolve(viewModelType);
view.BindingContext = viewModel;
}
When I click register in debug mode the register method gets called which means the command binding has been connected but the value I put in the Entry fields for username and password aren't connected to the corresponding properties on the VM and the properties just have null values.
I'm using Event to Command Behavior found here to implement a command in the ItemTapped event of a ListView. I copied the EventToCommandBehavior and BehaviorBase classes to my project.
Here is my View
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:AppVentas.Behaviors"
mc:Ignorable="d"
x:Class="AppVentas.Views.UsuariosView">
<ContentPage.Content>
<ListView HasUnevenRows="True"
ItemsSource="{Binding Usuarios}"
ItemTapped="ListView_ItemTapped">
<ListView.Behaviors>
<local:EventToCommandBehavior
EventName="ItemTapped"
Command="{Binding OpenChatCommand}"/>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Nombre}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
And my ViewModel
public class UsuariosViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Usuarios> Usuarios { get; set; }
public ICommand OpenChatCommand { get; set; }
private UsuarioController usuarioController = new UsuarioController();
public UsuariosViewModel()
{
Usuarios = new ObservableCollection<Usuarios>(usuarioController.Get().Where(i => i.Token == null));
OpenChatCommand = new Command<Usuarios>(OpenChat);
}
void OpenChat(Usuarios usuario)
{
//trying to do something
}
}
The problem is that OpenChatCommand never gets executed, the method OnEvent of the EventToCommandBehavior class do gets executed but the line Command.Execute (resolvedParameter); just doesn't do anything.
I'm using the PropertyChanged.Fody package if that's of any use.
Any help is appreciated, thanks.
From Reusable EventToCommandBehavior, I suggest you can use Converter in your code, I create simple that you can take a look:
<ContentPage
x:Class="Listviewsample.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converter="clr-namespace:Listviewsample"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:local="clr-namespace:Listviewsample.Behaviors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<ContentPage.Resources>
<converter:TappedItemConverter x:Key="converter1" />
</ContentPage.Resources>
<StackLayout>
<!-- Place new controls here -->
<ListView
x:Name="listview1"
HasUnevenRows="True"
ItemsSource="{Binding images}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding title}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Behaviors>
<local:EventToCommandBehavior
Command="{Binding command1}"
Converter="{StaticResource converter1}"
EventName="ItemTapped" />
</ListView.Behaviors>
</ListView>
</StackLayout>
public partial class MainPage : ContentPage
{
public ObservableCollection<imagemodel> images { get; set; }
public Command command1 { get; set; }
public MainPage()
{
InitializeComponent();
images = new ObservableCollection<imagemodel>()
{
new imagemodel(){title="image 1"},
new imagemodel(){title="image 2"},
new imagemodel(){title="image 3"}
};
command1 = new Command<imagemodel>(commandpage);
this.BindingContext = this;
}
private void commandpage(imagemodel m)
{
Console.WriteLine("the image model title is {0}",m.title.ToString());
}
}
public class imagemodel
{
public string title { get; set; }
}
The BehaviorBase.cs:
public class BehaviorBase<T> : Behavior<T> where T : BindableObject
{
public T AssociatedObject { get; private set; }
protected override void OnAttachedTo(T bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
if (bindable.BindingContext != null)
{
BindingContext = bindable.BindingContext;
}
bindable.BindingContextChanged += OnBindingContextChanged;
}
protected override void OnDetachingFrom(T bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
AssociatedObject = null;
}
void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
The EventToCommandBehavior.cs:
public class EventToCommandBehavior : BehaviorBase<View>
{
Delegate eventHandler;
public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty InputConverterProperty = BindableProperty.Create("Converter", typeof(IValueConverter), typeof(EventToCommandBehavior), null);
public string EventName
{
get { return (string)GetValue(EventNameProperty); }
set { SetValue(EventNameProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public IValueConverter Converter
{
get { return (IValueConverter)GetValue(InputConverterProperty); }
set { SetValue(InputConverterProperty, value); }
}
protected override void OnAttachedTo(View bindable)
{
base.OnAttachedTo(bindable);
RegisterEvent(EventName);
}
protected override void OnDetachingFrom(View bindable)
{
DeregisterEvent(EventName);
base.OnDetachingFrom(bindable);
}
void RegisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));
}
MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
eventInfo.AddEventHandler(AssociatedObject, eventHandler);
}
void DeregisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
if (eventHandler == null)
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));
}
eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
eventHandler = null;
}
void OnEvent(object sender, object eventArgs)
{
if (Command == null)
{
return;
}
object resolvedParameter;
if (CommandParameter != null)
{
resolvedParameter = CommandParameter;
}
else if (Converter != null)
{
resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null);
}
else
{
resolvedParameter = eventArgs;
}
if (Command.CanExecute(resolvedParameter))
{
Command.Execute(resolvedParameter);
}
}
static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
{
var behavior = (EventToCommandBehavior)bindable;
if (behavior.AssociatedObject == null)
{
return;
}
string oldEventName = (string)oldValue;
string newEventName = (string)newValue;
behavior.DeregisterEvent(oldEventName);
behavior.RegisterEvent(newEventName);
}
}
The Converter.cs,The Command property of the behavior is data bound to the command1 property of the associated ViewModel, while the Converter property is set to the TappedItemConverter instance, which returns the Tapped item of the ListView from the ItemTappedEventArgs.
public class TappedItemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var eventArgs = value as ItemTappedEventArgs;
return eventArgs.Item;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Here is the sample at github, you can take a look:
https://github.com/CherryBu/Eventtocommand
Assuming your bindingcontext is correctly set to an instance of UsuariosViewModel, the issue I see here is that you are not passing the command parameter. Your command accepts a Usuarios, but you need to pass that in via the CommandParameter property on the EventToCommandBehavior.
I also notice you have ListView_ItemTapped defined, but I'm not sure what you're doing in that method. It shouldn't be required to make the command work, but maybe you're using it for something else.
I created a custom control which has an Entry and a Label inside a StackControl. Then I exposed the IsEnabled property of the Entry thorough a bindable property called IsEntryEnabled which I wanted to bind to a bool property of my VM. But it never triggers. Any idea what am I doing wrong here?
Custom control - Xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:effects="clr-namespace:VMSTablet.Effects;assembly=VMSTablet"
x:Class="VMSTablet.Controls.StandardFormEntry">
<StackLayout >
<Label Text="{Binding LabelText}" Style="{StaticResource DefaultLabelStyle}" TextColor="{StaticResource DarkGreenColor}"/>
<Entry x:Name="CustomEntry" Text="{Binding Text}"
IsEnabled="{Binding IsEntryEnabled}"
Keyboard="{Binding Keyboard}"
Behaviors="{Binding Behaviors}"
TextColor="{StaticResource DarkGreenColor}"
Placeholder="{Binding Placeholder}" Style="{StaticResource DefaultEntryStyle}" >
<Entry.Effects>
<effects:EntryBarColorEffect/>
</Entry.Effects>
</Entry>
</StackLayout>
</ContentView>
Custom control - Code Behind
namespace VMST.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class StandardFormEntry
{
public event EventHandler OnTextChanged;
public event EventHandler OnUnfocused;
public event EventHandler OnFocused;
public static readonly BindableProperty PlaceholderProperty =
BindableProperty.Create("Placeholder", typeof(string), typeof(StandardFormEntry), string.Empty);
public static readonly BindableProperty LabelTextProperty =
BindableProperty.Create("LabelText", typeof(string), typeof(StandardFormEntry), string.Empty);
public static readonly BindableProperty EntryTextProperty =
BindableProperty.Create("EntryText", typeof(string), typeof(StandardFormEntry), string.Empty);
public static BindableProperty IsEntryEnabledProperty =
BindableProperty.Create("IsEntryEnabled", typeof(bool), typeof(StandardFormEntry), true);
public static readonly BindableProperty KeyboardProperty =
BindableProperty.Create("Keyboard", typeof(Keyboard), typeof(StandardFormEntry), Xamarin.Forms.Keyboard.Default);
public static readonly BindableProperty BehaviorsProperty =
BindableProperty.Create(nameof(Behaviors), typeof(IList<Behavior>), typeof(StandardFormEntry));
public string LabelText
{
set
{
SetValue(LabelTextProperty, value);
}
get => (string)GetValue(LabelTextProperty);
}
public string EntryText
{
set => SetValue(EntryTextProperty, value);
get => (string)GetValue(EntryTextProperty);
}
public string Placeholder
{
set => SetValue(PlaceholderProperty, value);
get => (string)GetValue(PlaceholderProperty);
}
public bool IsEntryEnabled
{
set
{
SetValue(IsEntryEnabledProperty, value);
}
get => (bool)GetValue(IsEntryEnabledProperty);
}
public Keyboard Keyboard
{
set => SetValue(KeyboardProperty, value);
get => (Keyboard)GetValue(KeyboardProperty);
}
public IList<Behavior> Behaviors
{
set => SetValue(BehaviorsProperty, value);
get => (IList<Behavior>)GetValue(BehaviorsProperty);
}
public StandardFormEntry()
{
InitializeComponent();
BindingContext = this;
CustomEntry.BindingContext = this;
PropertyChanged += StandardFormEntry_PropertyChanged;
CustomEntry.Unfocused += CustomEntry_Unfocused;
CustomEntry.TextChanged += CustomEntry_TextChanged;
CustomEntry.Focused += CustomEntry_Focused;
}
private void StandardFormEntry_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == IsEntryEnabledProperty.PropertyName)
{
**//This never happens!**
}
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
}
private void CustomEntry_Focused(object sender, FocusEventArgs e)
{
OnFocused?.Invoke(sender, e);
}
private void CustomEntry_TextChanged(object sender, TextChangedEventArgs e)
{
OnTextChanged?.Invoke(sender, e);
}
private void CustomEntry_Unfocused(object sender, FocusEventArgs e)
{
OnUnfocused?.Invoke(sender, e);
}
}
}
View Model
I'm trying to trigger the IsEntryEnabled property inside the EditForm() method below. But it doesn't work.
public class PassRegistrationPageViewModel : ViewModelBase
{
public DelegateCommand AddressCommand { get; set; }
public DelegateCommand EditCommand { get; set; }
public DelegateCommand ConfirmCommand { get; set; }
public PassRegistrationPageViewModel(INavigationService navigationService) : base(navigationService)
{
Title = "Page Title";
AddressCommand = new DelegateCommand(ShowBuildings);
EditCommand = new DelegateCommand(EditForm);
ConfirmCommand = new DelegateCommand(ConfirmPass);
//IsQrVisible = false;
}
private bool _isQrVisible;
public bool IsQrVisible
{
get { return _isQrVisible; }
set {
SetProperty(ref _isQrVisible, value);
}
}
private bool _isEditingEnabled;
public bool IsEditingEnabled //Bound to the view below
{
get { return _isEditingEnabled; }
set { SetProperty(ref _isEditingEnabled, value); }
}
private string _text;
public string Text
{
get { return _text; }
set
{
SetProperty(ref _text, value);
}
}
private async void ShowBuildings()
{
await NavigationService.NavigateAsync(nameof(BuildingListPage));
}
public void EditForm()
{
IsEditingEnabled = IsEditingEnabled ? false : true;
}
public void ConfirmPass()
{
}
}
View
I bind the IsEditingEnabled property to IsEntryEnabled property in my custom control which is supposed to trigger the IsEnabled property in the Entry inside. But it never triggers.
<controls:StandardFormEntry IsEntryEnabled="{Binding IsEditingEnabled}" EntryText="{Binding Text}" LabelText="Name" Grid.Column="0" Grid.Row="3"/>
In your custom control, you need to give a name to your content view and refer the name to the source of the binding property, like below:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:effects="clr-namespace:VMSTablet.Effects;assembly=VMSTablet"
x:Class="VMSTablet.Controls.StandardFormEntry"
x:Name="CustomEntryControl">
<StackLayout >
<Label Text="{Binding LabelText}" Style="{StaticResource DefaultLabelStyle}" TextColor="{StaticResource DarkGreenColor}"/>
<Entry x:Name="CustomEntry" Text="{Binding Text}"
IsEnabled="{Binding IsEntryEnabled, Source={x:Reference CustomEntryControl}}"
Keyboard="{Binding Keyboard}"
Behaviors="{Binding Behaviors}"
TextColor="{StaticResource DarkGreenColor}"
Placeholder="{Binding Placeholder}" Style="{StaticResource DefaultEntryStyle}" >
<Entry.Effects>
<effects:EntryBarColorEffect/>
</Entry.Effects>
</Entry>
</StackLayout>
In our edit page, we have an issue to populate the value into Selected Item in picker and it won't select for some reason from the LoadCourses() or LoadRoundCategories() either.
Any ideas?
Here's the code:
ViewModel
public class EditGolfRoundViewModel : INotifyPropertyChanged
{
ApiServices _apiServices = new ApiServices();
private string _message;
private ObservableCollection<GolfCourse> _courses;
private ObservableCollection<GolfRoundCategory> _roundCategories;
private object_selectedGolfCourse;
private GolfRoundCategory _selectedGolfRoundCategory;
private GolfRound _golfRound;
public EditGolfRoundViewModel()
{
_selectedGolfCourse = new GolfCourse();
_selectedGolfRoundCategory = new GolfRoundCategory();
LoadCourses();
LoadRoundCategories();
}
public GolfRound GolfRound
{
get { return _golfRound; }
set
{
_golfRound = value;
OnPropertyChanged();
}
}
public string Message
{
get { return _message; }
set
{
_message = value;
OnPropertyChanged();
}
}
public ObservableCollection<GolfCourse> GolfCourses
{
get { return _courses; }
set
{
if (_courses != value)
{
_courses = value;
OnPropertyChanged();
}
}
}
public ObservableCollection<GolfRoundCategory> GolfRoundCategories
{
get { return _roundCategories; }
set
{
_roundCategories = value;
OnPropertyChanged();
}
}
public object SelectedGolfCourse
{
get { return _selectedGolfCourse; }
set
{
_selectedGolfCourse = value;
var golfCourse = _selectedGolfCourse as GolfCourse;
Guid tempGolfCourseID = golfCourse.GolfCourseID;
OnPropertyChanged("SelectedGolfCourse");
}
}
public GolfRoundCategory SelectedGolfRoundCategory
{
get { return _selectedGolfRoundCategory; }
set
{
_selectedGolfRoundCategory = value;
OnPropertyChanged();
}
}
public ICommand EditCommand
{
get
{
return new Command(async() =>
{
GolfRound.GolfCourseID = SelectedGolfCourse.GolfCourseID;
GolfRound.GolfCourse = SelectedGolfCourse;
GolfRound.GolfRoundCategoryID = SelectedGolfRoundCategory.GolfRoundCategoryID;
GolfRound.GolfRoundCategory = SelectedGolfRoundCategory;
GolfRound.LastModifiedUTC = System.DateTime.Now;
await _apiServices.PutGolfRoundAsync(GolfRound, Settings.AccessToken);
});
}
}
public ICommand DeleteCommand
{
get
{
return new Command(async () =>
{
await _apiServices.DeleteGolfRoundAsync(GolfRound.GolfRoundID, Settings.AccessToken);
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private async void LoadCourses()
{
GolfCourses = new ObservableCollection<GolfCourse>(await _apiServices.GetGolfCoursesAsync(Settings.AccessToken));
}
private async void LoadRoundCategories()
{
GolfRoundCategories = new ObservableCollection<GolfRoundCategory>(await _apiServices.GetGolfRoundCategoriesAsync(Settings.AccessToken));
}
}
View - XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:AthlosifyMobile.ViewModels.Golf"
x:Class="AthlosifyMobile.Views.EditGolfRoundPage">
<StackLayout Orientation="Vertical" VerticalOptions="Center" Spacing="30" Padding="30">
<Entry Text="{Binding GolfRound.Name}" Placeholder="name" FontSize="Default" />
<Entry Text="{Binding GolfRound.Notes}" Placeholder="notes" FontSize="Default" />
<Entry Text="{Binding GolfRound.DailyHandicap}" Placeholder="daily handicap" FontSize="Default" />
<Label Text="Date" />
<DatePicker Date="{Binding GolfRound.TeeOffUTC}"
Format="D"
Margin="30, 0, 0, 30" />
<Picker x:Name="pCourse" Title="Course" ItemsSource="{Binding GolfCourses}"
SelectedItem="{Binding SelectedGolfCourse, Mode=TwoWay}"
ItemDisplayBinding="{Binding Name}"></Picker>
<Entry Text="{Binding GolfRound.GolfCourse.Name}" Placeholder="selected golf course" FontSize="Default" />
<Picker x:Name="pCategory" Title="Category" ItemsSource="{Binding GolfRoundCategories}"
SelectedItem="{Binding SelectedGolfRoundCategory, Mode=TwoWay}"
ItemDisplayBinding="{Binding Name}"></Picker>
<Entry Text="{Binding SelectedGolfRoundCategory.Name}" Placeholder="selected round category" FontSize="Default" />
<Button Command="{Binding EditCommand}" Text="Edit Round" />
<Button Command="{Binding DeleteCommand}" Text="Delete Round" />
<Label Text="{Binding Message}" ></Label>
</StackLayout>
View - code behind
public partial class EditGolfRoundPage : ContentPage
{
public EditGolfRoundPage (GolfRound round)
{
var editGolfRoundViewModel = new EditGolfRoundViewModel();
editGolfRoundViewModel.GolfRound = round;
BindingContext = editGolfRoundViewModel;
InitializeComponent ();
//var editGolfRoundViewModel = new EditGolfRoundViewModel();
//editGolfRoundViewModel.GolfRound = round;
//editGolfRoundViewModel.SelectedGolfCourse = round.GolfCourse;
//BindingContext = editGolfRoundViewModel;
}
}
Implement IEquatable for class of property used in SelectedItem:
public class GolfCourse : IEquatable<GolfCourse>
{
...
public bool Equals(GolfCourse other)
{
if (other == null) return false;
return (this.Name.Equals(other.Name));
}
}
Usage, assuming ItemsSource contains an object with value of Name as shown below:
SelectedGolfCourse = new GolfCourse { Name = "Course 2" };
The following is true as of Xamarin Forms v4.7.
See if you are doing this:
<Picker x:Name="DefaultEntitlement" Title="Entitlement"
SelectedItem="{Binding SelectedOwnerEntitlement, Mode=TwoWay}"
ItemsSource="{Binding OwnerEntitlements, Mode=TwoWay}">
</Picker>
Instead of this:
<Picker x:Name="DefaultEntitlement" Title="Entitlement"
ItemsSource="{Binding OwnerEntitlements, Mode=TwoWay}"
SelectedItem="{Binding SelectedOwnerEntitlement, Mode=TwoWay}">
</Picker>
you are binding view model before Initialise page so that is wrong thing we can not bind data without Initialise page fro that you need to change code of xaml.cs like below
public EditGolfRoundPage (GolfRound round)
{
InitializeComponent ();
BindingContext = editGolfRoundViewModel;
BindingContext.GolfRound = round;
}
that will work for you
Happy Coding :)
Xaml
<Picker x:Name="ProductPicker" WidthRequest="220" HeightRequest="35" Title="Select" ItemsSource="{Binding ProductList}" SelectedItem="{Binding ProductSelected}" ItemDisplayBinding="{Binding ProductName}"> </Picker>
ViewModel
public List<ProductModel> ProductList { get; set; }
Populating Data in Datasource in Viewmodel
ProductList = Products.Result.ToList();
Getting Selected Data
private object _ProductSelected;
public object ProductSelected
{
get { return _ProductSelected; }
set
{
_ProductSelected = value;
ProductSelected_SelectedIndex.Execute(value);
OnPropertyChanged("ProductSelected"); //in case you are using MVVM Light
}
}
private Command ProductSelected_SelectedIndex
{
get
{
return new Command((e) =>
{
}}}
private object _CitySelectedFromList;
public object CitySelectedFromList
{
get { return _CitySelectedFromList; }
set
{
_CitySelectedFromList = value;
var cityid = _CitySelectedFromList as CityMasterModel;
tempcityids = Convert.ToInt32(cityid.Id);
}
}
Can you try once replacing your Viewmodel. I have changed the type from Object to actual type. Set the Default item while loading the items from endpoint.
public class EditGolfRoundViewModel : INotifyPropertyChanged
{
ApiServices _apiServices = new ApiServices();
private string _message;
private ObservableCollection<GolfCourse> _courses;
private ObservableCollection<GolfRoundCategory> _roundCategories;
private GolfCourse _selectedGolfCourse;
private GolfRoundCategory _selectedGolfRoundCategory;
private GolfRound _golfRound;
public EditGolfRoundViewModel()
{
LoadCourses();
LoadRoundCategories();
}
public GolfRound GolfRound
{
get { return _golfRound; }
set
{
_golfRound = value;
OnPropertyChanged();
}
}
public string Message
{
get { return _message; }
set
{
_message = value;
OnPropertyChanged();
}
}
public ObservableCollection<GolfCourse> GolfCourses
{
get { return _courses; }
set
{
if (_courses != value)
{
_courses = value;
OnPropertyChanged();
}
}
}
public ObservableCollection<GolfRoundCategory> GolfRoundCategories
{
get { return _roundCategories; }
set
{
_roundCategories = value;
OnPropertyChanged();
}
}
public GolfCourse SelectedGolfCourse
{
get { return _selectedGolfCourse; }
set
{
_selectedGolfCourse = value;
OnPropertyChanged("SelectedGolfCourse");
}
}
public GolfRoundCategory SelectedGolfRoundCategory
{
get { return _selectedGolfRoundCategory; }
set
{
_selectedGolfRoundCategory = value;
OnPropertyChanged();
}
}
public ICommand EditCommand
{
get
{
return new Command(async () =>
{
GolfRound.GolfCourseID = SelectedGolfCourse.GolfCourseID;
GolfRound.GolfCourse = SelectedGolfCourse;
GolfRound.GolfRoundCategoryID = SelectedGolfRoundCategory.GolfRoundCategoryID;
GolfRound.GolfRoundCategory = SelectedGolfRoundCategory;
GolfRound.LastModifiedUTC = System.DateTime.Now;
await _apiServices.PutGolfRoundAsync(GolfRound, Settings.AccessToken);
});
}
}
public ICommand DeleteCommand
{
get
{
return new Command(async () =>
{
await _apiServices.DeleteGolfRoundAsync(GolfRound.GolfRoundID, Settings.AccessToken);
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private async void LoadCourses()
{
GolfCourses = new ObservableCollection<GolfCourse>(await _apiServices.GetGolfCoursesAsync(Settings.AccessToken));
if (GolfCourses != null && GolfCourses.Count() > 0)
SelectedGolfCourse = GolfCourses[0];
}
private async void LoadRoundCategories()
{
GolfRoundCategories = new ObservableCollection<GolfRoundCategory>(await _apiServices.GetGolfRoundCategoriesAsync(Settings.AccessToken));
if (GolfRoundCategories != null && GolfRoundCategories.Count() > 0)
SelectedGolfRoundCategory = GolfRoundCategories[0];
}
}
You can try:
make sure your prop support INotifyPropertyChanged
make sure Selected Item variable has initial value
try debug your selected item variable, make sure has value
1. Initials
Take a look your code:
public EditGolfRoundViewModel()
{
_selectedGolfCourse = new GolfCourse();
_selectedGolfRoundCategory = new GolfRoundCategory();
LoadCourses();
LoadRoundCategories();
}
If you try to initial value Selected Item, don't do this:
_selectedGolfCourse = new GolfCourse();
_selectedGolfRoundCategory = new GolfRoundCategory();
let it null, is fine. You can do like this:
SelectedGolfRoundCategory = new GolfRoundCategory();
//or
SelectedGolfRoundCategory = dataFromAPI;
2. Assign
Take a look your code:
public ICommand EditCommand
{
get
{
return new Command(async() =>
{
GolfRound.GolfCourseID = SelectedGolfCourse.GolfCourseID;
GolfRound.GolfCourse = SelectedGolfCourse;
GolfRound.GolfRoundCategoryID = SelectedGolfRoundCategory.GolfRoundCategoryID;
GolfRound.GolfRoundCategory = SelectedGolfRoundCategory;
GolfRound.LastModifiedUTC = System.DateTime.Now;
await _apiServices.PutGolfRoundAsync(GolfRound, Settings.AccessToken);
});
}
}
You trying selected item variable insert into to object GolfRound, like this part:
GolfRound.GolfRoundCategoryID = SelectedGolfRoundCategory.GolfRoundCategoryID;
GolfRound.GolfRoundCategory = SelectedGolfRoundCategory;
Make sure you have INotifyPropertyChanged implement this model GolfRound for prop GolfRoundCategoryID and GolfRoundCategory. If not, it would not work. I have experience for this.
Hope this helpful.