I'm making an input page and I'm trying to implement a reset button. After a click on the button, the UI should be empty again.
I thought that entering an empty string would deal with this. In the code it seems to work and the value does get changed to "" but in the UI the typed text stays visible (so it doesn't show the empty "" string). I also tried with string.Empty as suggested in here but that also doesn't seem to work.
Am I missing something here? I'm kinda new to programming so if I did something horribly wrong, don't laugh too hard ;)
I'm using an MVVM pattern and Fody Weaver to deal with the property changed part of the code.
The UI / XAML
<TextBlock Text="Naam:"
Grid.Column="0"
Style="{StaticResource InputInputBlock}"
/>
<TextBox Foreground="White"
Grid.Column="1"
Text="{Binding Name, Mode=TwoWay}"
Style="{StaticResource InputInputBox}"
/>
<Button Content="Reset"
Height="50"
Width="150"
Grid.Column="0"
Grid.Row="2"
VerticalAlignment="Top"
HorizontalAlignment="Center"
Style="{StaticResource FlatButton}"
Command="{Binding ResetCommand}"
/>
The view model
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
}
}
public AddStakeholderViewModel()
{
ResetCommand = new RelayCommand(() => ResetForm());
}
private void ResetForm()
{
Name = " ";
}
You can implement the INotifyPropertyChanged interface in your class. This works for me:
public class Person : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Name");
}
}
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
XAML:
<TextBox Foreground="White"
Grid.Column="1"
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Style="{StaticResource InputInputBox}"
/>
MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = newPerson;
}
Person newPerson = new Person();
private void button_Click(object sender, RoutedEventArgs e)
{
newPerson.Name = "";
}
}
Related
I have a ListView that is bound on an ObservableCollection.
<ListView Grid.Column="0" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderThickness="0" Margin="5" Name="CustomerListView" ItemsSource="{Binding Customers}" SelectedItem="{Binding Path=CurrentCustomer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Margin="5,0,0,0" Text="{Binding LastName}"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In the same View i have some TextBoxes which are meant to edit the CurrentCustomer. I also have a save button. If you click this button the modifications of the CurrentCustomer should be saved. If the button "cancel" is pressed the modifications should be discarded.
<TextBox Name="CustomerSalutationTextBox" Grid.Column="1" Grid.Row="0" Height="20px" Margin="5" Text="{Binding Path=CurrentCustomer.Salutation, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The Problem is, if i make some changes on the currentCusomer, they are taking effect immediately.
Do you have a solution?
What you need to add in your ViewModel / the class you have a binding context to is to save what was previous in the Textfield.
And when you hit abort, u just overwrite your newValue with the old one.
I'm going to setup a small example.
class ExampleViewModel : INotifyPropertyChanged {
private string _customerLastName;
private string _customerName;
private string _initialCustomerName;
private string _initialCustomerLastName;
public string CustomerName {
get { return this._customerName; }
set {
this._customerName = value;
this.OnPropertyChanged();
}
}
public string CustomerLastName {
get { return this._customerLastName; }
set {
this._customerLastName = value;
this.OnPropertyChanged();
}
}
public ExampleViewModel(string customerName, string customerLastName) {
this.CustomerName = customerName;
this.CustomerLastName = customerLastName;
this._initialCustomerName = customerName;
this._initialCustomerLastName = customerLastName;
}
//example event handler for your abort button
private void OnAbortButtonClick(object sender, EventArgs args) {
this.CustomerName = this._initialCustomerName; //set the initial name
this.CustomerLastName = this._initialCustomerLastName; //set the initial lastName
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Alternative
As you might load your data from a database/csv file/something else, you should know the original values. When pressing the cancel button, you could invoke a CancelButtonClicked event in your ViewModel and some other class which subscribed to the ViewModels event and knows the original Model could set the original values on that viewModel instance, or just exchange the ViewModel instance with the original one.
Have a look at : https://msdn.microsoft.com/en-us/library/hh848246.aspx
class ExampleViewModel : INotifyPropertyChanged {
private string _customerLastName;
private string _customerName;
public event CancelButtonClicked CancelButtonClicked;
public string CustomerName {
get { return this._customerName; }
set {
this._customerName = value;
this.OnPropertyChanged();
}
}
public string CustomerLastName {
get { return this._customerLastName; }
set {
this._customerLastName = value;
this.OnPropertyChanged();
}
}
public ExampleViewModel(string customerName, string customerLastName) {
this.CustomerName = customerName;
this.CustomerLastName = customerLastName;
}
private void OnAbortButtonClick(object sender, EventArgs args) {
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
internal delegate void CancelButtonClicked(object sender);
public class SomeOtherClass {
private ExampleViewModel _viewModel;
public SomeOtherClass() {
this._viewModel = new ExampleViewModel("foo", "bar");
this._viewModel.CancelButtonClicked += ViewModelOnCancelButtonClicked;
}
private void ViewModelOnCancelButtonClicked(object sender) {
ExampleViewModel vm = sender as ExampleViewModel;
vm.CustomerName = "foo"; //set the initial values again
vm.CustomerLastName = "bar";
}
}
Alternative2
You could also exchange the complete VM when the event of the cancel button is invoked to retreive its original state.
Alternative3
Everytime your SelectedItem changes, you could save the current state of it by creating a copy of it. When your CancelButton is pressed, you set the SelectedItem to the copy of your original viewModel.
You'd need a copy constructor or a copy method for that purpose.
I've found out another solution. In the code behind of the view i've added following:
void saveButton_Click(object sender, RoutedEventArgs e)
{
BindingExpression be = customerFirstNameTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
}
My textbox with UpdateSourceTrigger Explicit
<TextBox Name="customerFirstNameTextBox" Grid.Column="1" Grid.Row="2" Height="20px" Margin="5" Text="{Binding Path=CurrentCustomer.FirstName, Mode=TwoWay, UpdateSourceTrigger=Explicit}" IsEnabled="{Binding Path=IsCustomerTextEnabled}"/>
And my button
<Button Name="SaveButton" Click="saveButton_Click" Margin="5" Content="Save"/>
I have a Textbox in WPF which has its "Text" Property bound to a string "EmployeeSource.ID" with Mode=TwoWay. My problem is that when i change the EmployeeSource object, the binding does not work. What is wrong in my approach?
XAML
<TextBox x:Name="NameTextBox" Margin="5,5,10,5" TextWrapping="Wrap"
Text="{Binding SelectedEmployee.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1" Grid.Column="1" />
Code Behind
private Employee _selectedEmployee;
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
_selectedEmployee = value;
UpdateTextBoxes();
}
}
private void UpdateTextBoxes()
{
NameTextBox.Text = SelectedEmployee?.Name;
}
Please try the code below. You need to implement the INotifyPropertyChanged interface inorder to achieve data binding in WPF. This is the basic concept of WPF data binding and MVVM pattern. This should work for you.
Code behind:
public class YourClassName : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Employee _selectedEmployee;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
// The constructor is private to enforce the factory pattern.
private YourClassName()
{
_selectedEmployee = new Employee();
}
public Employee selectedEmployee
{
get
{
return this._selectedEmployee;
}
set
{
if (value != this._selectedEmployee)
{
this._selectedEmployee = value;
NotifyPropertyChanged("selectedEmployee");
}
}
}
}
XAML :
<TextBox x:Name="NameTextBox" Margin="5,5,10,5" TextWrapping="Wrap"
Text="{Binding selectedEmployee.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1" Grid.Column="1" />
So I have a simple UDP chat app from a WinForm project, which I wanted to look a little bit better, so I am re-making it in WPF. As I realized I can easily put 2 or more TextBlocks inside of a ListItem, I wanted to display the last message of each chat, like so:
But I have no Idea on how to edit those TextBlocks :( I literary just started with WPF, so I bet I just made a duplicate, but because of that, I don't even know how to search for this issue.
Here is the custom ListBox:
<ListBox x:Name="myList" HorizontalAlignment="Left" Width="264" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Disabled" BorderThickness="0,1,1,0" MouseLeftButtonUp="myList_MouseLeftButtonUp" Margin="0,25,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="LightGray" BorderThickness="0,0,0,1" Width="250">
<DockPanel Margin="0,7">
<Ellipse Name="ellipse" Margin="5" DockPanel.Dock="Left" Style="{DynamicResource elstyle}">
</Ellipse>
<TextBlock Text="{Binding Name}" DockPanel.Dock="Top" Margin="0,0,0,7" FontWeight="Bold" MaxWidth="250"></TextBlock>
<TextBlock Text="{Binding ID}" DockPanel.Dock="Top" Visibility="Hidden" FontSize="1.333"></TextBlock>
<TextBlock x:Name="last_message" Text="{Binding LastMessage}" DockPanel.Dock="Bottom" MaxWidth="250"></TextBlock>
</DockPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is simplified model to show the principal but if you would create view model class that implement INotifyPropertyChanged interface to hold your item data
public class MyItem : INotifyPropertyChanged
{
private string _name;
private string _id;
private string _lastMessage;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public string ID
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("ID");
}
}
public string LastMessage
{
get { return _lastMessage; }
set
{
_lastMessage = value;
OnPropertyChanged("LastMessage");
}
}
}
and then in your window
public partial class MainWindow : Window
{
private readonly ObservableCollection<MyItem> _myItems = new ObservableCollection<MyItem>();
public MainWindow()
{
InitializeComponent();
myList.ItemsSource = _myItems;
_myItems.Add(new MyItem { Name = "name", ID = "id", LastMessage = "last message" });
_myItems[0].LastMessage = "new message";
}
}
and then you don't operate on myList control anymore but on _myItems list and its items. If you add/remove item in the collection it will add/remove item in the UI, if you change property of an item it will update bound property in the UI
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 have a small program to test out textbox databinding and it works when I change the value in the textbox itself, but when I try to change the value from the code-behind, the variable is updated but the Textbox is not updated. I have looked around but have not been able to find a solution, as most I have seen are for the textbox updating the variable, which is opposite of what I need. Here is the code:
XAML:
<Grid>
<TextBox Text="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="127,37,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox Text="{Binding LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="127,88,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBlock HorizontalAlignment="Left" Margin="28,40,0,0" TextWrapping="Wrap" Text="First Name" VerticalAlignment="Top"/>
<TextBlock HorizontalAlignment="Left" Margin="28,91,0,0" TextWrapping="Wrap" Text="Last Name" VerticalAlignment="Top"/>
<Button x:Name="Name" Content="Name!" HorizontalAlignment="Left" Margin="145,212,0,0" VerticalAlignment="Top" Width="75" Click="Name_Click"/>
</Grid>
And My Code:
namespace WpfApplication1
{
public class UIControls : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _firstName;
private string _lastName;
protected void Notify(string propertyName)
{
if(this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string FirstName
{
get { return _firstName; }
set
{
if (value != _firstName)
{
_firstName = value;
Notify("FirstName");
}
}
}
public string LastName
{
get { return _lastName; }
set
{
if (value != _lastName)
{
_lastName = value;
Notify("LastName");
}
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
UIControls viewModel = new UIControls();
this.DataContext = viewModel;
}
private void Name_Click(object sender, RoutedEventArgs e)
{
UIControls ui = new UIControls();
ui.FirstName = "Mike";
ui.LastName = "Smith";
}
}
}
Well you are creating new UIControls on every button click. New UIControls is not binded to text box. Try to create private filed that is binded to MainWindow, something like this:
public partial class MainWindow : Window
{
private UIControls viewModel;
public MainWindow()
{
InitializeComponent();
viewModel = new UIControls();
this.DataContext = viewModel;
}
private void Name_Click(object sender, RoutedEventArgs e)
{
viewModel.FirstName = "Mike";
viewModel.LastName = "Smith";
}