I'm trying to open a Popup with a button and have implemented a basic ICommand. The button is binded to the ICommand OpenPopupCommand while the Popup IsOpen attribute is binded to the "IsOpen" OnPropertyChanged. My thought process was to bind the Popup.IsOpen attribute to the ICommand as well to have IT trigger the OnPropertyChange but couldn't get it to work. I think I'm close but can't figure it out. Here is the code I have so far:
#region ICommand Members
private ICommand _openPopupCommand;
public ICommand OpenPopupCommand
{
get
{
if (_openPopupCommand == null)
_openPopupCommand = new RelayCommand(param => OpenPopupExecute(param));
return _openPopupCommand;
}
set
{
_openPopupCommand = value;
}
}
#endregion
#region Methods
public void OpenPopupExecute(object parameter)
{
parameter = true;
OnPropertyChanged("IsOpen");
}
#endregion
Button that "pops up" the Popup and the Popup XAML:
<Popup x:Name="FieldsPopup" Placement="Center" Width="400" Height="250" IsOpen="{Binding IsOpen}">
<StackPanel>
<TextBlock Background="LightBlue" HorizontalAlignment="Center" VerticalAlignment="Center" Height="250" Width="350" TextAlignment="Center" >This is a popup</TextBlock>
</StackPanel>
</Popup>
<Button Name="button_PatientIdentifierList" Width="23" Height="23" Grid.Column="2" Foreground="Black" Background="#FFCDCDCD" BorderBrush="#FF707070" Margin="3.4,4,4,0" VerticalAlignment="Top" Command="{Binding OpenPopupCommand}"/>
You're raising the PropertyChange notification, but I don't see you actually changing the property anywhere.
Unless I'm mistaken, this code here takes the CommandParameter (called parameter here) and sets it to true
public void OpenPopupExecute(object parameter)
{
parameter = true;
OnPropertyChanged("IsOpen");
}
However in your XAML the Button.CommandParameter isn't bound to anything
<Button Command="{Binding OpenPopupCommand}"/>
So I suspect that parameter is just null, and is not actually doing anything here.
What you seem to be missing is the actual IsOpen property definition, and setting it to true in your command's Execute code :
private bool _isOpen;
public bool IsOpen
{
get
{
return _isOpen;
}
set
{
_isOpen = value;
OnPropertyChanged("IsOpen");
}
}
public void OpenPopupExecute(object parameter)
{
IsOpen = true; // Will call OnPropertyChanged in setter
}
As a side note, I really don't like WPF's default PopupControl, and have a custom UserControl version of it on my blog if you ever decide you hate WPF's default PopupControl too :)
Related
I am trying to learn WPF by implementing a simple button and textbox. I want to understand why my buttons IsEnabled state isn't updating based on the value of my text field.
XAML:
<TextBox Height="100"
TextWrapping="Wrap"
Text="{Binding Test,NotifyOnSourceUpdated=True,NotifyOnTargetUpdated=True}"
VerticalAlignment="Top"
Padding="5, 3, 1, 1"
AcceptsReturn="True" Margin="161,10,10,0"/>
<Button Content="Go"
IsEnabled="{Binding MyButtonCanExecute}"
Command="{Binding MyButtomCommand}"
HorizontalAlignment="Left"
Margin="64,158,0,0"
VerticalAlignment="Top" Width="75"/>
C#:
class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool MyButtonCanExecute
{
get
{
return !String.IsNullOrWhiteSpace(Test);
}
}
private ICommand myButtonCommand;
public ICommand MyButtomCommand
{
get
{
if(myButtonCommand == null)
{
myButtonCommand = new RelayCommand(ShowMessage, param => this.MyButtonCanExecute);
}
return myButtonCommand;
}
}
private string test;
public string Test
{
get { return this.test; }
set
{
if (this.test != value)
{
this.test = value;
this.NotifyPropertyChanged("Test");
}
}
}
public MainWindowViewModel()
{
//
}
public void ShowMessage(object obj)
{
MessageBox.Show("Value of textbox is set to: " + this.Test);
}
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Questions:
When I type into the textbox, my breakpoint in the Test setter does not get hit. Why? If the textbox is bound to the Test property, isn't that the point?
When I type into the textbox, the MyButtonCanExecute gets called constantly. However, in debug the value of test is always null... why? Shouldn't it take whatever I type into the textbox?
The main issue seems to be that the value of Test isn't updating whenever I type.
I understand there may be a different way to implementing binding the IsEnabled state to the length of test, but I want to understand what's wrong with my understanding of how WPF works.
Answering my own question for future readers. Thanks for #ASh for pointing me in the right direction.
The problem was that, when typing into the textbox, the UpdateSourceTrigger had its default value (because I hadn't set it in the XAML). This meant that the property the textbox was bound to didn't update until the element lost focus, rather than when its text changed.
The solution:
Change the Text field of the textbox to this:
Text="{Binding Test,UpdateSourceTrigger=PropertyChanged}"
Note the new UpdateSourceTrigger=PropertyChanged means the source property (MainWindowViewModel.Test) updates every time I type.
Then, inside the Test property setting I had to add a new NotifyPropertyChanged call for the MyButtonCanExecute property which is dependent on the value of Test:
private string test;
public string Test
{
get { return this.test; }
set
{
if (this.test != value)
{
this.test = value;
this.NotifyPropertyChanged("Test");
this.NotifyPropertyChanged("MyButtonCanExecute");
}
}
}
And also add the UpdateSourceTrigger to the IsEnabled value of the button:
<Button Content="Go"
IsEnabled="{Binding MyButtonCanExecute,UpdateSourceTrigger=PropertyChanged}"
Command="{Binding MyButtomCommand}" />
EDIT:
A better solution is to remove the IsEnabled binding altogether, so you just have:
<Button Content="Go"
Command="{Binding MyButtomCommand}" />
This is because when we create the MyButtonCommand we pass in MyButtonCanExecute as the CanExecute property to the RelayCommand:
myButtonCommand = new RelayCommand(ShowMessage, param => this.MyButtonCanExecute);
I am using Exceed IntegerUpDown control in my .xaml file. I want to bind IntegerUpDown value as a CommandParameter of a button.
I do not have any code behind files and this is a custom control xaml file. So i want to achieve this by only using xaml systax.
<DockPanel>
<xctk:IntegerUpDown x:Name="ExtraExpressionValue" Increment="1" FormatString="N0" AllowSpin="True" Width="70" Watermark="Numeric" AllowTextInput="False" Minimum="0" Value="999"/>
<Button Style="{StaticResource ContextMenuButton}" Margin="5,0,0,0" Content="Add" Command="{Binding SetExtaExpressionValueCommand}" CommandParameter="{Binding ElementName=ExtraExpressionValue,Path=Value}"/>
</DockPanel>
Above is my xaml code. this return 0 to command method.
My command class is as follows,
public class DesignItemCommands
{
private ICommand setExtaExpressionValueCommand;
public ICommand SetExtaExpressionValueCommand => setExtaExpressionValueCommand ?? (setExtaExpressionValueCommand = new CommandHandler(SetExtaExpressionValue, canExecute));
private bool canExecute;
public DesignItemCommands()
{
canExecute = true;
}
private void SetExtaExpressionValue(object parameter)
{
//I need parameter here..
}
}
Couldn't find a way on the requirement. Just posting here to help someone later on this issue.
I used a ViewModel Variable to bind IntegerUpDown control value.
<DockPanel>
<xctk:IntegerUpDown Increment="1" Value="{Binding ExtraExpressionValue}"/>
<Button Content="Add" Command="{Binding SetExtaExpressionValueCommand}"/>
</DockPanel>
My ViewModel is as follows,
public class DesignItemCommands
{
private ICommand setExtaExpressionValueCommand;
public ICommand SetExtaExpressionValueCommand => setExtaExpressionValueCommand ?? (setExtaExpressionValueCommand = new CommandHandler(SetExtaExpressionValue, canExecute));
private bool canExecute;
public int ExtraExpressionValue { get; set; }
public DesignItemCommands()
{
canExecute = true;
ExtraExpressionValue = 1;
}
private void SetExtaExpressionValue(object parameter)
{
//I can use value here using variable ExtraExpressionValue
}
}
Hope this helps someone later.
I was learning on some stuff on universal windows app when i came across this problem:
i want to build a splitview menu with a hamburger button without code behind.
so i setup a viewmodel with a property and a command for changing the value of that property. to check if the commmand is fired i included a small messagedialog.
i bound the splitview IsPaneOpen to my viewmodel but somehow it does not seem to work.
xaml code
<Page.Resources>
<vm:PaneViewModel x:Key="viewModel"/>
</Page.Resources>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{StaticResource viewModel}" >
<Button Content="Test" Command="{Binding Path=OpenPane, Mode=TwoWay}" ManipulationMode="All"/>
<SplitView DisplayMode="CompactInline"
x:Name="Splitview"
OpenPaneLength="150"
CompactPaneLength="20"
IsPaneOpen="{Binding IsPaneOpen, Mode=TwoWay}">
<SplitView.Pane>
<StackPanel Height="400">
<Button Height="40">
<TextBlock Text="Testbutton" Width="100"/>
</Button>
</StackPanel>
</SplitView.Pane>
<SplitView.Content>
<TextBlock Margin="30" Text="{Binding ShowAnything, Mode=TwoWay}"/>
</SplitView.Content>
</SplitView>
</StackPanel>
ViewModel Code
internal class PaneViewModel
{
public PaneViewModel()
{
OpenPane = new OpenPaneCommand(this);
}
private bool isPaneOpen = true;
public bool IsPaneOpen
{
get
{
return isPaneOpen;
}
set
{
isPaneOpen = value;
}
}
public string ShowAnything { get { return isPaneOpen.ToString(); } }
public OpenPaneCommand OpenPane { get; set; }
public void OpenPaneMethod()
{
if (isPaneOpen == false)
isPaneOpen = true;
else
isPaneOpen = false;
}
}
and Command Code
internal class OpenPaneCommand : ICommand
{
public OpenPaneCommand(PaneViewModel ViewModel)
{
this.viewModel = ViewModel;
}
private PaneViewModel viewModel;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
Blub();
viewModel.OpenPaneMethod();
}
async private void Blub()
{
var dialog = new MessageDialog("Testausgabe");
await dialog.ShowAsync();
}
like i said - the messagedialog shows up, but neither the textblock in the splitview.content or ispaneopen seems to change. debuging shows me that my method to change the value does indeed change the value.
so i was wondering if my binding or datacontext-setting was off.
Maybe you guys got an hint for me where my missunderstanding comes from.
thanks!
meq
Your binding isn't working because the View isn't notified of change on your ViewModel. Your VM needs to implement INotifyPropertyChanged interface and raise the PropertyChanged event whenever your property changes.
So typically you would put something like this to your setter:
...
set
{
isPaneOpen = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsPaneOpen");
}
...
I think your view model should inherit the ObservableObject.
and further all properties should be update like below:
public bool IsPaneOpen
{
get { return isPaneOpen; }
set
{
this.Set<bool>(ref this._loisPaneOpendedPendingCount, value);
}
}
same for ShowAnything property
I am new to xaml, WPFs, C# and the MVVM paradigm. I have started with an app based on this example project, in the selected excerpts i want to disable the authenticate button from the LoginPageViewModel after the authenticate button has been clicked(There is no point clicking the button if you are authenticated). I have got command binding working, as well as text control binding between the view and ViewModel. my LoginPageViewModel is based on a abstract class that inherits from INotifyPropertyChanged
The setter AuthenticateButtonEnabled is working, but it is not binding to the isEnabled proprerty on the form. My question is, what could I have missed, and How can i trace the binding between a View and a ViewModel?
the LoginPageView.xaml button:
<Button x:Name="authenticateButton" Content="{x:Static res:Strings.LoginPage_authenticateButton_content}"
Grid.Column="2" Margin="53,4,0,10"
Grid.Row="2" FontSize="16"
IsEnabled="{Binding Path=AuthenticateButtonEnabled}"
Command="{Binding Path=AuthenticateCommand}" HorizontalAlignment="Left" Width="87"/>
the viewModel
private String _username;
private String _responseTextBlock;
private String _linkTextBlockURI;
private String _linkTextBlockText;
private bool _authenticateButtonEnabled;
...
private async void Authenticate()
{
ResponseTextBlock = Strings.LoginPage_responseBlock_content_checking;#this works!
AuthenticateButtonEnabled = false;
return;
}
....
public bool AuthenticateButtonEnabled
{
get { return _authenticateButtonEnabled; }
set { _authenticateButtonEnabled = value; OnPropertyChanged("AuthenticateButtonEnabled"); }
}
// this is in the abstract class.
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
If you want to have both: command and AuthenticateButtonEnabled, then simply check for this property in CanExecute delegate and vise-versa in property setter update command.
Here is implementation with DelegateCommand and some improvements which you may find useful:
bool _isAuthenticateButtonEnabled;
public bool IsAuthenticateButtonEnabled
{
get { return _isAuthenticateButtonEnabled; }
set
{
_isAuthenticateButtonEnabled = value;
OnPropertyChanged();
AuthenticateCommand.Update();
}
}
// the base could class could actually implement this
void OnPropertyChanged([CallerMemberName] string property) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
public DelegateCommand AuthenticateCommand { get; }
// view model constructor
public ViewModel()
{
AuthenticateCommand = new DelegateCommand(o =>
{
... // some actions when command is executed
}, o =>
{
bool somecondition = ...; // some condition to disable button, e.q. when executing command
return somecondition && IsAuthenticateButtonEnabled;
});
}
This will let you to have both: property to enable/disable button, which can be used in binding (to another control, e.g. CheckBox.IsChecked) and command which can have independent condition to disable button when command shouldn't be executed (typically in async command delegate, when it performs a long running command, but for this you may want to check this answer.).
if you bind the command Property of the Button to an ICommand Property in your Viewmodel, then you do NOT need to handle the IsEnabled Property of the Button because its handled by the CanExecute Method of the ICommand implementation.
google for RelayCommand or DelegateCommand
Thanks to the posters for your help, I wanted to share the working solution for others. I used the DelegateCommand, but had to change some parts in the loginPageViewModel to make it work: I also updated the xaml so that the controls were all inactive after a successful authentication.
the loginPage xaml:
<Label x:Name="usernameLabel" Content="{x:Static res:Strings.LoginPage_usernameLabel_content}" HorizontalAlignment="Left" Margin="10,4,0,0" Grid.Row="0" VerticalAlignment="Top" Width="130" FontSize="16" Height="36" Grid.Column="1"/>
<TextBox x:Name="usernameTextBox" Grid.Column="2" Grid.Row="0" TextWrapping="Wrap"
Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding AuthenticateButtonEnabled}"
Margin="10,5,0,6" FontSize="16" HorizontalAlignment="Left" Width="130" TextChanged="usernameTextBox_TextChanged"/>
<Label x:Name="passwordLabel" Content="{x:Static res:Strings.LoginPage_passwordLabel_content}" Margin="10,5,0,0" Grid.Row="1" VerticalAlignment="Top" FontSize="16" Height="36" Grid.RowSpan="2" HorizontalAlignment="Left" Width="130" Grid.Column="1"/>
<PasswordBox x:Name="passwordBox" Grid.Column="2" Margin="10,0,0,9"
PasswordChanged="PasswordBox_PasswordChanged"
IsEnabled="{Binding AuthenticateButtonEnabled}"
Grid.Row="1" FontSize="16" HorizontalAlignment="Left" Width="130"/>
<Button x:Name="authenticateButton" Content="{x:Static res:Strings.LoginPage_authenticateButton_content}"
Grid.Column="2" Margin="53,4,0,10"
Grid.Row="2" FontSize="16"
IsEnabled="{Binding AuthenticateButtonEnabled}"
Command="{Binding Path=AuthenticateCommand}" HorizontalAlignment="Left" Width="87"/>
the loginPageViewModel:
....
private bool _authenticateButtonEnabled;
private DelegateCommand _authenticateCommand;
public bool AuthenticateButtonEnabled {
get { return _authenticateButtonEnabled; }
set
{
_authenticateButtonEnabled = value;
DynamicOnPropertyChanged(); // this is so named to not content with onPropertyChanged defined elsewhere.
AuthenticateCommand.Update();
}
}
...
public DelegateCommand AuthenticateCommand
{
get {
if (_authenticateCommand == null)
{
_authenticateCommand = new DelegateCommand(Authenticate, AuthenticateEnded);
}
return _authenticateCommand;
}
}
private bool AuthenticateEnded(object obj) {
return _authenticateButtonEnabled;
}
private async void Authenticate(object obj)
{
AuthenticateButtonEnabled = false;
ResponseTextBlock = Strings.LoginPage_responseBlock_content_checking;
i3SoftHttpClient _httpClient = new i3SoftHttpClient();
i3SoftUser _i3SoftUser;
AuthenticateCommand.CanExecute(false);
....
// if authentication does not succeed - turn the buttons back on.
AuthenticateCommand.CanExecute(true);
}
and to the Delegate command class i added:
public void Update()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
I would like to understand how to correctly use MVVM and data binding when we are working with many properties.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="463" Text="{Binding OriginalText, UpdateSourceTrigger=PropertyChanged}" />
<Label Height="28" HorizontalAlignment="Left" Margin="12,242,0,0" Name="label1" VerticalAlignment="Top" Width="463" Content="{Binding ModifiedText}"/>
<CheckBox Content="Upper" Height="16" HorizontalAlignment="Left" Margin="12,41,0,0" Name="checkBox1" VerticalAlignment="Top" />
<CheckBox Content="Underline" Height="16" HorizontalAlignment="Left" Margin="12,63,0,0" Name="checkBox2" VerticalAlignment="Top" />
<CheckBox Content="Bold" Height="16" HorizontalAlignment="Left" Margin="12,85,0,0" Name="checkBox3" VerticalAlignment="Top" />
<CheckBox Content="Shadow" Height="16" HorizontalAlignment="Left" Margin="12,107,0,0" Name="checkBox4" VerticalAlignment="Top" />
<CheckBox Content="Red" Height="16" HorizontalAlignment="Left" Margin="12,129,0,0" Name="checkBox5" VerticalAlignment="Top" />
<CheckBox Content="Scary" Height="16" HorizontalAlignment="Left" Margin="12,151,0,0" Name="checkBox6" VerticalAlignment="Top" />
<CheckBox Content="Remove first letter" Height="16" HorizontalAlignment="Left" Margin="12,173,0,0" Name="checkBox7" VerticalAlignment="Top" />
<CheckBox Content="Remove last letter" Height="16" HorizontalAlignment="Left" Margin="12,195,0,0" Name="checkBox8" VerticalAlignment="Top" />
</Grid>
I have a OriginalText TextBox and a ModifiedText Label. When I check a box I would like to directly apply the modification without having to click a button. How should I do that?
In my ViewModel I created all the properties that are binded to the XAML CheckBox.
private string _originalText = string.Empty;
public string OriginalText
{
get { return _originalText; }
set
{
_originalText = value;
NotifyPropertyChanged("OriginalText");
}
}
private string _modifiedText;
public string ModifiedText
{
get { return _originalText; }
set
{
_originalText = value;
NotifyPropertyChanged("ModifiedText");
}
}
private bool upper;
public bool Upper
{
get { return upper; }
set
{
upper = value;
NotifyPropertyChanged("Upper");
// Should I notify something else here or call a refresh method?
}
}
private bool removeFirstLetter;
public bool RemoveFirstLetter
{
get { return removeFirstLetter; }
set
{
removeFirstLetter = value;
NotifyPropertyChanged("RemoveFirstLetter");
// Should I notify something else here or call a refresh method?
}
}
// ...
Then I created a Work method in the same ViewModel class at this moment. I ll move this method into the business later.
private void Work()
{
string result = _originalText;
if (Upper)
result = result.ToUpper();
if (removeFirstLetter)
result = result.Substring(1, result.Length);
// if ...
ModifiedText = result;
}
My question is when, where should I call the work method? Should I call it in each setter or getter? I dont like the idea. I do something wrong...
Thank you.
In your particular case, you should create a Boolean property using the INotifyPropertyChanged interface. Now bind this property to your "IsChecked" check box property. By calling your Work() method inside the setter, every time the check box is "ticked" the setter will trigger each time.
The answer to your question is very simple: Use Commands.
Commands are MVVM's way to realize the binding to a method in your ViewModel. The implementation of Commands follows a very standard pattern. You will find plenty of information over the Internet here is just a short sketch:
Commands implemented in your ViewModel have to be of type ICommand and every Command has to come along with to methods in your code one responsible for executing the actual method and the other one for checking if the execution is currently possible.
These methods have to be named CanExecute and Execute respectively. It is commonly the case to facilitate the use of several Commands with a small helping class called DelegateCommand which provides delegates for the previously mentioned methods.
Take this class as it is without any modifications:
public class DelegateCommand<T> : ICommand {
private Predicate<T> canExecute;
private Action<T> execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand (Predicate<T> canExecute, Action<T> execute) {
this.canExecute = canExecute;
this.execute = execute;
}
public bool CanExecute (object param) {
return canExecute((T)param);
}
public void Execute (object param) {
execute((T)param);
}
public void CanExecuteChangedRaised () {
CanExecuteChanged(this, new EventArgs());
}
}
Then your Command declarations are of type DelegateCommand rather than of type ICommand. See the following example to illustrate and you will get the idea:
Supose you have a method foo() in your ViewModel you want to be called with a click to a button:
class ViewModel {
// ...
public DelegateCommand<object> FooCommand { get; set; }
public ViewModel () {
FooCommand = new DelegateCommand<object>(CanExecuteFooCommand, ExecuteFooCommand);
}
public bool CanExecuteFooCommand (object param) {
return true;
}
public void ExecuteFooCommand (object param) {
foo();
}
// ...
}
Supposing you have set your ViewModel as the controls DataContext via it's DataContext property the only thing left to do is to bind the FooCommand to your button like this:
That's it!
APPENDIX (referring to comment):
In order to have some action take place without actually hitting the Button you would simply have to track any changed in the UI with your ViewModel and react accordingly - that's what MVVM is about: Track the data from the UI modify or process them and populate them back to the UI.
To react on a TextBox Text change create a corresponding string property in your ViewModel and track whether the new ioncoming value from the View is different to the current textBox text:
private string _text;
public string Text {
get { return _text; }
set {
// the text in the TextBox is about to change.
if (!_text.Equals(value))
{
doSomething();
}
_text = value;
FirePropertyChanged("Text");
}
}
For doing the same with your CheckBox you can apply ICommand as described above since CheckBox is derived from Button and is therefor offering the Command property.