I have ComboBox inside StackPanel. I am using MVVM and try to bind 'GotFocus' event Command to Command in ViewModel but when I Click on 'ComboBox', it don't work (It don't call Command in ViewModel) but if I move that 'ComboBox' out of 'StackPanel' it's working properly.
How can I fire event from 'CombBox' inside 'StackPanel' in MVVM?
<StackPanel x:Name="StackPanel" Grid.Column="2" Grid.Row="6">
<ComboBox x:Name="ComboBox" ItemsSource="{Binding Values}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<cmd:EventToCommand Command="{Binding Path=GotFocusCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</StackPanel>
ViewModel's code is:
public ViewModelCommand GotFocusCommand { get; set; }
////Change your tag from EventToCommand to InvokeCommandAction
<StackPanel x:Name="StackPanel" Grid.Column="2" Grid.Row="6">
<ComboBox x:Name="ComboBox" ItemsSource="{Binding Values}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<cmd:InvokeCommandAction="{Binding Path=GotFocusCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
////Then, I use my commands this way in my view model:
private ICommand _GotFocusCommand;
public ICommand GotFocusCommand
{
get
{
if (_GotFocusCommand == null)
{
_GotFocusCommand =
new RelayCommand(
param => GotFocusCommand_Executed(),
GotFocusCommand_CanExecute
);
}
return _GotFocusCommand;
}
}
////RelayCommandClass.cs:
public class RelayCommand : ICommand
{
private Action _handler;
public RelayCommand(Action handler)
{
_handler = handler;
}
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
if (value != _isEnabled)
{
_isEnabled = value;
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_handler();
}
}
////Finally, you can create an event in your view model:
private void GotFocusCommand_Executed()
{
//DoSomething here
}
private bool GotFocusCommand_CanExecute()
{
return true;
}
Related
So I have this object:
public class Test : INotifyPropertyChanged
{
public string Name { get; set; }
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
public Test(string name, string path, bool selected)
{
Name = name;
Path = path;
isSelected = selected;
}
public override string ToString()
{
return Name;
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
So I have ListView bind with my object (Test) and when the user click on ListViewItem I want to change my IsSelected property from true to false (or Or vice versa...)
And MouseLeftButtonUp:
<ListView Name="listViewTests" ItemsSource="{Binding Tests}">
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding MouseLeftButtonUpCommand}"
CommandParameter="{Binding ElementName=listViewTests, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
My Execute Command:
public void Execute(object parameter)
{
Test test = parameter as Test;
if (test != null)
{
}
}
So instead of change my object property inside this Execute method i wonder how to do that in XAML
You can setup the ItemContainerStyle and bind the ListBoxItem.IsSelected property to your data model Test.IsSelected:
<ListView ItemsSource="{Binding Tests}">
<ListView.ItemContainerStyle>
<!-- The DataContext of this Style is the ListBoxItem.Content (a 'Test' instance) -->
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
I'm not sure if this is possible but I'm looking for a way to bind a button to a generic class that contain all the properties i will need to use. Every button needs a relay command so that would be included but all of our buttons will need to bind visibility and being enabled. Instead of having this group of properties and relay command for every button we will use within the given windows view model I was wondering if there was a way to have the button bind to a class then in our view model we reference a new instance of that class for each button needed and then be just be able to set the properties on that class to the values we need. I hope this makes sense.
There's probably a bunch of different ways to do something like this. I don't know if I'd choose to have a class instance for each button. But here's a rough/quick/dodgy example of a solution.
The main model for the form is providing the button models by way of a list. The individual button models then handle the button bindings.
EDIT: Extended the code a bit. Now includes command bindings. Also shows use of ItemsControl as suggested by #Xavier. Hope it helps.
MainWindow.xaml:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400">
<StackPanel>
<!-- Known buttons -->
<StackPanel Margin="20">
<Button DataContext="{Binding ButtonModels[0], Mode=OneTime}" Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
<Button DataContext="{Binding ButtonModels[1], Mode=OneTime}" Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
<Button DataContext="{Binding ButtonModels[2], Mode=OneTime}" Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
</StackPanel>
<!-- Dynamic buttons -->
<StackPanel Margin="20">
<ItemsControl ItemsSource="{Binding ButtonModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel>
</Window>
MainWindow.xaml.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Model();
}
}
public class Model
{
private Random rnd = new Random();
public List<ButtonModel> ButtonModels { get; private set; }
public Model()
{
this.ButtonModels = new List<ButtonModel>();
for (int i = 0; i < 5; i++)
{
this.ButtonModels.Add(new ButtonModel
{
LabelText = "Button " + (i + 1),
Command = new RelayCommand((index) => { this.ChangeColour((int)index); }),
CommandParameter = i
});
}
}
private void ChangeColour(int index)
{
this.ButtonModels[index].Colour = new SolidColorBrush(Color.FromRgb((byte)rnd.Next(50, 256), (byte)rnd.Next(50, 256), (byte)rnd.Next(50, 256)));
}
}
public class ButtonModel : ObservableObject
{
private string _LabelText;
public string LabelText { get => _LabelText; set => this.SetProperty(ref _LabelText, value); }
private Brush _Colour = new SolidColorBrush(Color.FromRgb(205, 205, 205));
public Brush Colour { get => _Colour; set => this.SetProperty(ref _Colour, value); }
private RelayCommand _Command;
public RelayCommand Command { get => _Command; set => this.SetProperty(ref _Command, value); }
private int _CommandParameter;
public int CommandParameter { get => _CommandParameter; set => this.SetProperty(ref _CommandParameter, value); }
}
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (field == null && value == null)
{
return false;
}
if (field == null || !field.Equals(value))
{
field = value;
this.RaisePropertyChangedEvent(propertyName);
return true;
}
return false;
}
protected void RaisePropertyChangedEvent(string propertyName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class RelayCommand : ICommand
{
private Action<object> execute;
private Predicate<object> canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<object> action, Predicate<object> canExecute = null)
{
this.execute = action;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
}
Pressing the delete key is not causing the command below to fire. One can assume DelegateCommand follows interface contracts since other instances of DelegateCommand referenced in the View are properly firing within the ViewModel.
Why?
View:
<ListBox Height="132"
Name="lbFiles"
ItemsSource="{Binding LbItems, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"
VerticalAlignment="Center"
SelectedValue="{Binding Path=ListBoxSelection}">
<ListBox.InputBindings>
<KeyBinding Key="Delete"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}
,Path=DataContext.CommandInputFilesDeleteSelected}" />
</ListBox.InputBindings>
ViewModel:
private DelegateCommand commandInputFilesDeleteSelected;
public ICommand CommandInputFilesDeleteSelected {
get {
if (commandInputFilesDeleteSelected == null) {
commandInputFilesDeleteSelected = new DelegateCommand(InputFilesDeleteSelected);
}
return saveCommand;
}
}
private void InputFilesDeleteSelected() { //this never fires :(
LbItems.Remove(ListBoxSelection);
}
private DelegateCommand commandInputFilesDeleteSelected;
public ICommand CommandInputFilesDeleteSelected {
get {
if (commandInputFilesDeleteSelected == null) {
commandInputFilesDeleteSelected = new DelegateCommand(InputFilesDeleteSelected);
}
return saveCommand; //changed from saveCommand to commandInputFilesDeleteSelected whoops
}
}
I'm using the MVVM Light Toolkit with Silverlight.
On my UserControl I have a ListBox that displays a list of files. Each file has a delete image next to the file name. In the DataTemplate for the listbox I have an image (or can use a button) and a TextBlock.
So I want to capture using the event when the user will clicks on the image(or button with image) to remove the file from the list of files.
But I cannot seem to capture the event. Maybe this is due to having the SelectedItem Event on the listbox?
public class MainViewModel : ViewModelBase
{
#region Properties
public const string SelectedListBoxFilePropertyName = "SelectedUploadFile";
private UploadFile _selectedUploadFile = null;
public UploadFile SelectedUploadFile
{
get
{
return _selectedUploadFile;
}
set
{
if (_selectedUploadFile == value)
return;
_selectedUploadFile = value;
RaisePropertyChanged(SelectedListBoxFilePropertyName);
}
}
public const string UploadFilesPropertyName = "UploadFiles";
private ObservableCollection<UploadFile> _uploadFiles = new ObservableCollection<UploadFile>();
public ObservableCollection<UploadFile> UploadFiles
{
get
{
return _uploadFiles;
}
set
{
if (_uploadFiles == value)
return;
_uploadFiles = value;
RaisePropertyChanged(UploadFilesPropertyName);
}
}
#endregion
public static ICommand BrowseCommand { get; private set; }
public static ICommand DragDropFileCommand { get; private set; }
public static ICommand RemoveCommand { get; private set; }
#region Constructor
public MainViewModel()
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
UploadFiles = new UploadFileContainer().UploadFiles;
}
else
{
// Code runs "for real"
}
WireUpCommands();
}
#endregion
#region Event Handlers
private void OnBrowseFileCommand()
{
var dialog = new OpenFileDialog();
dialog.ShowDialog();
if (dialog.Files != null)
AddFiles(dialog.Files);
}
private void OnDropFileCommand(DragEventArgs e)
{
var files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];
AddFiles(files);
}
private void OnRemoveFileCommand()
{
UploadFiles.Remove(_selectedUploadFile);
}
#endregion
#region Private Methods
private void WireUpCommands()
{
BrowseCommand = new RelayCommand(OnBrowseFileCommand);
DragDropFileCommand = new RelayCommand<DragEventArgs>(e => OnDropFileCommand(e));
RemoveCommand = new RelayCommand(OnRemoveFileCommand);
UploadCommand = new RelayCommand(OnClickUploadCommand);
}
#endregion
}
<ListBox Grid.Row="1" Height="214" HorizontalAlignment="Left" AllowDrop="True" Margin="6,26,0,0" Name="UploadFilesListBox" VerticalAlignment="Top" Width="415" ItemsSource="{Binding Path=UploadFiles}" SelectedItem="{Binding Path=SelectedListBoxFile, Mode=TwoWay}" ScrollViewer.VerticalScrollBarVisibility="Auto">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<cmd:EventToCommand Command="{Binding DragDropFileCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.Background>
<ImageBrush ImageSource="/FileUploadApplication;component/Resources/dragdrophere.png" Stretch="None" />
</ListBox.Background>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding RemoveCommand}">
<Image Source="/FileUploadApplication;component/Resources/delete.png"/>
</Button>
<Image Source="/FileUploadApplication;component/Resources/delete.png">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding RemoveCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Image> <TextBlock Text=" " />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Since your ItemsSource is UploadFiles it's probably sending the event to UploadFile and not the view model the user control is bound to.
Your button is the element of the ItemTemplate. you're binding the listbox ItemsSource to the ObservableCollection. Every Itemtemplate DataContext is no MainViewModel, but UploadFile, which has no RemoveCommand.
I was solving this by adding to every item the parent object using constructor. RemoveCommand was inside the item's ViewModel and insede the remove function i was calling the parent's method to delete the item.
Not sure if that's the best solution but it worked for me.
I have list of checkboxes on a window specifying some items to be ordered. I need to first disable the Order button when the windows loads and enable it after selecting/check some items(checkboxes) and vice versa. I have bind the IsChecked property of the checkbox.
Edit Import from OP comment:-
I have only one checkbox in the ItemsControl. and I have bind the ItemsControl's ItemsSource to List. that way we can show multiple checkboxes as per the items in the List.
Here is the code:
<ItemsControl ItemsSource="{Binding FavoriteItems}" Margin="80,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel>
<Grid>
<CheckBox IsChecked="{Binding IsHouseholdSelected}" Content="{Binding SubCategoryName}" Grid.ColumnSpan="1" FontFamily="Calibri" FontWeight="Bold" />
</Grid>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
Below is a sample code that could help you out. Basically, the key here is I had the Items in the list implicitly notify its parent ViewModel's Command object to raise the CanExecuteChanged event every time the IsChecked property changes. (Also, I'm using "DelegateCommand" here, which is just the same as "RelayCommand").
ViewModels:
public class ViewModel : INotifyPropertyChanged
{
public DelegateCommand MyCommand { get; set; }
private ObservableCollection<ItemViewModel> items = new ObservableCollection<ItemViewModel>();
public ObservableCollection<Item> Items
{
get { return this.items; }
}
public ViewModel()
{
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 1" });
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 2" });
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 3" });
this.MyCommand = new DelegateCommand(this.CanExecute, this.Execute);
}
public void Execute(object parameter)
{
MessageBox.Show("Executed");
}
public bool CanExecute(object parameter)
{
return (this.items.Count == this.items.Count((x) => x.IsChecked));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
}
public class ItemViewModel
{
private ViewModel parent;
private bool isChecked;
public string Text { get; set; }
public bool IsChecked
{
get { return this.isChecked; }
set
{
this.isChecked = value;
if (this.parent.MyCommand != null)
this.parent.MyCommand.OnCanExecuteChanged(null);
}
}
public Item(ViewModel parent)
{
this.parent = parent;
}
}
View:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<DockPanel>
<Button DockPanel.Dock="Bottom" Command="{Binding MyCommand}">Test</Button>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Text}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
</Window>
Bind a command to the button and implement the CanExecute method to check the status of the checkboxes and either enable or disable the button and use the Execute method to invoke the functionality that you want on the button.
MVVM RelayCommand
CanExecute on MSDN
EDIT: Here is some source code of how to implement a RelayCommand. The RelayCommand class can be found at the first link provided above. I'm assuming that you know how to hook up the DataContext to the ViewModel implementation.
<StackPanel>
<CheckBox Name="MyCheckBox" Content="Some CheckBox"
IsChecked="{Binding MyCheckBoxChecked}"/>
<Button Content="Click me" Command="{Binding MyCommand}"/>
</StackPanel>
public class OrderViewModel
{
private RelayCommand MyRelayCommand;
public OrderViewModel()
{
MyRelayCommand = new RelayCommand(Execute, CanExecute);
MyCheckBoxChecked = false;
}
public RelayCommand MyCommand
{
get { return MyRelayCommand; }
}
public bool MyCheckBoxChecked { get; set; }
private bool CanExecute(object o)
{
// Here I'm just checking the property we've bound to but you can put
// anything in here that will return a bool, including a check of any/all
// of the checkboxes you may need to check
return MyCheckBoxChecked;
}
private void Execute(object o)
{
Console.WriteLine(#"Executing ...");
}
}