Image IsEnabled with Binding Property Does not work Xamarin Forms - c#

Using Xamarin forms -pcl v 2.3.4.267 -Debug on Android Device
I have an Image that is being used as button
<Image Source="loginbutton.png"
Aspect="AspectFit"
HorizontalOptions="Fill"
Margin="50,20,50,0"
fe:TappedGestureAttached.Command="{Binding Login}"
IsVisible ="{Binding user.IsSubmitEnabled}"<---works fine
IsEnabled="{Binding user.IsSubmitEnabled}"<---Does nothing
/>
as i mentioned in the code the Is Visible Works Great But Is Enabled does nothing.
note:-if there is any workaround please share it .

This is a known issue in Xamarin, already reported here and should be fixed in a future version of Xamarin.Forms, more specifically version 2.4.0-pre.
As a Workaround you can use the IsSubmitEnabled as the parameter for the CanExecute parameter in your Command.
Something like this:
public MyViewModel()
{
Login = new Command(() => OnLogin(), () => IsSubmitEnabled);
}
But you will need to add a line Login.CanExecute(null); in your Property setter too.
private bool _isSubmitEnabled;
public bool IsSubmitEnabled
{
get { return _isSubmitEnabled; }
set
{
_isSubmitEnabled= value;
RaisePropertyChanged(nameof(IsSubmitEnabled));
Login.CanExecute(null);
}
}
This should work in the mean time. Till the fix is in production.
Note: just for information, this issue seems only to be happening on Android while on iOS seems to be working correctly.
Hope this helps.-

Are you using both statements in parallel?
IsVisible ="{Binding user.IsSubmitEnabled}"<---works fine
IsEnabled="{Binding user.IsSubmitEnabled}"<---Does nothing
Then IsEnabled=false is only active if the button is invisible, because both are binding to the same boolean property => IsSubmitEnabled.
Maybe you have to use a second boolean binding property?

If you want unclickable when you enable property false you do as following
As First binding property of IsVisible and IsEnable must be different.
<Image Source="loginbutton.png"
Aspect="AspectFit"
HorizontalOptions="Fill"
Margin="50,20,50,0"
fe:TappedGestureAttached.Command="{Binding Login}"
IsVisible ="{Binding user.IsSubmitVisible}"
IsEnabled="{Binding user.IsSubmitEnabled}"
/>
And you change the code in ViewModel like:
public void Login()
{
If(IsSubmitEnabled){
// Put your code here
}
}

According to this post, if we bind IsEnabled property before binding commands, the properties wont' trigger. I ran in to same problem and moved IsEnabled binding after Command binding, and IsEnabled property was set correctly.
Hope that helps

Related

Is there a way to get UpdateSourceTrigger=PropertyChanged for compiled bindings?

I'm working on a UWP application and I realized that the default UpdateSourceTrigger mode for the TextBox control, which is LostFocus, can't be changed when using a compiled binding.
This means that whenever I want the binding to update for a TextBox, I have to use all this repeated boilerplate:
<TextBox
Text="{x:Bind ViewModel.Title, Mode=TwoWay}"
TextChanged="TextBox_OnTextChanged"/>
private void TextBox_OnTextChanged(object sender, TextChangedEventArgs e)
{
ViewModel.Title = ((TextBox)sender).Text;
}
Now, this is not too bad, but having to remember to create the TextChanged handler every single time a TextBox is used is annoying and error prone.
This would work fine with a classic binding:
<TextBox Text="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
But of course, here there would be the additional overhead of usinc classic bindings (involving runtime reflections, etc.).
Is there a way to get the same behaviour of UpdateSourceTrigger=PropertyChanged as well? I'd be completely fine with, say, writing a custom attached property that sets things up, as long as I can do everything I need directly from XAML, with no code behind involved.
Thanks!
UPDATE: (in response to Nico Zhu - MSFT's answer)
For my testing, it works well.
It doesn't for me, at all, As I said multiple times already, using UpdateSourceTrigger with x:Bind is just not possible. It doesn't compile, the property is shown in red in the XAML editor, it just isn't there. I really don't know where are you trying that, if you say it's working for you. I'm currently targeting 17763 as minimum and I can 100% guarantee that that does not work.
Compiled Binding is used with the {x:Bind} syntax as opposed to the {Binding} syntax of Classic Binding.
I'm well aware of the difference, I've already mentioned this multiple times, both in my original question here (with code snippets too) as well as in my comments.
It still uses the notifying interfaces (like INotifyPropertyChanged) to watch for changes
As I said, I'm aware of this too. But again, as from this question, this isn't the problem here at all. The issue is not with updates from the viewmodel to the bound property, but from the bound property (TextBox.Text in this case) to the viewmodel.
{x:Bind} is by default OneTime compared to {Binding} which is OneWay. so you need to declare bind Mode OneWay or TwoWay for {x:Bind}.
I'm sorry, but I have to say at this point I'm starting to wonder if you've actually read my initial question at all. I'm aware of this, and in fact you can see in both my original code snippets that I had already used the explicit Mode=TwoWay property in both my bindings.
And once again, this was not what the question was about, at all.
To reiterate: the issue here is that the TextBox.Text property defaults to the LostFocus trigger, and that the UpdateSourceTrigger property is not available for compiled bindings. So I'd like to know if there's a way to achieve the same, with a compiled binding, in XAML-only, without having to manually create a TextChanged handler every single time (and if not, if you plan to eventually add the UpdateSourceTrigger property to compiled bindings too).
Side note: I didn't mean to sound disrespectful here, and I hope we've now solved the existing misunderstandings with my question.
UPDATE #2: turns out the issue was causing by the ReSharper plugin, which was marking the UpdateSourceTrigger property as error in compiled bindings.
I've opened an issue for that here: https://youtrack.jetbrains.com/issue/RSRP-474438
Please check UpdateSourceTrigger documentation.
The default UpdateSourceTrigger value is Default. And
using default behavior from the dependency property that uses the binding. In Windows Runtime, this evaluates the same as a value with PropertyChanged. If you used Text="{x:Bind ViewModel.Title, Mode=TwoWay}", the Title will be changed when text changes. we have not need modify the viewmode in TextChanged even handler.
The premise is that we need implement INotifyPropertyChanged like the follow.
public class HostViewModel : INotifyPropertyChanged
{
private string nextButtonText;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public HostViewModel()
{
this.NextButtonText = "Next";
}
public string NextButtonText
{
get { return this.nextButtonText; }
set
{
this.nextButtonText = value;
this.OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// Raise the PropertyChanged event, passing the name of the property whose value has changed.
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
For more detail please refer Data binding in depth document.
Update
<TextBox Text="{x:Bind Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> doesn't compile at all, as I said the UpdateSourceTrigger property isn't available at all when using a compiled binding.
For my testing, it works well. Compiled Binding is used with the {x:Bind} syntax as opposed to the {Binding} syntax of Classic Binding. It still uses the notifying interfaces (like INotifyPropertyChanged) to watch for changes but {x:Bind} is by default OneTime compared to {Binding} which is OneWay. so you need to declare bind Mode OneWay or TwoWay for {x:Bind}.
Xaml
<StackPanel Orientation="Vertical">
<TextBox Text="{x:Bind Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{x:Bind Title, Mode=OneWay}" /> <!--declare bind mode-->
</StackPanel>
Code behind
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string _title;
public string Title
{
get
{
return _title;
}
set
{
_title = value;
OnPropertyChanged();
}
}

TextBlock binding to user property displays empty text

I've bound many things without issue, but this one isn't working for some reason. I have a base viewmodel that loads a property called User from the login window:
public void LoadUser()
{
if ((LoginState?)Application.Current.Properties["LoginState"] == LoginState.Success)
{
User = new UserModel((string)Application.Current.Properties["UserLName"], (string)Application.Current.Properties["UserFName"], (int)Application.Current.Properties["UserLevel"]);
RaisePropertyChanged("User");
}
}
This part works fine according to the breakpoint values. This property User is in the base of the viewmodel that is attached as the DataContext of my MainWindow. I bind it on the view with this:
<TextBlock Text="{Binding Path=Name, Source=User}"
Grid.Column="1" Grid.Row="1" Foreground="Black"/>
I know the text block works as I can bind other properties to it, but it won't display this property for some reason. Can you see why?
TextBlock's Default Binding MAY not be TwoWay. So Can you Set it to be TwoWay explicitly in xaml and check once.
Also, that RaisePropertyChanged is custom implementation right. Can you step into it during debug and see if the Event is Null?

can't bind to IsEnabled property of ToggleButton using caliburn micro

I'm trying to bind the IsEnabled property of a ToggleButton with no success.
once the NotifyOfPropertyChange is fired, I'm getting the following exception:
Value does not fall within the expected range.
Using a simple Button, the above configurations works as expected.
I wonder if there any workaround for that one?
Thanks
UPDATE:
well it took me a while to pinpoint the problem, but finally managed to understand the behavior:
I've created a simple tester where I use a button to enable/disable a ToggleButton.
when the ToggleButton control does not contain anything, all works properly; however, after adding sub controls to it (in our case I just added a StackPanel) an exception is raised:
Value does not fall within the expected range - right after NotifyOfPropertyChange() is called.
Here is the problematic view I'm using:
<StackPanel>
<ToggleButton x:Name="SayHello" Grid.Column="1" IsEnabled="{Binding HasValue}" Height="190">
<StackPanel x:Name="sp"> </StackPanel>
</ToggleButton>
<Button x:Name="Click"></Button>
</StackPanel>
The ViewModel:
private bool _hasvalue;
public bool HasValue
{
get { return _hasvalue; }
set
{
_hasvalue = value;
NotifyOfPropertyChange(() => HasValue);
}
}
public void Click()
{
HasValue = !HasValue;
}
Any way to workaround that one? - the platforms is WP8.
I couldn't replicate the error from the example above, is there additional information in your ViewModel?
you should also be able to get the effect you want (although I'd still be interested to see the root cause of your error), by using the Caliburn.Micro conventions. Is x:Name=sp causing anything to be bound?
If you have a method SayHello, with a UI element bound to the method via a convention: x:Name="SayHello"
You can create a bool property on your ViewModel called CanSayHello, which Caliburn.Micro will use to Enable/Disable the control; although you will have to call NotifyPropertyChanged when that property changes (so the UI is aware and can update the control).
E.g.
<!-- Your existing Control, Note `IsEnabled` is not bound -->
<ToggleButton x:Name="SayHello" Height="40">
// On your ViewModel
public bool CanSayHello
{
get
{
return HasValue;
}
}
public void Click()
{
HasValue = !HasValue;
NotifyOfPropertyChange(() => CanSayHello);
}
Some additional info.

Metro style windows 7 WPF app -toggleSwitch-

I'm currently fiddling around with the look of one of my older wpf apps using MahApps metro library. I'm stuck with Controls:ToggleSwitch where I can bind almost everything but commands.
When I try to bind a command as below,
<Controls:ToggleSwitch Header="Start Playing" OnLabel="Stop" OffLabel="Play"
IsChecked="{Binding ToggleRecordCommand}"
CommandParameter="{Binding}" />
I get an error like;
Error 62 A TwoWay or OneWayToSource binding cannot work on the read-only property 'ToggleRecordCommand' of type 'RecorderApp.View.MainWindowViewModel'.
Also it tells me there is no CommandParameter. How am I going to bind actions to this?
First of all, as Brendan said, IsChecked property has to be binded with a general Property which has INotifyPropertyChanged, NOT an ICommand type.
In order to bind with Command, the easiest workaround is to use Click(or Checked) event with xaml.cs Code-behind works.
In XAML, as below.
<ToggleButton x:Name="recordButton"
Checked="OnRecordButton_Checked"
IsChecked={Binding IsRecording} />
In Code-behind, as below.
private void OnRecordButton_Checked(object sender, RoutedEventArgs e)
{
if (recordButton.IsChecked.GetValueOrDefault())
{
// Do your own logic to execute command. with-or-without command parameter.
viewModel.ToggleRecordCommand.Execute(null);
}
}
And, In ViewModel (assumption), as below.
// Property for toggle button GUI update
public bool IsRecording{
get{ return _isRecording;}
set{
_isRecording = value;
NotifyPropertyChanged("IsRecording");
}
}
public ICommand ToggleRecordCommand{
// Your command logic.
}
IsChecked is a bool? property and will likely not work if you pass it an ICommand. Source code
If you'd like to see this supported, please raise an issue on the project site and we can discuss it further.

Custom UserControl "IsEnabled" data binding not working

I have a kinda awful problem with my WPF application right now...
I have a custom UserControl used to edit details of a component. It should start by being not enabled, and become enabled as soon as the user chose a component to edit.
The problem is: the IsEnabled property does not even change.
Here is my code:
<my:UcComponentEditor Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsEnabled="{Binding EditorEnabled}"
DataContext="{Binding VmComponent}" />
EditorEnabled is a property in my ViewModel (VmComponent), and is by default false, becomes true when the user chose a component or created one
Just for the record, in my ViewModel:
private Boolean _editorEnabled = false;
public Boolean EditorEnabled
{
get { return _editorEnabled; }
set
{
_editorEnabled = value;
OnPropertyChanged("EditorEnabled");
}
}
When I try to launch my app, the UserControl is starting... enabled.
I added breakpoints everywhere, the EditorEnabled is false from the beginning.
I also did a horribly stupid thing to try to figure out what's happening: I created a converter (so useful -- converting a boolean to boolean -- eh), put a breakpoint on it, and... The code is never reached.
<my:UcComponentEditor Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsEnabled="{Binding EditorEnabled, Converter={StaticResource BoolConverter}}"
DataContext="{Binding VmComponent}" />
That probably means that the property isEnabled is never set, since the converter is never reached.
Do you see any kind of problem there? I started working in WPF about one week ago and therefore I may have missed something essential...
Thank you very much for your time :-)
You should add a DependencyProperty for the binding to work properly. See here for more information.
Code-behind:
public static readonly DependencyProperty EditorEnabledDependencyProperty = DependencyProperty.Register("EditorEnabled", typeof(bool), typeof(UcComponentEditor), new PropertyMetadata(false));
public bool EditorEnabled
{
get { return (bool)base.GetValue(UcComponentEditor.EditorEnabledDependencyProperty); }
set { base.SetValue(UcComponentEditor.EditorEnabledDependencyProperty, value); }
}
The issue I think is that there is a binding on the DataContext property of the user control. Which means the EditorEnabled property should be a property in the VmComponent object. At least that's what my problem was.
To get around it, I specified a proper source to the binding of IsEnabled. Once I did that the control started working as expected.
Hope that helps.
Encapsulating your control in a DockPanel (for example) will remove the need for a DependencyProperty.
You can then simply do your binding with the dockpanel instead of the custom control. Setting the variable bound to IsEnabled on the Dockpanel will automatically enable or disable the items contained in the Dockpanel.

Categories