Accessing UI controls in Viewmodel - wpf

I have Stackpanel with Buttons as follows,
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 10 0 0" Name="mystack">
<Button Width="30" Name="btn1" Height="30" Content="1" Margin="10"/>
<Button Width="30" Height="30" Content="2" Margin="10"/>
<Button Width="30" Height="30" Content="3" Margin="10"/>
<Button Width="30" Height="30" Content="4" Margin="10"/>
</StackPanel>
How to make these buttons as single object and use that in viewmodel?
because I have to check each and every buttons "Content" with my viewmodel property..

You would have to create a binding.
Content={Binding SomePropertyInYourViewModel, UpdateSourceTrigger=PropertyChanged}}

you need to add button command and button command parameter to the button
<Button Content="Button1" Command="{StaticResource DoSomethingCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}" />
this link may help you How to get the Content of Button in ViewModel?
this is how to add command in MVVM
public class ViewModelBase
{
public ViewModelBase()
{
_canExecute = true;
}
private ICommand _doSomethingCommand;
public ICommand DoSomethingCommand
{
get
{
return _doSomethingCommand ?? (_doSomethingCommand = new CommandHandler(() => MyAction(), _canExecute));
}
}
private bool _canExecute;
public void MyAction()
{
}
}
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}

Related

Lost Focus event issue in WPF

I have implement the command Binding on Text Box and implement the command for click action in Button. Initially focus is persists in the Text Box only. My issue is me tried to click the button on view through mouse. In this case my text box lost focus command triggered first that is fine but that the button click command is not invoked. Text Box lost focus event handled the event to traverse.
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = new ViewModel();
InitializeComponent();
txtServer.Focus();
}
}
public class RelayCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private Action<object> execute;
private Predicate<object> canExecute;
private event EventHandler CanExecuteChangedInternal;
public RelayCommand(Action<object> execute = null, Predicate<object> canExecute=null )
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
execute.Invoke(parameter);
}
}
public class ViewModel
{
private void ActionRequestedEvent(object param)
{
}
private ICommand _ActionCommand;
public ICommand ActionCommand
{
get
{
if (_ActionCommand == null)
this._ActionCommand = new RelayCommand(param =>
{
ActionRequestedEvent(param);
});
return _ActionCommand;
}
set
{
_ActionCommand = value;
}
}
}
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBox Width="150" Name="txtServer" Height="25">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding ActionCommand}" CommandParameter="DataSetServerLostFocus"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<Button Content="..." Width="25" Height="25" Margin="3 0 0 0" Command="{Binding ActionCommand}"/>
</StackPanel>
Most likely your problem is not an implementation problem, but a problem of the testing method you have chosen.
As I assume, you have set breakpoints and are trying to "catch" a call to the ActionRequestedEvent (object param) method.
When the command is executed for the first time, control is transferred to Debug Studio.
You press "Continue" and the command is not executed a second time.
This is due to the fact that after activating the Studio, your Window loses the user focus and therefore the command for the button is no longer called.
Here is an example of testing - you will see the result of calling the method in the Studio's Output Window.
using System.Diagnostics;
using System.Windows.Input;
namespace LostFocusCommand
{
public class ViewModel
{
private int num;
private void ActionRequestedEvent(object param)
{
Debug.WriteLine($"{++num}: {param}");
}
private ICommand _actionCommand;
public ICommand ActionCommand => _actionCommand
?? (_actionCommand = new RelayCommand(ActionRequestedEvent));
}
}
public partial class LostFocusCommandWindow : Window
{
public LostFocusCommandWindow()
{
InitializeComponent();
}
}
<Window x:Class="LostFocusCommand.LostFocusCommandWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:LostFocusCommand"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
Title="LostFocusCommandWindow" Height="450" Width="800"
FocusManager.FocusedElement="{Binding ElementName=txtServer}">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBox Width="150" x:Name="txtServer" Height="25">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding ActionCommand}"
CommandParameter="DataSetServerLostFocus"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<Button Content="..." Width="25" Height="25" Margin="3 0 0 0"
Command="{Binding ActionCommand}"
CommandParameter="Button"/>
</StackPanel>
</StackPanel>
</Window>
I also wanted to point out to you the incorrect implementation of ICommand.
In some cases, such an implementation may not work correctly.
Use the implementation at this link: BaseInpc, RelayCommand and RelayCommand<T> classes.

Relay Command not firing on menu item click

My View
<Button.ContextMenu>
<ContextMenu x:Name="Conn_Context_button" Style="{StaticResource LeftContextMenuStyle}">
<MenuItem Style="{StaticResource LeftContextMenuItemStyle}" Header="{x:Static properties:ResourceWrapper.Dashboard_Connection_Delete}" Click="MenuItem_DeleteConnection_Click" />
<MenuItem Style="{StaticResource LeftContextMenuItemStyle}" Header="{x:Static properties:ResourceWrapper.Dashboard_Connection_Refresh}" Command="{Binding MyViewModel.RefreshCommand}" />
</ContextMenu>
MyViewModel.cs
public RelayCommand RefreshCommand { get; set; }
RefreshCommand = new RelayCommand(RefreshConnection);
private void RefreshConnection(object sender)
{
//My Logic
}
Here RefreshCommand is not firing when i click the refresh menu item
As a good example, take a look to this situation.
Here's a simple piece of code taken from one of my current projets:
private void PrepareCommands()
{
RefreshCommand = new RelayCommand(RefreshCommandMethod);
AddConfigurationCommand = new RelayCommand(AddConfigurationCommandMethod, param => CanAddConfiguration);
EditConfigurationCommand = new RelayCommand(EditConfigurationCommandMethod, param => CanEditConfiguration);
RemoveConfigurationCommand = new RelayCommand(RemoveConfigurationCommandMethod, param => CanRemoveConfiguration);
}
where the commands are
#region Commands
public ICommand AddConfigurationCommand { get; set; }
public ICommand EditConfigurationCommand { get; set; }
public ICommand RemoveConfigurationCommand { get; set; }
public ICommand RefreshCommand { get; set; }
#endregion
Bindings are
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" DockPanel.Dock="Right">
<Button Template="{StaticResource AddButton}" Command="{Binding AddConfigurationCommand}" Margin="3,0" />
<Button Template="{StaticResource EditButton}" Command="{Binding EditConfigurationCommand}" Margin="3,0" />
<Button Template="{StaticResource DeleteButton}" Command="{Binding RemoveConfigurationCommand}" Margin="3,0" />
</StackPanel>
As Jan Walczak said above, try to use ICommand instead of RelayCommand. If you have created your own RelayCommand, don't forget to inherit from ICommand.

Disable validation for a button in IDataErrorInfo

I have two buttons "Search" and "Clear" on my View for which I have two commands on my view model. I have implemented IDataErrorInfo on my ViewModel and validated the input fields. How can I disable validation for the Clear button?
<TextBox Text="{Binding SearchText, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, ValidatesOnExceptions=True}"
Validation.ErrorTemplate="{StaticResource ErrorTemplate}"/>
<Button Content="Search" Command="{Binding SearchCommand}" />
<Button Content="Clear" Command="{Binding ClearCommand}" />
I am assumed you want to enable / disable the clear button based on validation in the search textbox. I have used the MvvmLight Relaycommand for commanding from the latest MVVMLight using the namespace using GalaSoft.MvvmLight.CommandWpf; refer the code below.
<Window x:Class="DataTemplateSelector_Learning.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window3" Height="300" Width="300">
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Search Text"/>
<TextBox Width="100" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay,
ValidatesOnDataErrors=True, NotifyOnValidationError=True, ValidatesOnExceptions=True}" />
</StackPanel>
<Button Content="Search" Command="{Binding SearchCommand}" />
<Button Content="Clear" Command="{Binding ClearCommand}" />
</StackPanel>
</Grid>
public partial class Window3 : Window
{
public Window3()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
class ViewModel:INotifyPropertyChanged,IDataErrorInfo
{
private string searchText;
private bool enableClear;
public string SearchText
{
get { return searchText; }
set { searchText = value; Notify("SearchText"); }
}
public ICommand SearchCommand { get; set; }
public ICommand ClearCommand { get; set; }
public ViewModel()
{
ClearCommand = new RelayCommand(OnClear, CanClear);
}
private bool CanClear()
{
return enableClear;
}
private void OnClear()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void Notify(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public string Error
{
get { return String.Empty; }
}
public string this[string columnName]
{
get
{
String errorMessage = String.Empty;
if (!string.IsNullOrEmpty(SearchText))
{
if (SearchText.Length > 0)
{
enableClear = true;
}
else
{
enableClear = false;
}
}
return errorMessage;
}
}
}

How to update the textblock through NewFile menu click command

I am new to the MVVM pattern. I am facing problem in updating the textblock.I have one textblock and menu button.
//View.xaml for textblock
<Grid DataContext="{Binding Source={StaticResource MenuHandler}}" Height="23" HorizontalAlignment="Stretch" VerticalAlignment="Top" Grid.Row="2" Grid.ColumnSpan="3" Margin="5,0,5,0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=StatusText, UpdateSourceTrigger=PropertyChanged}" Name="StatusText" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Stretch" Width="100" IsEnabled="True" Height="23"/>
</Grid>
//View.xaml for Menu
<Menu DataContext="{Binding Source={StaticResource MenuHandler}}" Height="25" HorizontalAlignment="Stretch" Name="menu1" VerticalAlignment="Top" Visibility="Visible" Background="AliceBlue">
<MenuItem Header="_New" Command="{Binding NewFileCommand}" Name="NewFile" >
<MenuItem.Icon>
<Image Source="/WhitelistBlacklistEditor;component/Images/NewFile.png" Width="25"/>
</MenuItem.Icon>
</MenuItem>
</Menu >
In view model i am using MenuHandler class
//MenuHandler.cs
public string StatusText
{
get { return _StatusText; }
set
{
_StatusText = value;
RaisePropertyChangedEvent(StatusText);
}
}
public ICommand NewFileCommand
{
get { return new DelegateCommand(NewFile_Click); }
}
public void NewFile_Click()
{
StatusText = "checking";
}
If i update the StatusText in other than NewFile_Click() method, the value is updating in the textblock but if i do the same in NewFile_Click() through ICommand it is not updating.
I am also inheritted the MenuHandler class by INotifyPropertyChanged and i checked by adding twoway mode in xaml.
public class DelegateCommand : ICommand
{
private readonly Predicate _canExecute;
private readonly Action _execute;
public event Event Handler CanExecuteChanged;
public Delegate Command(Action<object> execute)
: this(execute, null)
{
}
public Delegate Command(Action<object> execute,
Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public override bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public override void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
if( CanExecuteChanged != null )
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
private DelegateCommand _NewFileCommand;
public ICommand NewFileCommand
{
get
{
if (adcom == null)
adcom = new DelegateCommand(CommandExecuter, CommandExecute);
return adcom;
}
}
private bool CommandExecuter(object obj)
{
return true;
}
private void CommandExecute(object obj)
{
Statuscheck="";
}

WPF/Silverlight: How do you make a Button call ICommand.CanExecute when the command parameter is changed?

How do you make a Button call ICommand.CanExecute when the command parameter is changed?
This is my current XAML.
<Button Content="Delete" Command="{Binding DeleteItemCommand}" CommandParameter="{Binding SelectedItem, ElementName=DaGrid}" />
EDIT It appears this is only an issue in WPF.
I'm not sure what you're doing wrong, but here is an example of a Button being controlled both by a BindingParameter and a CanExecute Flag. Perhaps your binding parameter isn't a DependencyProperty, and therefore, when it changes the Button isn't being notified.
<UserControl x:Class="SilverlightICommandTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ct="clr-namespace:SilverlightICommandTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<ct:TestModel x:Key="Model" />
</UserControl.Resources>
<StackPanel x:Name="LayoutRoot" Orientation="Vertical" Background="White" DataContext="{StaticResource Model}">
<CheckBox Content="Enable" IsChecked="{Binding TestCmd.CanDoCommand, Mode=TwoWay}" />
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ElementName=testSlider, Path=Value}" Width="40" Grid.Column="0" />
<Slider Name="testSlider" Minimum="0" Maximum="100" SmallChange="1" Grid.Column="1" />
</Grid>
<Button Command="{Binding TestCmd}" CommandParameter="{Binding ElementName=testSlider, Path=Value}" Content="Do Something" />
</StackPanel>
</UserControl>
And the code file:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace SilverlightICommandTest
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
}
public class TestModel : DependencyObject
{
TestCommand _testCmd = new TestCommand();
public TestCommand TestCmd { get { return _testCmd; } }
public TestModel()
{
}
}
public class TestCommand : DependencyObject, ICommand
{
public static readonly DependencyProperty CanDoCommandProperty = DependencyProperty.Register("CanDoCommand", typeof(Boolean), typeof(TestCommand), new PropertyMetadata(false, new PropertyChangedCallback(CanDoCommandChanged)));
public Boolean CanDoCommand
{
get { return (Boolean)GetValue(CanDoCommandProperty); }
set { SetValue(CanDoCommandProperty, value); }
}
public event EventHandler CanExecuteChanged;
public TestCommand()
{
}
public Boolean CanExecute(Object parameter)
{
return this.CanDoCommand && (((Int32)(Double)parameter) % 2 == 0);
}
public void Execute(Object parameter)
{
MessageBox.Show("Oh Hai!");
}
private void OnCanDoCommandChanged(DependencyPropertyChangedEventArgs args)
{
if (this.CanExecuteChanged != null)
{
this.CanExecuteChanged(this, new EventArgs());
}
}
private static void CanDoCommandChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
((TestCommand)sender).OnCanDoCommandChanged(args);
}
}
}
In the future I recommend doing a little more research on the pattern first (http://www.silverlightshow.net/items/Model-View-ViewModel-in-Silverlight.aspx), and if you still can't figure it out, post more of your source code.
Strange. Normally OnCommandParameterChanged calls UpdateCanExecute (both internal methods). Does the Binding to CommandParameter work as expected?
You need to call CommandManager.InvalidateRequerySuggested to re-evaluate CanExecute. Note that it will re-evaluate it for all commands, not just the one your want...

Resources