I'm trying to folow the mvvm pattern. When using galasoft EventToCommand i'm getting then following error:
The best overloaded method match for 'GalaSoft.MvvmLight.Command.RelayCommand.RelayCommand(System.Action)' has some invalid arguments...
Code from my XAML:
<toolkit:DatePicker Header="Select Date"
ValueStringFormat="{}{0:D}"
HorizontalAlignment="Left" Margin="0,126,0,0"
Name="datePicker1"
VerticalAlignment="Top" FontFamily="Verdana"
FontSize="22" Width="450">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<cmd:EventToCommand PassEventArgsToCommand="True"
Command="{Binding DateSelection}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</toolkit:DatePicker>
In the modelview:
public MainViewModel()
{
DateSelection = new RelayCommand<DateTimeValueChangedEventArgs>(time_Call);
}
public RelayCommand<DateTimeValueChangedEventArgs> DateSelection
{
get;
set;
}
void time_Call(object sender, DateTimeValueChangedEventArgs e)
{
}
I'm blank!
Can you Two-Way bind to the Value property instead? This would simplify things and let you use the true power of XAML and MVVM... binding.
<toolkit:DatePicker Header="Select Date"
ValueStringFormat="{}{0:D}"
HorizontalAlignment="Left" Margin="0,126,0,0"
Name="datePicker1"
VerticalAlignment="Top" FontFamily="Verdana"
FontSize="22" Width="450"
Value={Binding SelectedDate, Mode=TwoWay}" />
The view model
private DateTime selectedDate;
public DateTime SelectedDate
{
get
{
return this.selectedDate;
}
set
{
if (this.selectedDate != value)
{
this.selectedDate = value;
this.RaisePropertyChanged("SelectedDate");
}
}
}
public MainViewModel()
{
// initialize to today being selected
this.SelectedDate = DateTime.Now;
// the property changed might not be necessary if you are just trying to get the new value
this.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(MainViewModel_PropertyChanged);
}
void MainViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(e.PropertyName="SelectedDate")
{
// do something if needed
}
}
Related
Im new to WPF and MVVM.
Im trying to create Login window using MVVM and i succeeded to create.
here is the Login.xmal code.
<Button x:Name="btnLogin" Content="Login" HorizontalAlignment="Left" Margin="51,0,0,10"
VerticalAlignment="Bottom" Width="124" Height="57" Grid.Column="1"
CommandParameter="{Binding ElementName=txtPassword}"
Command="{Binding LoginCommand}"
>
</Button>
<Button x:Name="btnClose" Content="Close" HorizontalAlignment="Left" Margin="180,0,0,10"
VerticalAlignment="Bottom" Width="124" Height="57" Grid.Column="1" Command="{Binding ExitCommand}">
</Button>
<Label Content="User Name" Margin="10,74,0,0" VerticalAlignment="Top" Height="49"
VerticalContentAlignment="Center" Grid.Column="1" HorizontalAlignment="Left" Width="130"/>
<TextBox x:Name="txtUserName" HorizontalAlignment="Right" Height="49" Margin="0,74,10,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="185"
VerticalContentAlignment="Center" Grid.Column="1" FontSize="18">
<TextBox.Text>
<Binding Path="Username" Mode="OneWayToSource">
<Binding.ValidationRules>
<ExceptionValidationRule></ExceptionValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Label Content="Password" Margin="10,128,0,0" VerticalAlignment="Top" Height="49"
VerticalContentAlignment="Center" Grid.Column="1" HorizontalAlignment="Left" Width="130"/>
<PasswordBox x:Name="txtPassword" HorizontalAlignment="Right"
Height="49" Margin="0,128,10,0"
VerticalAlignment="Top" Width="185"
VerticalContentAlignment="Center" Grid.Column="1" FontSize="18">
</PasswordBox>
after this i have created the viewModeBase.cs class in which i implemented INotifyPropertyChanged and this included in LoginViewModel.cs...
here is LoginViewModel.cs code
public class LoginViewModel : ViewModelBase
{
private string m_username;
public string Username
{
get { return m_username; }
set
{
m_username = value;
OnPropertyChanged("Username");
}
}
private string m_password;
public string Password
{
get { return m_password; }
set
{
m_password = value;
OnPropertyChanged("Password");
}
}
private DelegateCommand exitCommand;
public ICommand ExitCommand
{
get
{
if (exitCommand == null)
{
exitCommand =new DelegateCommand(Exit);
}
return exitCommand;
}
}
private void Exit()
{
Application.Current.Shutdown();
}
public LoginViewModel()
{
}
private DelegateCommand<object> loginCommand;
public ICommand LoginCommand
{
get
{
if (loginCommand == null)
{
loginCommand = new DelegateCommand<object>(Login);
}
return loginCommand;
}
}
public void Login(object pPasswordBox)
{
try
{
if (string.IsNullOrEmpty(Username))
{
MessageBox.Show("Username cannot be blank.");
return;
}
if (string.IsNullOrEmpty(((PasswordBox)pPasswordBox).Password))
{
MessageBox.Show("Password cannot be blank.");
return;
}
dlUsers odlUsers = new dlUsers();
bool lResult = odlUsers.UserAuthentication(clsGymManagment.ConnectionString, Username,
((((PasswordBox)pPasswordBox).Password)));
if (lResult)
{
///TODO: Need code to Hide Login Window and Open New XAML.....
}
else
{
MessageBox.Show("Username/Password is wrong.");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
As i want to Hide LOGIN.XAML file and open UI.XAML file.. (UI.XAML you can consider any XAML window.)...
also it would be help full if you could assist me to navigation between Usercontrol on UI.XAML
You need to control the login window from a separate block of code, for instance App.xaml.cs. Set app.xaml to call code rather than show a window.
Have App_Startup create LoginViewModel, new up a form, set the data context of the form to your ViewModel and show the it.
Updates to the form will update the ViewModel, when it closes it will return control to your calling code.
Login.xaml.cs
private void btnOk_Click(object sender, RoutedEventArgs e)
{
if (anything incorrect)
{
MessageBox.Show("Enter a username and password");
}
else
DialogResult = true;
}
App.xaml.cs
Login.DataContext = LoginViewModel;
if (Login.ShowDialog() ?? false)
{
//Check the LoginViewModel for a correct password.
}
Fortunately the ability to hide and display different controls as you move through different pages inside an application is already written for you. See http://msdn.microsoft.com/en-us/library/ms750478.aspx.
Navigation Window is really powerful and can quite easily be skinned to provide very completely different looks too. See http://alski.net/post/2012/01/13/WPF-Wizards-part-2-Glass.aspx
My WPF window binds directly to an Entity Framework data context (CollectionViewSource). Users begin editing immediately once a record is found. The moment the form is dirty I'd like to disable the Add button and enable the Save and Undo buttons. Is there a simple way to do this using binding or an event?
I'm not using MVVM. I use Entity Framework database first and the EF designer. I'm hoping to avoid adding code for every field. The database is quite large.
You can do this without events if you'd like. And you can leverage the power of DataBinding without going so far as MVVM. The example below demonstrates, in a very simple way, how you can accomplish this. If your entity classes don't already have an IsDirty property (it has been a while since I've used database-first EF), you could add the property with a partial class.
XAML:
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center" Text="Name:" Margin="10"/>
<TextBox x:Name="NameTextBox" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="10" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<StackPanel Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="Save" HorizontalAlignment="Right" Margin="5" IsEnabled="{Binding IsDirty}" />
<Button Content="Cancel" HorizontalAlignment="Right" Margin="5" IsEnabled="{Binding IsDirty}" Click="Cancel_Click"/>
<Button Content="Add" HorizontalAlignment="Right" Margin="5" IsEnabled="{Binding IsClean}"/>
</StackPanel>
</Grid>
</Window>
A test Entity class:
public class Entity : INotifyPropertyChanged
{
private string _name;
private bool _isDirty = false;
public string Name
{
get { return _name; }
set
{
if(!IsDirty)
IsDirty = (value != _name);
_name = value;
RaisePropertyChanged("Name");
}
}
public bool IsDirty
{
get{ return _isDirty; }
set{
_isDirty = value;
RaisePropertyChanged("IsDirty");
RaisePropertyChanged("IsClean");
}
}
public bool IsClean
{
get { return !_isDirty; }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
And the code behind for the window:
namespace Test
{
public partial class EditTesting : Window
{
private Entity _myEntity;
public EditTesting()
{
InitializeComponent();
_myEntity = new Entity();
this.DataContext = _myEntity;
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
_myEntity.Name = string.Empty;
_myEntity.IsDirty = false;
}
}
}
I want to implement a checkbox which functions as a select-all / unselect-all checkbox but I am getting some problems with the binding. I am not really new to XAML and WPF so it is a mistery to me why my code is not working. I hope you can help. This is what I am doing:
First of all, I use MVVMLight and the event-to-command tags.
My view is called SetupView.xaml and my view-model is called SetupViewModel.cs
public class SetupViewModel : ViewModelBase
{
private List<FilterOptions> m_informationToShow;
private FilterOptions m_currentSelection;
public List<FilterOptions> InformationToShow
{
get { return m_informationToShow; }
set
{
m_informationToShow = value;
RaisePropertyChanged("InformationToShow");
RaisePropertyChanged("InformationToShowCount");
}
}
public FilterOptions CurrentSelection
{
get { return m_currentSelection; }
set
{
m_currentSelection = value;
RaisePropertyChanged("CurrentSelection");
}
}
}
The FilterOptions-object is my model object. Because you will need some of it it to understand the problem, here it show the part you need to understand the problem:
public class FilterOptions
{
private string m_projectName;
private BugsFilter m_bugsFilter;
private BuildsFilter m_buildsFilter;
private ChangeSetsFilter m_changeSetsFilter;
private ProgressInfoFilter m_progressInfoFilter;
private RisksFilter m_risksFilter;
private bool m_projectHealthFilter;
public bool AllFilterValues
{
get
{
if (m_bugsFilter.AtLeastOneFieldEnabled() ||
m_buildsFilter.AtLeastOneFieldEnabled() ||
m_changeSetsFilter.AtLeastOneFieldEnabled() ||
m_progressInfoFilter.AtLeastOneFieldEnabled() ||
m_risksFilter.AtLeastOneFieldEnabled() ||
m_projectHealthFilter
)
{
return true;
}
else
{
return false;
}
}
set
{
if (value == false)
{
m_bugsFilter.NoInformation();
m_buildsFilter.NoInformation();
m_changeSetsFilter.NoInformation();
m_progressInfoFilter.NoInformation();
m_risksFilter.NoInformation();
m_projectHealthFilter = false;
}
else
{
m_bugsFilter.CompleteInformation();
m_buildsFilter.CompleteInformation();
m_changeSetsFilter.CompleteInformation();
m_progressInfoFilter.CompleteInformation();
m_risksFilter.CompleteInformation();
m_projectHealthFilter = true;
}
}
}
I will proceed with my view:
<UserControl.Resources>
<viewModels:SetupViewModel x:Key="thisViewModel"></viewModels:SetupViewModel>
<DataTemplate x:Key="ProjectEntryTemplate">
<Border Margin="75,20,5,0">
<CheckBox Name="naam" Content="{Binding ProjectName}"
FontFamily="Segoe UI"
FontWeight="Light"
FontSize="24"
IsChecked="{Binding AllFilterValues}"
DataContext="{Binding}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<mvvm:EventToCommand Command="{Binding ProjectListItemCheckedChanged, Source={StaticResource thisViewModel}}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<mvvm:EventToCommand Command="{Binding ProjectListItemCheckedChanged, Source={StaticResource thisViewModel}}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</Border>
</DataTemplate>
</UserControl.Resources>
As you can see, I have a datatemplate which I am using in a listbox:
<ListBox ItemsSource="{Binding InformationToShow}"
ItemTemplate="{StaticResource ProjectEntryTemplate}"
SelectedIndex="0"
BorderThickness="0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<mvvm:EventToCommand Command="{Binding SelectionListboxChanged}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
Besides from that, I have in the same window, in another grid on the right side, a lot of checkboxes. They all correspond to a given filter from the FilterOptions object. This is what I am doing in my view:
<StackPanel Orientation="Vertical"
Margin="0,5,0,5">
<CheckBox Name="activeBugs" Content="Active bugs"
FontFamily="Segoe UI"
FontWeight="Light"
FontSize="22"
IsChecked="{Binding CurrentSelection.BugsFilter.ActiveBugs}">
</CheckBox>
<CheckBox Name="resolvedBugs" Content="Resolved bugs"
FontFamily="Segoe UI"
FontWeight="Light"
FontSize="22"
IsChecked="{Binding CurrentSelection.BugsFilter.ResolvedBugs}"/>
<CheckBox Name="bugTrend" Content="Bug trend"
FontFamily="Segoe UI"
FontWeight="Light"
FontSize="22"
IsChecked="{Binding CurrentSelection.BugsFilter.BugTrend}"/>
</StackPanel>
Last but not least, this are the command functions which I have in my view-model:
RelayCommand m_selectionChanged;
public ICommand SelectionListboxChanged
{
get
{
if (m_selectionChanged == null)
m_selectionChanged = new RelayCommand(param => SelectionListboxChangedExec(param), param => true);
return m_selectionChanged;
}
}
private void SelectionListboxChangedExec(object param)
{
SelectionChangedEventArgs e = (SelectionChangedEventArgs)param;
ListBox b = (ListBox)e.Source;
CurrentSelection = (FilterOptions)b.SelectedItem;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RelayCommand m_projectCheckedChanged;
public ICommand ProjectListItemCheckedChanged
{
get
{
if (m_projectCheckedChanged == null)
m_projectCheckedChanged = new RelayCommand(param => ProjectListItemCheckedChangedExec(param), param => true);
return m_projectCheckedChanged;
}
}
private void ProjectListItemCheckedChangedExec(object param)
{
RoutedEventArgs e = (RoutedEventArgs)param;
CheckBox checkBox = (CheckBox)e.Source;
FilterOptions dataContext = (FilterOptions)checkBox.DataContext;
if ((bool)checkBox.IsChecked)
dataContext.AllFilterValues = true;
else
{
dataContext.AllFilterValues = false;
}
//var expression = checkBox.GetBindingExpression(ToggleButton.IsCheckedProperty);
//expression.UpdateSource();
}
I really cant find the problem. Is there anyone who can help me?
Your help will be much appreciated !!
Thank you all in advance !
Why do you have to do it with a command? bind the checkbox to a BindingOption class that internally notifies the parent ViewModel, this notification really doesn't belong in the view, but to the view-model.
The ViewModel isn't supposed to know that the view uses a CheckBox.
So what u should do have the individual options have a notifying IsSelected property bound to the CheckBoxes, provide each of them with a reference to the parent ViewModel, and notify the parent when current IsSelected was changed. Doing this in the View, although you can find many way to do it, you're not supposed to, MVVM is about separating view-tasks from viewmodel-tasks.
This is way also gives you control on "Select all", "Deselect all" or "Invert selection" etc.
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;
}
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.