MVVM: Change button content and command binding at runtime - wpf

I have a button which should be used as Connect or Disconnect button, depending on the Connected-property of the ViewModel:
<Button Content="_Connect" x:Name="connectButton" Command="{Binding ConnectCommand}"/>
Now depending on the property, the content should be either "_Connect" or "_Disconnect" and the command binding should go either to ConnectCommand or DisconnectCommand.
Is there a nice way of doing that or should I use a command for both and have a DataTrigger to set the content separately depending on the Connected property?
Thanks a lot!

Using same command you can handle this with enum.
<Button Name="btnOption"
Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="8"
Command="{Binding RxOptionCommand}"
Content="{Binding RxOptionContent}" />
Based on your View Model's connect or disconnect business change the button content and at the same time set your enum value in to a variable.
public enum EnumRxRecStatus
{
None = 0,
New = 1,
}
Now you can check the condition in to your command event
public void OnRxOptionCommand(object sender)
{
if (RequestForRxOption == EnumRxRecStatus.None)
{
// Do something
}
else if (RequestForRxOption == EnumRxRecStatus.New)
{
// Do something
}
}

It is indeed very simple:
As Clemens suggested, I ended up setting the Binding and the Contents with a DataTrigger.

Related

databinding and focus coordination

I have several controls including a DataGrid that I want to be disabled until there is a valid value in the first TextBox in the presentation. So I added a boolean property to bind to in the VM and bind to it in the xaml (below).
The binding works, but has the side effect of 'trapping' the user in the TextBox (MoneyToAllocate).
Presumably this is because the TB binding is LostFocus and there is no place for the focus to go and actually trigger the updates. What's a good way to fix this?
Cheers,
Berryl
ViewModel
public bool HasMoneyToAllocate { get { return MoneyToAllocate.Amount > 0; } }
public Money MoneyToAllocate {
get { return _moneyToAllocate; }
set {
if (value.Amount < 0) return;
_moneyToAllocate = new Money(value.Amount, SelectedCurrency);
NotifyPropertyChanged(() => HasMoneyToAllocate);
}
}
View
<TextBox Text="{Binding MoneyToAllocate, Converter={StaticResource moneyConverter}}" />
<DataGrid IsEnabled="{Binding HasMoneyToAllocate}" ...
EDIT
I should have added that I tried PropertyChanged for update but it gets a bit messy since the value of the text box needs to be formatted by the converter. Any other ideas?
FINAL EDIT
I wound up letting another control that previously wasn't a tab stop be a tab stop, so the text box had a place to go. Phil understood the problem best and gets the answer, even though the range of values the user can input (.001 to decimal.MaxValue) make an up-down impractical.
Use UpdateSourceTrigger=PropertyChanged
<TextBox
Text="{Binding MoneyToAllocate, UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource moneyConverter}}" />
Then you have to use UpdateSourceTrigger=PropertyChanged
- if you use that binding you are using the value in the VM will not effected till the focus moves from the textBox
- but if you add UpdateSourceTrigger=PropertyChanged to your binding the VM property (MoneyToAllocate) will effected immediately (when the textBox.Text value changed)

WPF CommandParameter binding to PasswordBox.Password

I have a MVVM run treeview. On the top level is an Account object that contains credentials. I have a PasswordBox that can be used to change the account password with a Save button right behind it. The code is as follows and is part of the Account level template:
PasswordBox Width="100" x:Name="pbPassword"/>
Button x:Name="btnSave" Command="{Binding ClickCommand}" CommandParameter="{Binding ElementName=pbPassword, Path=Password}" Height="20" Width="50">Save
I put something into the PasswordBox and then click Save. The ClickCommand fires, but the parameter is always string.Empty. What am I missing?
For security reasons, WPF doesn't provide a dependency property for the Password property of PasswordBox (reference 1, 2), so your command parameter binding doesn't work.
You could bind the command argument to PasswordBox, then access the appropriate property from within your command implementation:
<Button Command="{Binding ClickCommand}" CommandParameter="{Binding ElementName=pbPassword}">
// command implementation
public void Execute(object parameter)
{
var passwordBox = (PasswordBox)parameter;
var value = passwordBox.Password;
}
You may want to consider other options that do not involve keeping the password in memory as plain text.
Hope this helps,
Ben
--PLEASE STOP MARKING ME DOWN ON THIS, See comment below, this does not work, but left in so no-one else makes the same mistake--
Sorry old Q, but found an improvement on that
<Button Content="Log On"
Command="{Binding LogOnCommand}"
CommandParameter="{Binding ElementName=PasswordControl, Path=SecurePassword}" />
that way your command need only know about a SecureString object
public override void Execute(object parameter)
{
_viewModel.LogOnAsync((SecureString)parameter);
}
keeping knowledge of the ui from the command

Change ToggleButton/RadioButton state only on external event

I want to present several ToggleButton/RadioButton elements that:
Map to an enumeration, meaning the DataContext has a "public Mode CurrentMode" property.
Are mutually exclusive (only one button is checked)
When a button is clicked, the state doesn't change immediately. Instead, a request is sent to a server. The state changes when the response arrives.
Have a different image for checked/unchecked state
For example, 4 buttons would display the following view-model:
public class ViewModel
{
public enum Mode { Idle, Active, Disabled, Running }
Mode m_currentMode = Mode.Idle;
public Mode CurrentMode
{
get { return m_currentMode; }
set
{
SendRequest(value);
}
}
// Called externally after SendRequest, not from UI
public void ModeChanged(Mode mode)
{
m_currentMode = mode;
NotifyPropertyChanged("CurrentMode");
}
}
My initial approach was to use the solution from How to bind RadioButtons to an enum?, but that is not enough since the button state change immediately, even if I don't call NotifyPropertyChanged in the setter. In addition, I don't like the "GroupName" hack.
Any ideas? I don't mind creating a custom button class, as I need many buttons like that for multiple views.
I'm using .NET 3.5 SP1 and VS2008.
If you want to use the RadioButtons you just need to make some minor tweaks to workaround the default behavior of the RadioButton.
The first issue you need to workaround is the automatic grouping of RadioButtons based on their common immediate parent container. Since you don't like the "GroupName" hack your other option is to put each RadioButton inside of its own Grid or other container. This will make each button a member of its own group and will force them to behave based on their IsChecked binding.
<StackPanel Orientation="Horizontal">
<Grid>
<RadioButton IsChecked="{Binding Path=CurrentMode, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Idle}">Idle</RadioButton>
</Grid>
<Grid>
<RadioButton IsChecked="{Binding Path=CurrentMode, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Active}">Active</RadioButton>
</Grid>
<Grid>
<RadioButton IsChecked="{Binding Path=CurrentMode, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Disabled}">Disabled</RadioButton>
</Grid>
<Grid>
<RadioButton IsChecked="{Binding Path=CurrentMode, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Running}">Running</RadioButton>
</Grid>
</StackPanel>
This brings me to the next workaround which is ensuring the button clicked on doesn't stay in its Checked state after clicking on it which was needed in order to trigger the set call because you are binding on the IsChecked property. You will need to send out an additional NotifyPropertyChanged, but it must be pushed into the queue of the Dispatch thread so the button will receive the notification and update its visual IsChecked binding. Add this to your ViewModel class, which is probably replacing your existing NotifyPropertyChanged implementation and I am assuming your class is implementing the INotifyPropertyChanged which is missing in the question's code:
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
Dispatcher uiDispatcher = Application.Current != null ? Application.Current.Dispatcher : null;
if (uiDispatcher != null)
{
uiDispatcher.BeginInvoke(DispatcherPriority.DataBind,
(ThreadStart)delegate()
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
});
}
}
}
Then in your CurrentMode's Setter call NotifyPropertyChanged("CurrentMode"). You probably already needed something like this since your Server's ModeChanged call is probably coming in on a thread that isn't the Dispatcher thread.
Finally you will need to apply a Style to your RadioButtons if you want them to have a different Checked/Unchecked look. A quick Google search for WPF RadioButton ControlTemplate eventually came up with this site: http://madprops.org/blog/wpf-killed-the-radiobutton-star/.

XAML ReadOnly ComboBox

To set up a ReadOnly ComboBox in XAML (WPF), you have to set up a ComboBox and a TextBox showing only one of them according to a pair of properties IsReadOnly/IsEditable that must exist on your ViewModel. Note that on this sample "UserNVL" must exist in the resources and it should be a NameValueList collection that allows us to convert ID to names. In this case the RecipientID is the key for a user name. Note also the VisibilityConverter must also exist in the resources and it's a standard BooleanToVisibilityConverter.
Gosh! This was so hard to find I had to made it myself. This allows the user the select the content of the text box. No way a disabled ComboBox would ever allow you to do it.
There are two properties named IsHitTestVisible & IsTabVisible. the former makes the control deaf to mouse events and the latter to keyboard events.
This could help you as it would not give the disabled look to your combo box but you will succeed in making a read only combo box..
Source :-
http://www.telerik.com/community/forums/wpf/combobox/isreadonly-does-seem-to-work.aspx
Why not just set IsEnabled=false?
<DockPanel>
<TextBlock Text="Recipient" Margin="6,9,3,6" HorizontalAlignment="Right"/>
<ComboBox
x:Name="RecipientID"
ItemsSource="{Binding Source={StaticResource UserNVL}}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding Path=RecipientID}"
Height="20"
Margin="6,6,0,6"
MinWidth="200"
HorizontalAlignment="Left"
IsEditable ="True"
Visibility="{Binding Path=IsEditable, Converter={StaticResource VisibilityConverter}}"/>
<TextBox
x:Name="RecipientName"
Text="{Binding ElementName=RecipientID, Path=Text}"
Margin="6,6,0,6"
MinWidth="200"
HorizontalAlignment="Left"
Style="{StaticResource textBoxInError}"
Visibility="{Binding Path=IsReadOnly, Converter={StaticResource VisibilityConverter}}"/>
</DockPanel>
I think that you will find it much easier and practical to create a class to extend the ComboBox class in this very simple manner:
override the OnSelectionChanged method of the Combobox to check the property IsReadOnly before to allow base.OnSelectionChanged(e) to run.
That way you just have to set ComboBox.IsReadOnly property to True. No big XAML to write everywhere...
Here is a custom ComboBox subclass that gives the read only behaviour I needed for my scenario:
public class ReadOnlyComboBox : ComboBox
{
static ReadOnlyComboBox()
{
IsDropDownOpenProperty.OverrideMetadata(typeof(ReadOnlyComboBox), new FrameworkPropertyMetadata(
propertyChangedCallback: delegate { },
coerceValueCallback: (d, value) =>
{
if (((ReadOnlyComboBox)d).IsReadOnly)
{
// Prohibit opening the drop down when read only.
return false;
}
return value;
}));
IsReadOnlyProperty.OverrideMetadata(typeof(ReadOnlyComboBox), new FrameworkPropertyMetadata(
propertyChangedCallback: (d, e) =>
{
// When setting "read only" to false, close the drop down.
if (e.NewValue is true)
{
((ReadOnlyComboBox)d).IsDropDownOpen = false;
}
}));
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (IsReadOnly)
{
// Disallow changing the selection when read only.
e.Handled = true;
return;
}
base.OnSelectionChanged(e);
}
}
Points about this approach:
Doesn't break any existing styles applied to the element, unlike an approach that introduces additional UI elements.
Doesn't break input focus while read only. You can still tab into and click to focus this element. This is more accessible, which is a concern in my scenario.
The UI element doesn't, but default, look any different when read only. If you need that, you would have to apply relevant styles to make it so.
If IsEnabled is set to false, Combobox value is nearly not readable. What I found as suitable solution is:
combobox and textbox (formated as readonly) are in the same grid position
combobox spans to next column to gain additional 15 width so dropdown button is visible
textbox.IsVisible is bound to combobox.IsEnabled with bool to visibility converter.
textbox.Text is bound to combobox.SelectedItem (in my case it is strongly typed so I actually bound into .DisplayText of it)

Enable button based on TextBox value (WPF)

This is MVVM application. There is a window and related view model class.
There is TextBox, Button and ListBox on form. Button is bound to DelegateCommand that has CanExecute function. Idea is that user enters some data in text box, presses button and data is appended to list box.
I would like to enable command (and button) when user enters correct data in TextBox. Things work like this now:
CanExecute() method contains code that checks if data in property bound to text box is correct.
Text box is bound to property in view model
UpdateSourceTrigger is set to PropertyChanged and property in view model is updated after each key user presses.
Problem is that CanExecute() does not fire when user enters data in text box. It doesn't fire even when text box lose focus.
How could I make this work?
Edit:
Re Yanko's comment:
Delegate command is implemented in MVVM toolkit template and when you create new MVVM project, there is Delegate command in solution. As much as I saw in Prism videos this should be the same class (or at least very similar).
Here is XAML snippet:
...
<UserControl.Resources>
<views:CommandReference x:Key="AddObjectCommandReference"
Command="{Binding AddObjectCommand}" />
</UserControl.Resources>
...
<TextBox Text="{Binding ObjectName, UpdateSourceTrigger=PropertyChanged}"> </TextBox>
<Button Command="{StaticResource AddObjectCommandReference}">Add</Button>
...
View model:
// Property bound to textbox
public string ObjectName
{
get { return objectName; }
set {
objectName = value;
OnPropertyChanged("ObjectName");
}
}
// Command bound to button
public ICommand AddObjectCommand
{
get
{
if (addObjectCommand == null)
{
addObjectCommand = new DelegateCommand(AddObject, CanAddObject);
}
return addObjectCommand;
}
}
private void AddObject()
{
if (ObjectName == null || ObjectName.Length == 0)
return;
objectNames.AddSourceFile(ObjectName);
OnPropertyChanged("ObjectNames"); // refresh listbox
}
private bool CanAddObject()
{
return ObjectName != null && ObjectName.Length > 0;
}
As I wrote in the first part of question, following things work:
property setter for ObjectName is triggered on every keypress in textbox
if I put return true; in CanAddObject(), command is active (button to)
It looks to me that binding is correct.
Thing that I don't know is how to make CanExecute() fire in setter of ObjectName property from above code.
Re Ben's and Abe's answers:
CanExecuteChanged() is event handler and compiler complains:
The event
'System.Windows.Input.ICommand.CanExecuteChanged'
can only appear on the left hand side
of += or -=
there are only two more members of ICommand: Execute() and CanExecute()
Do you have some example that shows how can I make command call CanExecute().
I found command manager helper class in DelegateCommand.cs and I'll look into it, maybe there is some mechanism that could help.
Anyway, idea that in order to activate command based on user input, one needs to "nudge" command object in property setter code looks clumsy. It will introduce dependencies and one of big points of MVVM is reducing them.
Edit 2:
I tried to activate CanExecute by calling addObjectCommand.RaiseCanExecuteChanged() to ObjectName property setter from above code. This does not help either. CanExecute() is fired few times when form is initialized, but after that it never gets executed again. This is the code:
// Property bound to textbox
public string ObjectName
{
get { return objectName; }
set {
objectName = value;
addObjectCommand.RaiseCanExecuteChanged();
OnPropertyChanged("ObjectName");
}
}
Edit 3: Solution
As Yanko Yankov and JerKimball wrote, problem is static resource. When I changed button binding like Yanko suggested:
<Button Command="{Binding AddObjectCommand}">Add</Button>
things started to work immediately. I don't even need RaiseCanExecuteChanged(). Now CanExecute fires automatically.
Why did I use static resource in first place?
Original code was from WPF MVVM toolkit manual. Example in that manual defines commands as static resource and then binds it to menu item. Difference is that instead of string property in my example, MVVM manual works with ObservableCollection.
Edit 4: Final explanation
I finally got it. All I needed to do was to read comment in CommandReference class. It says:
/// <summary>
/// This class facilitates associating a key binding in XAML markup to a command
/// defined in a View Model by exposing a Command dependency property.
/// The class derives from Freezable to work around a limitation in WPF when
/// databinding from XAML.
/// </summary>
So, CommandReference is used for KeyBinding, it is not for binding in visual elements. In above code, command references defined in resources would work for KeyBinding, which I don't have on this user control.
Of course, sample code that came with WPF MVVM toolkit were correct, but I misread it and used CommandReference in visual elements binding.
This WPF MVVM really is tricky sometimes.
Things look much clearer now with the edits, thanks! This might be a stupid question (I'm somewhat tired of a long day's work), but why don't you bind to the command directly, instead of through a static resource?
<Button Command="{Binding AddObjectCommand}">Add</Button>
Since you are using the DelegateCommand, you can call it's RaiseCanExecuteChanged method when your text property changes. I'm not sure what you are trying to accomplish with your CommandReference resource, but typically you just bind the commands directly to the button element's Command property:
<TextBox Text="{Binding ObjectName, UpdateSourceTrigger=ValueChanged}" />
<Button Command="{Binding AddObjectCommand}" Content="Add" />
This would be the relevant portion of your view model:
public string ObjectName
{
get { return objectName; }
set
{
if (value == objectName) return;
value = objectName;
AddObjectCommand.RaiseCanExecuteChanged();
OnPropertyChanged("ObjectName");
}
}
Try raising CanExecuteChanged when your property changes. The command binding is really distinct from the property binding and buttons bound to commands are alerted to a change in status by the CanExecuteChanged event.
In your case, you could fire a check when you do the PropertyChanged on the bound property that would evaluate it and set the command's internal CanExecute flag and then raise CanExecuteChanged. More of a "push" into the ICommand object than a "pull".
Echoing Abe here, but the "right" path to take here is using:
public void RaiseCanExecuteChanged();
exposed on DelegateCommand. As far as dependencies go, I don't think you're really doing anything "bad" by raising this when the property that the command depends on changes within the ViewModel. In that case, the coupling is more or less contained wholly within the ViewModel.
So, taking your above example, in your setter for "ObjectName", you would call RaiseCanExecuteChanged on the command "AddObjectCommand".
I know this is an old question but I personally think it's easier to bind the textbox Length to button's IsEnabled property, e.g.:
<TextBox Name="txtbox" Width="100" Height="30"/>
<Button Content="SomeButton " Width="100" Height="30"
IsEnabled="{Binding ElementName=txtbox, Path=Text.Length, Mode=OneWay}"></Button>
If ElementName binding does not work, use:
<Entry x:Name="Number1" Text="{Binding Number1Text}" Keyboard="Numeric"></Entry>
<Entry x:Name="Number2" Text="{Binding Number2Text}" Keyboard="Numeric"></Entry>
<Button Text="Calculate" x:Name="btnCalculate" Command="{Binding CalculateCommand}" IsEnabled="{Binding Source={x:Reference Number1, Number2}, Path=Text.Length, Mode=OneWay}"></Button>
or use:
<Entry x:Name="Number1" Text="{Binding Number1Text}" Placeholder="Number 1" Keyboard="Numeric"></Entry>
<Entry x:Name="Number2" Text="{Binding Number2Text}" Placeholder="Number 2" Keyboard="Numeric"></Entry>
<Button VerticalOptions="Center" Text="Calculate" x:Name="btnCalculate" Command="{Binding CalculateCommand}">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding Source={x:Reference Number1, Number2},
Path=Text.Length}"
Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Button.Triggers>

Resources