Binding xaml property visibility to ViewModel, control with button - c#

I have a StackLayout property in xmal like shown below:
<StackLayout x:Name="_infoView"
Margin="0,10,0,10"
BackgroundColor="Black"
IsVisible="{Binding State}"/>
and a binding bool variable in ViewModel
private Boolean _state = true;
public Boolean State
{
get { return _state; }
set { }
}
I have a button in my xmal and would like to control the visibility of my StackLayout, So I did something like this:
<Button x:Name="CloseButton"
Grid.Row="0"
Grid.Column="3"
Command="{Binding CloseWindowCommand}"/>
and in ViewModel
CloseWindowCommand = new Command(CloseWindowTapped, CanCloseWindowTapped);
public ICommand CloseWindowCommand { get; set; }
public void CloseWindowTapped()
{
State = false;
}
public bool CanCloseWindowTapped()
{
return true;
}
I'd assume, by tap on the CloseButton, my StackLayout will gone... but it is not working

ViewModel should implement INotifyPropertyChanged interface for informing View about changes.
public class ViewModel : INotifyPropertyChanged
{
// Implementing INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName);
}
// In the setter of property raise event to inform view about changes
private Boolean _state = true;
public Boolean State
{
get
{
return _state;
}
set
{
_state = value;
RaisePropertyChanged();
}
}
}

Related

Xamarin forms bindable property entry not working

I'm struggeling a bit with a bindable property and the propertyChanged event not firing when new text is entered.
I've made a minimal codesample:
Xaml custom control:
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BindingPropertyProject.CustomFlyout">
<Entry x:Name="MyEntry"/>
Codebehind:
public partial class CustomFlyout : Grid
{
public CustomFlyout()
{
InitializeComponent();
}
public string MyEntryText
{
get { return (string)GetValue(MyEntryTextProperty); }
set
{
SetValue(MyEntryTextProperty, value);
}
}
public static readonly BindableProperty MyEntryTextProperty =
BindableProperty.Create(nameof(MyEntryText), typeof(string),
typeof(CustomFlyout),
defaultValue: string.Empty,
defaultBindingMode: BindingMode.TwoWay
, propertyChanging: TextChanged);
private static void TextChanged(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is CustomFlyout control)
{
control.MyEntry.Text = newValue?.ToString();
}
}
}
}
Consuming class xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BindingPropertyProject"
x:Class="BindingPropertyProject.MainPage">
<Grid>
<local:CustomFlyout MyEntryText="{Binding TextPropertyFromBindingContext, Mode=TwoWay}" HorizontalOptions="FillAndExpand" VerticalOptions="Start"/>
</Grid>
Consuming class codebehind:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
private string _textPropertyFromBindingContext = "bound";
public string TextPropertyFromBindingContext
{
get
{
return _textPropertyFromBindingContext;
}
set
{
if (_textPropertyFromBindingContext != value)
{
_textPropertyFromBindingContext = value;
OnPropertyChanged();
}
}
}
}
It binds the "bound" value just fine, but subsequent changes entered in the entry does not raise property changed.
I've tried a number of suggestions i found from googeling, but this should be fine right?
UPDATE:
Ok - so i actually got i to work by adding binding in the custom view:
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BindingPropertyProject.CustomFlyout">
<Entry x:Name="MyEntry" Text="{Binding TextPropertyFromBindingContext }"/>
Is this really the way to do it? I mean - i could only make it work, if bindings was named EXACTLY the same in custom view, and consuming part..
i could only make it work, if bindings was named EXACTLY the same in
custom view, and consuming part..
It's not necessary to have same binding name. Please refer following code.
Custom Control
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="StackQA2XF.CustomControl.MyCustomControl">
<ContentView.Content>
<Entry x:Name="CustomEntry"/>
</ContentView.Content>
</ContentView>
public partial class MyCustomControl : ContentView
{
public static readonly BindableProperty EntryTextProperty =
BindableProperty.Create(nameof(EntryText), typeof(string), typeof(MyCustomControl), default(string), BindingMode.TwoWay);
public string EntryText
{
get { return (string)GetValue(EntryTextProperty); }
set { SetValue(EntryTextProperty, value); }
}
public MyCustomControl()
{
InitializeComponent();
CustomEntry.SetBinding(Entry.TextProperty, new Binding(nameof(EntryText), source: this));
}
}
Consuming Class
<customcontrols:MyCustomControl EntryText="{Binding TitleText}"/>
public class MainViewModel : INotifyPropertyChanged
{
private string _titleText = "Good morning";
public string TitleText
{
get
{
return _titleText;
}
set
{
_titleText = value;
OnPropertyChange(nameof(TitleText));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChange(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Please do binding in the code for custom control and raise property change for the binding property in viewmodel.
CustomEntry.SetBinding(Entry.TextProperty, new Binding(nameof(EntryText), source: this));
OnPropertyChange(nameof(TitleText));
Please refer https://www.youtube.com/watch?v=ZViJyL9Ptqg.
I have tested this code able to get fired propertyChanged event when Entry text is changed from custom view.
It binds the "bound" value just fine, but subsequent changes entered in the entry does not raise property changed.
From Bindable Properties property changes, BindableProperty MyEntryTextProperty binding TextPropertyFromBindingContext, so the propertyChanged event will be fired when you change TextPropertyFromBindingContext, Instead of changing the value of MyEntry.
You can change TextPropertyFromBindingContext bu Button.click, then you will see the propertyChanged event will be fired.
public partial class Page3 : ContentPage, INotifyPropertyChanged
{
private string _textPropertyFromBindingContext = "bound";
public string TextPropertyFromBindingContext
{
get
{
return _textPropertyFromBindingContext;
}
set
{
if (_textPropertyFromBindingContext != value)
{
_textPropertyFromBindingContext = value;
RaisePropertyChanged("TextPropertyFromBindingContext");
}
}
}
public Page3()
{
InitializeComponent();
this.BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private void btn1_Clicked(object sender, EventArgs e)
{
TextPropertyFromBindingContext = "test";
}
}

ComboBox SelectedItem Binding

I have a ComboBox in my View:
<ComboBox Name="comboBox1" ItemsSource="{Binding MandantList}" SelectedItem="{Binding CurrentMandant, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Firma}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Here is my Model:
public class MandantListItem : INotifyPropertyChanged
{
public MandantListItem() { }
string _Firma;
bool _IsChecked;
public string Firma
{
get { return _Firma; }
set { _Firma = value; }
}
public bool IsChecked
{
get
{
return _IsChecked;
}
set
{
_IsChecked = value;
OnPropertyChanged(nameof(IsChecked));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And here is my ViewModel:
public class MaViewModel : INotifyPropertyChanged
{
public ObservableCollection<MandantListItem> MandantList { get { return _MandantList; } }
public ObservableCollection<MandantListItem> _MandantList = new ObservableCollection<MandantListItem>();
private MandantListItem _CurrentMandant;
public MandantListItem CurrentMandant
{
get { return _CurrentMandant; }
set
{
if (value != _CurrentMandant)
{
_CurrentMandant = value;
OnPropertyChanged("CurrentMandant");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
How to fill the ComboBox:
public zTiredV2.ViewModel.MaViewModel MAList = new zTiredV2.ViewModel.MaViewModel();
this.comboBox1.ItemsSource = MAList.MandantList;
MAList.MandantList.Add(new zTiredV2.Model.MandantListItem { Firma = "A", Homepage = "a.com", IsChecked = false });
MAList.MandantList.Add(new zTiredV2.Model.MandantListItem { Firma = "B", Homepage = "b.com", IsChecked = false });
But my item doesnt update ... tried also via IsChecked, but no success either ... when i iterate through MAList, IsChecked is always false. And how can i bind a TextBlock to the selected Firma?
Have a hard time with MVVM, but i like it.
You should set the DataContext of the ComboBox to an instance of your view model. Otherwise the bindings won't work:
this.comboBox1.DataContext = MAList;
Also note that the _MandantList backing field for your property shouldn't be public. In fact, you don't need it at all:
public ObservableCollection<MandantListItem> MandantList { get; } = new ObservableCollection<MandantListItem>();
Setting the DataContext should cause the CurrentMandant property to get set when you select an item in the ComboBox. It won't set the IsChecked property though.

Xamarin Label IsVisible Propety isn't changing

I have a PopUp Page and in this Popup page i have a Label with IsVisible Property bound to my ViewModel. However the site is not refreshing when the IsVisible Property is changing. Whats wrong with my code?
My Xaml
<StackLayout>
<Label Text="Name der Einkaufsliste" />
<Entry x:Name="entryList" FontSize="20"
Placeholder="z.B. Lidl" />
<Label Text="Liste schon Vorhanden!" TextColor="Red" IsVisible="{Binding IsVisible, Mode=TwoWay}"/>
</StackLayout>
My xaml.cs
PopupViewModel vm = new PopupViewModel();
public PopupViewListeHinzufügen()
{
InitializeComponent();
BindingContext = vm;
}
private void Button_Clicked(object sender, EventArgs e)
{
MasterPage master = new MasterPage();
master.addList(entryList.Text);
}
public void ListeVorhandenMeldung()
{
vm.setLabelVisible();
}
My viewmodel:
public class PopupViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public PopupViewModel()
{
IsVisible = false;
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void setLabelVisible()
{
IsVisible = true;
}
private bool isVisible;
public bool IsVisible
{
get
{
return isVisible;
}
set
{
isVisible = value;
OnPropertyChanged("IsVisible");
}
}
}
Thanks for any help!

Enable Button on RelayCommand WPF

Button is not getting enable on Command Enable, doEnable method. Click method is disabling button.
Button IsEnabled is Bind with ViewModel public property IsEnable, which is setting true on doEnable.
Kindly advise what is wrong in below Code
XAML:
<Button Content="{Binding DataText}" Height="30" Width="80" Command="{Binding Enable}" Click="ButtonBase_OnClick" IsEnabled="{Binding IsEnable}" ></Button>
Window2.cs:
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
DataContext = new ButtonEnableViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if(sender is Button btn)
btn.IsEnabled = false;
}
}
ButtonEnableViewModel.cs
class ButtonEnableViewModel : INotifyPropertyChanged
{
public ButtonEnableViewModel()
{
IsEnable = true;
DataText = "Click Here";
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChange(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private RelayCommand _enableCmd;
public RelayCommand Enable => _enableCmd ?? (_enableCmd = new RelayCommand(doEnable));
public bool IsEnable { get; set; }
public string DataText { get; set; }
protected void doEnable(object obj)
{
IsEnable = true;
DataText = "Clicked";
OnPropertyChange(nameof(IsEnable));
OnPropertyChange(nameof(DataText));
}
}
You should generally use the CanExecute method of the command to disable the Button. Most implementations of the ICommand interface accepts a Predicate<object> that you can use to tell the command when to enable the command/button.
In this case you might simply remove the event handler from the code-behind though and just handle the command in the view model:
<Button Content="{Binding DataText}" Height="30" Width="80" Command="{Binding Enable}" IsEnabled="{Binding IsEnable}" />
This should work since you set the IsEnable property in the Execute method of the command and raise the PropertyChanged event. You generally don't handle Click events in the code-behind of the view when you bind to a command of a view model.
If you use the CanExecute method of the command, you don't need to bind to the IsEnable property:
<Button Content="{Binding DataText}" Height="30" Width="80" Command="{Binding Enable}" />
View Model:
private RelayCommand _enableCmd;
public RelayCommand Enable => _enableCmd ?? (_enableCmd = new RelayCommand(doEnable, x => _isEnabled));
private bool _isEnabled;
public string DataText { get; set; }
protected void doEnable(object obj)
{
_isEnabled = true;
Enable.RaiseCanExecuteChanged();
DataText = "Clicked";
OnPropertyChange(nameof(DataText));
}

WPF MVVM button command that gets textbox value

I'm having a great trouble with understanding how button command works. I have something like this
{Binding TxtBox} gets value from model, let's say it's "aaa". I would like click the button and the value should appear in the second textbox (the one with {Binding TxtBox2}).
This is my xaml:
<TextBox Text="{Binding TxtBox, Source={StaticResource viewModel}}" />
<TextBox Text="{Binding TxtBox2, Source={StaticResource viewModel}}" />
<Button Command="{Binding ClickCommand}"/>
This is my ViewModel:
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
Do I really need this CommandHandler class? I copied the code from the net.
public string TxtBox
{
get { return Model.TxtBoxValue; }
set { Model.TxtBoxValue = value; }
}
public string TxtBox2 { get; set; }
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute)); // I believe that when the button is clicked MyAction() is triggered, right?
}
}
private bool _canExecute = true;
public void MyAction()
{
this.TxtBox2 = this.TxtBox; // should something like this work? Because right now it doesn't
}
The second textbox's binding never gets notified that it's bound property is changed. When you set this.TxtBox2 you should fire the propertychanged event for that property so the binding will be updated.
See think link for everything on bindings
I don't know if you are using prism as mvvm framework but that comes with the DelegateCommand class. I don't think there is a simple/lightweight implementation in the .net framework. See this link for the mvvm framework and the delegate command
The View reacts to binding changes through PropertyChanged events, of which you have none. Have anything that binds to the View implement INotifyPropertyChanged and then fire events when props change, and you're all set for your bindings to work (one way or two way).
Change your model to look like this and it should work for you.
public class MyViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged = null;
protected virtual void RaisePropertyChanged(string propName)
{
if(PropertyChanged != null)
{
Task.Run(() => PropertyChanged(this, new PropertyChangedEventArgs(propName)));
}
}
#endregion
public string TxtBox
{
get { return Model.TxtBoxValue; }
set
{
Model.TxtBoxValue = value;
RaisePropertyChanged("TxtBox");
}
}
// presuming TxtBox2Value is in Model...else use a field
public string TxtBox2
{
get { return Model.TxtBox2Value; }
set
{
Model.TxtBox2Value = value;
RaisePropertyChanged("TxtBox2");
}
}
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute)); // I believe that when the button is clicked MyAction() is triggered, right?
}
}
private bool _canExecute = true;
public void MyAction()
{
this.TxtBox2 = this.TxtBox; // should something like this work? Because right now it doesn't
}
}
IMO - it is better to have your Model implement INotifyPropertyChanged and then bind directly to it rather than wrap it in your ViewModel. If Model : INotifyPropertyChanged, then your ViewModel now looks like this:
public class MyViewModel
{
// fire prop changed event here if this model will be swapped out after the ctor...otherwise don't worry about it
public Model Model { get; set; }
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute));
}
}
private bool _canExecute = true;
public void MyAction()
{
Model = new Model();
Model.TxtBox2 = "Some new value";
}
}
...and your xaml changes to this:
<TextBox Text="{Binding Model.TxtBox, Source={StaticResource viewModel}}" />
<TextBox Text="{Binding Model.TxtBox2, Source={StaticResource viewModel}}" />
<Button Command="{Binding ClickCommand}"/>

Categories