xamarin.forms binding from xaml to property - c#

I am a total newbie with bindings in xaml and I really don't get it sometimes.
I have this in my xaml:
<ActivityIndicator IsRunning="{Binding IsLoading}" IsVisible="{Binding IsLoading}" />
The binding "IsLoading". Where do I declare/set this property?!
My .cs looks like this:
....
public bool IsLoading;
public CardsListXaml ()
{
InitializeComponent ();
IsLoading = true;
....

Bindings are typically resolved from the BindingContext property (in other implementations, this property is called DataContext). This is null by default (at least in other implementations of XAML), thus your view is unable to find the specified properties.
In your case, you must set the BindingContext property to this:
public CardsListXaml()
{
InitializeComponent();
BindingContext = this;
IsLoading = true;
}
However, this alone will not suffice. Your current solution does not implement a mechanism to notify the view of any property changes, so your view would have to implement INotifyPropertyChanged. Instead, I suggest you implement the Model-View-ViewModel pattern, which not only fits beautifully with data binding, but will result in a more maintainable and testable code base:
public class CardsListViewModel : INotifyPropertyChanged
{
private bool isLoading;
public bool IsLoading
{
get
{
return this.isLoading;
}
set
{
this.isLoading = value;
RaisePropertyChanged("IsLoading");
}
}
public CardsListViewModel()
{
IsLoading = true;
}
//the view will register to this event when the DataContext is set
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
And then in your code-behind's constructor:
public CardsListView()
{
InitializeComponent();
BindingContext = new CardsListViewModel();
}
Just to clarify, DataContext cascades down the visual tree, thus the ActivityIndicator control will be able to read to properties specified in the bindings.
Edit: Xamarin.Forms (and Silverlight/WPF etc... sorry, it's been a while!) also provides a SetBinding method (see the Data Binding section).

Related

How do I correctly implement INotifyPropertyChanged for my bool property and bind to CheckBox.IsChecked?

Novice here. I've been trying to wrap my head around databinding, and wanted to do try out two-way binding of a checkbox in the view to a boolean in a separate class that I've called "State". The point is to ensure that they are always in sync.
So I've made a checkbox in the view and bound it to the aforementioned boolean property in the State-class, accompanied by a button that bypasses the checkbox and toggles the boolean property directly (aptly labeled 'Ninja!'). The point was to test that the checkbox' databinding reacts when the property changes. However, I can't for the best of me figure out how the OnPropertyChanged-method is supposed to be invoked when the property changes.
Here's what I have so far:
<CheckBox x:Name="checkBox" Content="CheckBox" HorizontalAlignment="Left" Margin="232,109,0,0" VerticalAlignment="Top" IsChecked="{Binding Checked, Mode=TwoWay}"/>
<Button x:Name="button" Content="Ninja!" HorizontalAlignment="Left" Margin="228,182,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
And the code for the "State"-class I've made:
namespace TestTwoWayBinding
{
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged(Checked);
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Checked));
}
}
}
}
And the code-behind on the view for initialization and handling the events:
namespace TestTwoWayBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private State _state;
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
}
private void button_Click(object sender, RoutedEventArgs e)
{
_state.Toggle();
}
}
}
From what I gather, OnPropertyChanged expects a String propertyName, but I don't know what that would entail here. When I put in the name of the property (Checked), then that naturally refers to a boolean, not a string. What am I not getting? And what else am I doing wrong, as the checkbox doesn't register the property change when I change it through the button?
The two answers which suggest you pass the string literal "Checked" will work, but IMHO aren't the best way to do it. Instead, I prefer using [CallerMemberName] when implementing the OnPropertyChanged() method. (I have no idea what that third answer is all about…it doesn't appear to have anything to do with this question, and I'd guess it was just copy/pasted from somewhere else).
Here's an example of how I'd write your State class:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get { return _checked; }
set { _checked = value; OnPropertyChanged(); }
}
public void Toggle()
{
Checked = !Checked;
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The key here is that the parameter marked with [CallerMemberName] will automatically be filled in with the correct value from the caller, simply by not passing any value. The default value of null is there just so the compiler will allow the caller to not pass a value.
Note that I also simplified the Toggle() method. There's no need to use an if statement to transform one bool value into another; that's what the Boolean operators are there for.
I also changed the OnPropertyChanged() method so that it's thread-safe, i.e. won't crash if some code unsubscribes the last handler from the PropertyChanged event between the time the event field is compared to null and the time the event is actually raised. Typically, this is a non-issue as these properties are nearly always accessed only from a single thread, but it's easy enough to protect against and is a good habit to get into.
Note that in C# 6, you have the option of just writing PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); for the method body. Not everyone is using the new compiler 100% of the time yet, so I just mention that as an optional choice for you.
Naturally, you also need to set the DataContext correctly, as shown in one of the other answers:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
Though, personally, I'm not sure I'd bother with the constructor. You appear to have no other code that would set checkBox.IsChecked, so it seems to me that you're always going to get the default value anyway. Besides, you can't create your view model class in XAML if it doesn't have a parameterized constructor; in the future, you may prefer to configure your DataContext like that. E.g.:
<Window.DataContext>
<l:State Checked="True"/>
</Window.DataContext>
And in the window's constructor:
public MainWindow()
{
InitializeComponent();
_state = (State)this.DataContext;
}
See also the related Q&A Automatically INotifyPropertyChanged. The question there is really about something different — they want to implement the interface without having to explicitly write anything in the property setter — but for better or worse, the answers they got are really more about your scenario, where it's just a question of simplifying the property setter implementation rather than making it completely automatic.
I have to admit, I would've thought there would have been another question already with which to mark yours as a duplicate. And I did find lots of related questions. But nothing that focuses directly on just "how do I implement and use a view model that implements INotifyPropertyChanged?", which is really what your question seems to be about.
Addendum:
I did some more searching, and while none of these seem like they would be considered exact duplicates per se, they all have good information that help address the question about implementing INotifyPropertyChanged:
Use of Attributes… INotifyPropertyChanged
INotifyPropertyChanged for model and viewmodel
BindableBase vs INotifyChanged
How to write “ViewModelBase” in MVVM (WPF)
You are real close. You need to make 2 small changes and your test works:
Assign the DataContext of your Window to the _state variable.
Put the string "Checked" into the OnPropertyChanged and pass propertyName to the PropertyChangedEventArgs in the OnPropertyChanged method.
So your MainWindow ctor becomes:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
and the State class file looks like:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
As a novice, I recommend you learn more about Model-View-ViewModel MVVM Design Pattern. It is a common pattern with WPF and helps encourage separation of concerns (keeping your business logic out of your user interface logic)
The OnPropertyChanged method expects the Checked property's name as argument - at the moment, you're passing its value!
This means, change the Checked property declaration to:
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}

Why does Binding to an instance of my class not work when this.DataContext = this

I have a class that defines a preconfigured socket and all the methods needed to access and control a specific piece of equipment remotely. Part of the class includes an instance of an object that holds the current status of various aspects of the equipment. Each item in the object reports updates using INotifyPropertyUpdate. When I plug it into my test program, all of the methods are called and execute properly, but the only way I seem to be able to get updates of the status to show in the UI is when the DataContext is set to the "Current" object inside the instance of the class. If I set the DataContext to the instance of the class, or to the UI, I stop getting updates in the UI. I would like to be able to use the UI as the DataContext and then bind in the XAML using {Binding Path=InstanceOfMyClass.Current.StatusItemA}
The pertinent parts of the classes in question:
public MyClass : Socket, INotifyPropertyChanged // INotifyPropertyChanged is also used to notify changes in other parts of the class
{
public MyClass : base(//socket configuration info here)
{}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private CurrentStatusObject _current = new CurrentStatusObject();
public CurrentStatusObject Current
{
get { return _current; }
set
{
if (_current != value)
{
_current = value;
NotifyPropertyChanged();
}
}
}
// other methods and properties etc.
}
// this is the Current status object
public class CurrentStatusObject : object, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _statusItemA;
public string StatusItemA
{
get { return _statusItemA; }
set
{
if (_statusItemA != value)
{
_statusItemA = value;
NotifyPropertyChanged(); // not necessary to pass property name because of [CallerMemberName]
}
}
}
This works:
c#
this.DataContext = this.InstanceOfMyClass.Current;
XAML
<Label Content="{Binding Path=StatusItemA, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
This does not work, but I want it to:
c#
this.DataContext = this;
XAML
<Label Content="{Binding Path=InstanceOfMyClass.Current.StatusItemA, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
Nor does this:
c#
this.DataContext = this.InstanceOfMyClass;
XAML
<Label Content="{Binding Path=Current.StatusItemA, Mode=OneWay, UpdateSourceTrigger}"/>
I didn't see any answers when searching, but sometimes my research skills fail me. Any help would be appreciated. I enjoy learning new ways of coding. This is my first c# or wpf project. All my projects previous to this have been vb.net in WinForms so I'm at a slight handicap with the learning curve. I would like to learn the correct way to reach my goals for this project, which at this point is simply completing the UI.
The CurrentStatusObject notifies changes internally and that does work. The problem is that the changes are only reflected in the User Interface if I set the DataContext for the UI to that one object. I want to be able to set the DataContext to include a wider scope. I would be happy if I could use the instance of MyClass as the DataContext, but that is not working right now.
The question is Why? and How do I get it to work (using correct practices)?
I presume you have a typo...
public _Current Current = new _Current();
But if so this is a field and not a property. If you change it to this then the binding might work
private _Current _current = new _Current();
public _Current Current
{
get
{
return _current;
}
}
B.T.W: it is not standard to use underscore as part of your class name. Removing it should be all you need
You can only bind to Properties, never fields.
InstanceOfMyClass and Current needs to be declared as properties before you can bind to it (to make DataContext = this work).
As an aside, MVVM dictates that you shouldn't be using your View code-behind as the view model. You should have a separate class for that.

How to modify inner control property by setting container control dependency property?

I am creating a control in WPF which have inside a label and I have created a dependency property in order to modify the label's visibility property.
My problem is that I cant find a way to change my label's visibility property at the same time my dependency property is assigned.
My code is as below:
public static readonly DependencyProperty captionVisibleProperty = DependencyProperty.Register(
"CaptionVisible",
typeof(bool),
typeof(MyCustomControl));
public bool CaptionVisible
{
get
{
return (bool)GetValue(captionVisibleProperty);
}
set
{
SetValue(captionVisibleProperty, value);
ShowCaption();
}
}
private void ShowCaption()
{
if (CaptionVisible)
{
captionLabel.Visibility = System.Windows.Visibility.Visible;
}
else
{
captionLabel.Visibility = System.Windows.Visibility.Collapsed;
}
}
As you can see I have tried to call my ShowCaption() method when my property is set, but nothing happens.
So, what I am supposed to do in order to get it done?
Hope someone can help me. Thank you in advance.
ShowCaption() shouldn't be necessary. Instead, just bind the label's Visibility property to your CaptionVisible property in xaml. It's also best to follow the Model-View-ViewModel design pattern for keeping your code organized. This means putting the logic for controlling your user interface (the View) in separate ViewModel classes, and then assigning ViewModel to that View's DataContext property.
That will make the binding a lot easier. Referencing properties that belong to user interface elements can sometimes be a bit of a hassle in WPF. By contract, WPF's bindings system was specifically designed to make it easy to get at the contents of a user interface element's DataContext.
You'll also need to use the handy-dandy BooleanToVisiblityConverter to make the binding work, since the Visibility property's type isn't bool. I like to put it in in the window's (or control's) resource dictionary for easy access:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Label Visibility="{Binding Path=CaptionVisible,
Converter={StaticResource BooleanToVisibilityConverter}}">
<!-- label content -->
</Label>
As a side note, unless CaptionVisible is going to be the target of a binding, making it a dependency property is overkill. In this binding it's only the source, so just implementing INotifyPropertyChanged would be sufficient:
class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private bool _captionVisible;
public bool CaptionVisible
{
get { return _captionVisible; }
set
{
if(_captionVisible != value)
{
_captionVisible = value;
RaisePropertyChanged("CaptionVisible");
}
}
}
}
I have found a solution to this problem:
If you need to link a nested control dependency property to a container control dependency property you can do this:
public static readonly DependencyProperty captionLabelVisibilityProperty = DependencyProperty.Register(
"CaptionVisibility",
typeof(Visibility),
typeof(MyContainerControl),
new FrameworkPropertyMetadata(
VisibilityPropertyChangedCallback));
public Visibility CaptionVisibility
{
get
{ return (Visibility)GetValue(captionLabelVisibilityProperty); }
set
{ SetValue(captionLabelVisibilityProperty, value); }
}
private static void VisibilityPropertyChangedCallback(DependencyObject controlInstance, DependencyPropertyChangedEventArgs args)
{
MyContainerControl myContainerControlInstance = (MyContainerControl)controlInstance;
myContainerControlInstance.myLabel.Visibility = (Visibility)args.NewValue;
}

Naming {Binding} when you have more then 1 type on 1 window

If, on a WPF window, you have more than 1 {Binding} for a parameter, is it possible to name them ?
For example for images i have (in the XAML part): Visibility="{Binding}" but also on some textboxes i want to make IsEnabled="{Binding}"
Is there some way to name them ? So that in the code in the backside (.cs side) it will pick the right one ?
Thanx in advance.
You can simply have view model wrapping those properties into single object:
public class ViewModel : INotifyPropertyChanged
{
private bool imagesVisibility;
private bool isTextBoxEnabled;
public event PropertyChangedEventHandler PropertyChanged;
public bool ImagesVisibility
{
get { return this.imagesVisibility; }
set
{
this.imagesVisibility = value;
this.PropertyChanged(this,
new PropertyChangedEventArgs("ImagesVisibility"));
}
}
public bool IsTextBoxEnabled
{
// ... similar as with ImagesVisibility property implementation
}
}
Note that you'll also need a boolean to visibility converter, which examples of you can find on StackOverflow (here)... or elsewhere.
Then, you simply set instance of ViewModel to your form data context:
public MyForm()
{
InitializeComponent();
DataContext = new ViewModel();
}
You can then do images binding like Visibility="{Binding ImagesVisibility}" and textbox IsEnabled="{Binding IsTextBoxEnabled}".

How to bind to a local property in Silverlight without using a DataContext?

I have a small question but have been finding quite a few different, and mostly ambiguous, answers:
I have the following user control and I am trying to bind to a public property within that control (Events). Everyone says that I have to use the data context, however, I don't really want to do that... I just want to bind to the property from within the control's
XAML...
The requirement is that the binding has to be 2 way so any changes in the ui will be reflected in the property (or rather the collection) it is bound to. Each Event object within that collection also implements INotifyPropertyChanged the same way as this control...
Any ideas would be greatly appreciated!
public partial class EventEditorWindow : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Event> events;
public ObservableCollection<Event> Events
{
get { return this.events; }
set
{
if( this.events != value )
{
this.events = value;
this.RaisePropertyChanged("Events");
}
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
this.VerifyPropertyName(propertyName);
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
var currentObjectType = this.GetType();
if (currentObjectType.GetProperty(propertyName) == null)
{
throw new ArgumentException("Property not found", propertyName);
}
}
}
Thanks,
Bleepzter.
In the constructor, set DataContext = this. That will effectively make your code behind your DataContext. AFAIK, you can't completely avoid making something the DataContext.
You could use a RelativeSource so you don't need the DataContext:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type EventEditorWindow }}, Path=Events}
I use this cheat sheet from time to time.
EDIT Oops this is WPF syntax. See this post to have a look at this post to solve it in Silverlight

Categories

Resources