Grouping related ICommands - c#

I'm new to C# and this is my first WPF project. I am following this tutorial (using their implementation of RelayCommand and attempting to use MVVM. I am implementing a clone of the standard Windows calculator. I would like to find a way to group functionality of similar buttons as what I am doing seems clumsy.
For exmaple, here is my my XAML of three buttons
<Button Content="_7" Focusable ="False" Command="{Binding Seven}" Style="{DynamicResource NumberButton}" Margin="0,134,184,129"/>
<Button Content="_8" Focusable ="False" Command="{Binding Eight}" Style="{DynamicResource NumberButton}" Margin="46,134,138,129"/>
<Button Content="_9" Focusable ="False" Command="{Binding Nine}" Style="{DynamicResource NumberButton}" Margin="92,134,92,129"/>
Here is the ICommands for those:
public ICommand Nine { get { return new RelayCommand(NineExecute); } }
public ICommand Eight { get { return new RelayCommand(EightExecute); } }
public ICommand Seven { get { return new RelayCommand(SevenExecute); } }
and the methods:
void NineExecute()
{
NumberPressed("9");
}
void EightExecute()
{
NumberPressed("8");
}
void SevenExecute()
{
NumberPressed("7");
}
What should I investigate in order to group similar function buttons such as numbers into a single ICommand, with a single method that can determine the sender - while still not putting code behind in the Window class as the article warns against.

Xlam code for a button (supposing that you defined your data context):
<....DataContext>
<loc:Commands/>
</....DataContext>
<Button Content="_9"
Command="{Binding Path=ShowMeABox}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Content}"/>
Our dummy command (using RelayCommand<T> from the provided link):
public class Commands
{
private static readonly ICommand _showShowBoxCommand =
new RelayCommand<string>(str => MessageBox.Show(str));
public static ICommand ShowMeABox { get { return _showShowBoxCommand; } }
}
That's it.
FYI.
It's seems that you explicitly specify button size which is generally a bad practice. To position your buttons use stack or wrap panel, or grid/uniformgrid.
Read info on styles and templates to increase code reuse.
Example:
<UniformGrid Columns="3" Rows="3">
<UniformGrid.DataContext>
<loc:Commands/>
</UniformGrid.DataContext>
<UniformGrid.Resources>
<Style TargetType="Button">
<Setter Property="Command" Value="{Binding ShowMeABox}"/>
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Content}"/>
</Style>
</UniformGrid.Resources>
<Button Content="_1"/>
<Button Content="_2"/>
<Button Content="_3"/>
<Button Content="_4"/>
<Button Content="_5"/>
<Button Content="_6"/>
<Button Content="_7"/>
<Button Content="_8"/>
<Button Content="_9"/>
</UniformGrid>
May be it's possible to bind Enumerable.Range(0,10) to populate control automatically in the MVVM fashion.
Good luck!

Use the CommandParameter property - that way you can bind all of your buttons to the same Command but with different CommandParameter for each number (ie, the CommandParameter should be an integer representing which button as actually pressed)

I would personally write this using a constructor:
public YourType()
{
this.Seven = new RelayCommand( () => NumberPressed("7"));
this.Eight = new RelayCommand( () => NumberPressed("8"));
this.Nine = new RelayCommand( () => NumberPressed("9"));
}
public ICommand Nine { get; private set; }
public ICommand Eight { get; private set; }
public ICommand Seven { get; private set; }
Note that it may also make sense to use the CommandParameter, and a single command. Many frameworks provide a variation on RelayCommand/ActionCommand/etc which accept a command parameter, which you can then specify in XAML. That specific implementation doesn't allow it, but many do, and will map to a method which gets the CommandParameter as a parameter directly.

Related

Wpf button focus is not set when using InputBindings

I have the following code:
<Button Focusable="True">
<Button.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding ButtonClickedCommand}" />
<MouseBinding Gesture="CTRL+LeftClick" Command="{Binding ButtonClickedWithCtrlCommand}" />
</Button.InputBindings>
</Button>
the view model is like this
public ICommand ButtonClickedCommand { get; }
public ICommand ButtonClickedWithCtrlCommand { get; }
...
{
ButtonClickedCommand = new DelegateCommand(() => { });
ButtonClickedWithCtrlCommand = new DelegateCommand(() => { });
}
Now if I click the button, the command is correctly executed. But somehow the focus of the button does not get set. Can someone tell me why?
It seems that the Command prevents the base functionality of the click (like the button down visalization as well). Is there a way to invoke that when using InputBindings?
PS: I know that I can achieve the same with behaviors and "event to command binding" (in fact that's how I fixed it). Maybe I'm abusing the input binding and I should not use it in the first place for "things" which are already bound (like click etc.)?

How to bind a method in my viewmodel to TextChanged?

I'm having a bit of trouble figuring out how to correctly bind my method to the viewmodel.
Here is my current XAML:
<TextBox x:Name="Length" Style="{StaticResource LengthStyle}" Height="Auto" Width="35"
TextChanged="{Binding Validate}" Text="{Binding Length, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}" />
Then in my viewmodel I have a method that looks like this:
public string Validate(Column c){
//unimportant
}
I'm just confused on how I can get this to work. Should I have a setter property that calls this? I would have just set this up as an event in the code behind but the project I'm working on prohibits that. Thanks.
Create a property like the following
private string length;
public string Length
{
get
{
return length;
}
set
{
length = value;
//do whatever you want
}
}
Include these 2 interactivity references :
1. System.Windows.Interactivity
2. Microsoft.Expression.Interactions
Then in your xaml declare this :
xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:interactions="clr-namespace:Microsoft.Expression.Interactivity.Input;assembly=Microsoft.Expression.Interactions"
The xaml for textbox will be like :
<TextBox>
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="TextChanged">
<behaviours:ExecuteCommandAction Command="{Binding Path=DataContext.ValidateCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl}}}"
CommandParameter="PassTheColumnHere"/>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
</TextBox>
You may need to make 2 changes as per your requirement:
1. Instead of Ancestortype "Usercontrol"(if your xaml is not a user control") , then give the file name.
2.Pass the command parameter, in your case the column as you are mentioning.
After that declare the command "ValidateCommand" in your view model:
public ICommand ValidateCommand{ get; private set; }
Inside the constructor initialize it:
ValidateCommand = new DelegateCommand<Column>(Validate);
and the rest logic you can implement in your method:
public void Validate(Column c){
//your logic
}

How do I pass the Button as CommandParameter from XAML in a Xamarin.Forms Page?

I would like to pass a Xamarin.Forms.Button in it's own Command as the CommandParameter to my ViewModel. I know how to achieve this from the code behind e.g. ...
XAML (with most properties missed out for brevity)
<Button x:Name="myButton"
Text="My Button"
Command="{Binding ButtonClickCommand}"/>
XAML.cs
public partial class MyTestPage
{
public MyTestPage()
{
InitializeComponent();
myButton.CommandParameter = myButton;
}
}
ViewModel
public class MyViewModel : ViewModelBase
{
public MyViewModel()
{
ButtonClickCommand = new Command(
(parameter) =>
{
var view = parameter as Xamarin.Forms.Button;
if (view != null)
{
// Do Stuff
}
});
}
public ICommand ButtonClickCommand { get; private set; }
}
... BUT is it possible to declare the CommandParameter in the XAML itself? Or in other words what is the binding syntax to set the parameter to the button itself?
<Button x:Name="myButton"
Text="My Button"
Command="{Binding ButtonClickCommand}"
CommandParameter="{[WHAT WOULD GO HERE]}"/>
btw I've already tried CommandParameter="{Binding RelativeSource={RelativeSource Self}}" and that didn't work.
Thanks,
Xamarin.Forms has a Reference markup extension that does just that:
<Button x:Name="myButton"
Text="My Button"
Command="{Binding ButtonClickCommand}"
CommandParameter="{x:Reference myButton}"/>
Although, this is the first time I'm seeing this need, and you probably can better separate your Views from your ViewModels and solve this by using a cleaner pattern, or by not sharing a command across buttons.
<Button x:Name="myButton"
Text="My Button"
Command="{Binding ButtonClickCommand}"
CommandParameter="{x:Reference myButton}"/>
In your ViewModel
public YourViewModel()
{
ButtonClickCommand= new Command(ButtonClicked);
}
private async void ButtonClicked(object sender)
{
var view = sender as Xamarin.Forms.Button;
}
A simple method would be:
In XAML:
<Button Text="BUTTON-TEST"
Clicked="Avaliar"
CommandParameter="like"/>
In C#:
private void Avaliar(object sender, EventArgs e)
{
Console.WriteLine(((Button)sender).CommandParameter);
}
<Button x:Name="myButton"
Text="My Button"
Command="{Binding ButtonClickCommand}"
CommandParameter={Binding RelativeSource=
{RelativeSource
Mode=FindAncestor,
AncestorType={x:Type Button}}/>
Should work, but im still at a loss why you need the button? The point of MVVM is to seperate Data and UI. everything you should need todo to the button can be done via DataBindings.
If the above doesnt work, the only other thing to try is to give the button an x:Key and CommandParamter = {StaticResource 'x:Key'}

How do I disable a button bound to a current item's ICommand when there is no current item?

Say you have a button whose command property is bound to some ICommand of the current item of some collection.
When the collection is null, the button remains enabled and clicking it seems to be a no-op. I want instead that the button remains disabled. I figured out the following to keep buttons disabled whenever the collection is null. It however seems a bit too convoluted for something that could perhaps be accomplished in a more natural, simpler, and more MVVM like.
Hence the question: is there a simpler way to keep that button disabled, ideally where no code-behind is used?
.xaml:
<Button Content="Do something" >
<Button.Command>
<PriorityBinding>
<Binding Path="Items/DoSomethingCmd" />
<Binding Path="DisabledCmd" />
</PriorityBinding>
</Button.Command>
</Button>
.cs:
public class ViewModel : NotificationObject
{
ObservableCollection<Foo> _items;
public DelegateCommand DisabledCmd { get; private set; }
public ObservableCollection<Foo> Items {
get { return _items; }
set { _items = value; RaisePropertyChanged("Items"); }
}
public ViewModel()
{
DisabledCmd = new DelegateCommand(DoNothing, CantDoAnything);
}
void DoNothing() { }
bool CantDoAnything()
{
return false;
}
}
Edit:
A couple of notes:
I am aware that I can use lambda expressions, but in this example code I do not.
I know what a predicate is.
I don't see how doing something with DoSomethingCmd.CanExecute will do anything to help as there is no DoSomethingCmd to access while there is no current item.
So I'll re-center my question: How can I avoid using the DisabledCmd? I am not interested in moving up the DoSomethingCmd as it is not what I am looking for. I wouldn't be asking this question otherwise.
Another edit:
So I basically adopted this answer as a solution: WPF/MVVM: Disable a Button's state when the ViewModel behind the UserControl is not yet Initialized?
It is, I believe, exactly what hbarck proposes.
I'd do it similar to akjoshi, only I'd use a normal Trigger instead of a DataTrigger, and I'd check on Button.Command to be null. Since it always makes sense to disable a Button that has no Command (especially in MVVM, where there are no click eventhandlers), it would also be a good idea to include this trigger into a default style for Buttons, in order to have this behaviour on all Buttons across the application... I don't see a reason to use a dummy command.
You can create a Trigger to check if the Item (data context of button) is null, and set Button's (or may be it's parent container's as Anton mentioned) IsEnabled property to false, something like this -
<DataTrigger
Binding="{Binding Path=Item}"
Value="{x:Null}">
<Setter Property="Control.IsEnabled" Value="False" />
</DataTrigger>
I am not in position to test it right now, but I think this should work.
Looking at the code in PresentationFramework.dll, I don't see any straightforward way to do it (see ButtonBase.UpdateCanExecute). You might have some luck deriving a class from Button and overriding the metadata for CommandProperty to handle changes yourself. But you can easily avoid having that do-nothing-command code in your viewmodel: create a special converter which will convert a null command to a shared always-disabled fallback ICommand. If you have lots of buttons that need this kind of behavior, an attached property and a style may be in order.
If you look at delegate command, the second parameter is a func that enables you to do exactly this, I am not quite sure, why are you making it so complex.
If you do for example:
DoSomethingCommand = new DelegateCommand(() => SampleAction, () => Items != null);
the button will be disabled when you simply bind its Command property to this command, like so:
<Button Command={Binding DoSomethingCommand} />
The button will then be automatically disabled when the condition in the delegate command becomes false. You also should call DoSomethingCommand.RaiseCanExecuteChanged() when the condition's outcome could change, than the button's IsEnabled updates to reflect the current state.
I used RelayCommands and this has a constructor where you can create a canExecute Predicate and then if it returns false the bound button will be disabled automatically.
On the delegate command you should rewrite the CantDoAnything() method to represent your enable and disable logic. And the binding you should simply bind to the Command.
DelegateCommand constructor on MSDN
DelegateCommand CanExecute BugFix
You can simply bind the button's IsEnabled property to the current item and use a converter.
Here's a complete demo:
<Page.Resources>
<Converters:NullToBoolConverter x:Key="NullToBoolConverter"
IsNullValue="False" IsNotNullValue="True" />
</Page.Resources>
<Page.DataContext>
<Samples:NoCurrentItemViewModel/>
</Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox
IsSynchronizedWithCurrentItem="True" Grid.Row="0"
ItemsSource="{Binding Items}"
DisplayMemberPath="Name"/>
<Button
Grid.Row="1"
Content="Do something"
IsEnabled="{Binding Items/, Converter={StaticResource NullToBoolConverter}}"
Command="{Binding Items/DoSomethingCommand}"/>
<Button Grid.Row="2" Content="Clear" Command="{Binding ClearCommand}"/>
</Grid>
View models - RelayCommand from MVVM Light
public class NoCurrentItemViewModel
{
public NoCurrentItemViewModel()
{
_items = new ObservableCollection<NoCurrentItemDetail>
{
new NoCurrentItemDetail{Name = "one"},
new NoCurrentItemDetail{Name = "two"},
};
ClearCommand = new RelayCommand(Clear);
}
public ICommand ClearCommand { get; private set; }
private void Clear()
{
_items.Clear();
}
private readonly ObservableCollection<NoCurrentItemDetail> _items;
public IEnumerable<NoCurrentItemDetail> Items
{
get { return _items; }
}
}
public class NoCurrentItemDetail
{
public NoCurrentItemDetail()
{
DoSomethingCommand = new RelayCommand(DoSomething);
}
private void DoSomething()
{
Debug.WriteLine("Do something: " + Name);
}
public ICommand DoSomethingCommand { get; private set; }
public string Name { get; set; }
}
The converter
public class NullToBoolConverter : IValueConverter
{
public NullToBoolConverter()
{
IsNullValue = true;
IsNotNullValue = false;
}
public bool IsNullValue { get; set; }
public bool IsNotNullValue { get; set; }
#region Implementation of IValueConverter
public object Convert(object value,
Type targetType, object parameter, CultureInfo culture)
{
return value == null ? IsNullValue : IsNotNullValue;
}
public object ConvertBack(object value,
Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
#endregion
}

How to disable a button if no items are selected in a ListView

I have a ListView Contained in a UserControl I would like to disabled a button when no items are selected in the UserControl, would it be the right way to do it? So far, it doesn't disable, it just stays enable all the way.
I've included the xaml code.
searchAccountUserControl is the UserControl name property in the xaml.
And AccountListView is the ListView name property in the userControl xaml.
<Button Content="Debit" IsEnabled="true" HorizontalAlignment="Left" Margin="18,175,0,0" Name="DebitButton" Width="128" Grid.Column="1" Height="32" VerticalAlignment="Top" Click="DebitButton_Click">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=searchAccountUserControl.AccountListView, Path=SelectedValue}" Value="{x:Null}" >
<Setter Property="Button.IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Thanks.
Finally i've used :
in my ViewModel :
private bool _isSelected;
public bool IsSelected { get { return _isSelected; }
set { _isSelected = _account.View.CurrentItem != null;
PropertyChanged.SetPropertyAndRaiseEvent(this, ref _isSelected, value,
ReflectionUtility.GetPropertyName(() => IsSelected)); } }
And then Use isEnabled = "{Binding Path=IsSelected}" in the xaml.
There are a few things wrong here.
Precedence, if you set IsEnabled on the control itself the style will never be able to change it.
ElementName, it's an ElementName, not a path, just one string that gives the name of one element. Everything beyond that goes into the Path.
Style syntax, if you set a Style.TargetType you should not set the Setter.Property with a type prefix (although leaving it does not break the setter).
By the way, this alone is enough:
<Button IsEnabled="{Binding SelectedItems.Count, ElementName=lv}" ...
It's obvious that you aren't using Commanding (ICommand Interface). You should either use that (and preferably the Model-View-ViewModel architecture).
But, if you want to stick with code-behind and XAML:
<ListView SelectionChanged="AccountListView_SelectionChanged" ... />
private void AccountListView_SelectionChanged(Object sender, SelectionChangedEventArgs args)
{
DebitButton.IsEnabled = (sender != null);
//etc ...
}
More information on MVVM: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
You need to set the DataContext of the View (UserControl) to the instance of the ViewModel you want to use. Then, from there, you can bind to properties on the ViewModel, including ICommands. You can either use RelayCommand (see link above) or use Commanding provided by a framework (for example, Prism provides a DelegateCommand). These commands take an Action (Execute) and a Func (CanExecute). Simply provide the logic in your CanExecute. Of course, you'd also have to have your ListView SelectedItem (or SelectedValue) be databound to a property on your ViewModel so you can check to see if it's null within your CanExecute function.
Assuming you use RelayCommand you don't have to explicitly call the RaiseCanExecuteChanged of an ICommand.
public class MyViewModel : ViewModelBase //Implements INotifyPropertyChanged
{
public MyViewModel()
{
DoSomethingCommand = new RelayCommand(DoSomething, CanDoSomething);
}
public ObservableCollection<Object> MyItems { get; set; }
public Object SelectedItem { get; set; }
public RelayCommand DoSomethingCommand { get; set; }
public void DoSomething() { }
public Boolean CanDoSomething() { return (SelectedItem != null); }
}
<ListView ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedItem}" ... />
<Button Command="{Binding DoSomethingCommand}" ... />

Categories

Resources