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

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.

Related

WPF Control binding does not always update UI

I have a button whose IsEnabled property is bound within my view model to a value indicating whether or not the data in the current view has been modified. This binding has been working fine so far until I'd tried adding Drag/Drop functionality to an ItemsControl within the view.
The Drag/Drop function is working fine and does everything it's supposed to. When it's completed any data manipulation it needs to, it sets the View Model's IsModified property to true. I've verified that the value is actually set to true.
The problem I'm having is that, when the IsModified property is changed from within my Drop method, the button's IsEnabled property isn't updating; when IsModified is set to true during the drag/drop operation, the button remains disabled. If I click the button, it suddenly updates and becomes enabled, requiring me to press the button a second time to actually do as it's intended.
The Drop method is being called on the Drop event on the ItemsControl item. Is this being called from another thread or something that is not informing the UI of the property change? I've tried finding supporting docs, but am having a bit of trouble.
Once again, setting the IsModified property continues to work under any other circumstances and updates the UI properly.
The code in question is fairly simple.
XAML:
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<EventSetter Event="DragEnter" Handler="IcFields_DragEnter"/>
<EventSetter Event="Drop" Handler="IcFields_Drop"/>
</Style>
</ItemsControl.ItemContainerStyle>
...
...
...
<Button Margin="5" Padding="10,0,10,0" IsEnabled="{Binding IsModified}"
Command="{Binding SaveChangesCommand}">Save Changes</Button>
where the IcFields_Drop handler allows the changes to be made to the model and, in the end, sets IsModified to true.
Edit:
Here's an abbreviated example of the implementation. Use the XAML above as the XAML for this example.
C# - Code-Behind
private void IcFields_Drop(object sender, DragEventArgs e)
{
ViewModel.Drop();
}
C# - View Model
private bool isModified;
public bool IsModified
{
get { return isModified; }
set { SetProperty(ref isModified, value); }
}
public void Drop()
{
//PSEUDO: Do some drag/drop logic for the items attached to the ItemsControl.
...
...
IsModified = true;
}
Instead of binding the IsEnabled property, you should return a bool from the CanExecute method of your command to indicate whether the Button should be enabled. You would then call a method that raises the CanExecuteChanged event of the command in your Drop() method.
Most ICommand implementations include a RaiseCanExecuteChanged() method or similar that you can call to refresh the status of the command:
public void Drop()
{
...
SaveChangesCommand.RaiseCanExecuteChanged();
}

Image IsEnabled with Binding Property Does not work Xamarin Forms

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

Easy way to cascade binding updates

Just hit a snag in my MVVM application that's made me wonder if I'm doing MVVM "right", or if I've missed a trick somewhere. Consider a situation like this on the viewmodel:
private _bookings ObservableCollection<Booking>;
public ObservableCollection<Booking> Bookings
{
get { return _bookings; }
set
{
_bookings = value;
OnPropertyChanged("Bookings");
}
}
public int Requested
{
get
{
return (int)Bookings.Sum(g => g.Requested);
}
}
public int Available
{
get
{
return (int)Bookings.Sum(g => g.Volume);
}
}
And then in a datagrid on the view:
<DataGrid DataContext="{Binding Bookings, Mode=TwoWay}" ItemsSource="{Binding SourceCollection}">
<!-- various datagrid related gubbins -->
</DataGrid>
<Label BorderThickness="1" Content="{Binding Requested}"></Label>
<Label BorderThickness="1" Content="{Binding Available}"></Label>
Now, whenever something in the ViewModel changes the contents of the Bookings collection, the contents of my grid will auto-update which is great.
But the Active and Requested properties which are calculated on the contents of that collection don't auto update, and can't because they're read-only. Even if I made each one two way and raised a changed event for them both (which I'm reluctant to do, because in the real-world version there are a lot of these dependent properties), the code chokes on execution because you can't make a two-way binding to a read-only property.
Is there any straightforward way round this problem, ideally to cascade changes to dependent properties and have them auto-update in the view?
Like #dellywheel mentioned in his answer that you need to cascade property changes if you want controls bound to these properties to get change notification.
However, if you are dealing with small class and can afford property changes, you can raise PropertyChanged event with empty string so all properties in class will pass on property change notification to UI. That ways you won't have to raise individual property change events for each properties.
OnPropertyChanged("");
You need to raise the OnPropertyChanged after Bookings collection changes (or wherever you need to in your code).
private _bookings ObservableCollection<Booking>;
public ObservableCollection<Booking> Bookings
{
get { return _bookings; }
set
{
_bookings = value;
OnPropertyChanged("Bookings");
OnPropertyChanged("Requested");
OnPropertyChanged("Available");
}
}

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