I'm making a WPF Application and I want the Text within my Textbox to change as I change the variable value, however, although I'm setting the variable correctly I can't get it to update the Textbox.
I have this class:
public class UserSettings : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string username;
public string nameuser
{
get { return username;} set { username= value; OnPropertyChanged(nameuser); }
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Which gets called in this class:
public partial class User: Window
{
private UserSettings objsettings = null;
public User()
{
objsettings = new UserSettings();
DataContext = objsettings;
InitializeComponent();
Console.WriteLine("objsettings.username1: " + objsettings.nameuser);
}
public void SetUserSettings(string username)
{
Console.WriteLine("Username: " + username);
objsettings.nameuser= username;
Console.WriteLine("objsettings.username2: " + objsettings.nameuser);
}
and the XAML is:
<TextBox Text="{Binding nameuser, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="210,193,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="30"/>
The console print in the SetUserSettings prints the value however the console print at the top prints the value as nothing. I'm aware that the top value is printing nothing as it gets set AFTER the console print is called (hence it wouldn't contain a value yet), but how do I update it so the window prints the new value, how do I get it to continously loop through User() without opening new windows but just update the values?
You send incorrect Notify:
get { return username;} set { username= value; OnPropertyChanged(nameof(nameuser)); }
You should send not nameuser, but nameof(nameuser).
As for initial username pass it to the User class:
public User(string username)
{
objsettings = new UserSettings();
objsettings.nameuser = username;
DataContext = objsettings;
InitializeComponent();
Console.WriteLine("objsettings.username1: " + objsettings.nameuser);
}
You send an incorrect Notification. You should send nameof(property name) instead the property value.
In addition, to increase the performance, we don't send notifications if there is no change in property value:
public string nameuser
{
get {
return username;
}
set {
if(username == value)
{
return;
}
username= value;
OnPropertyChanged(nameof(nameuser));
}
}
OnPropertyChanged method expects the property name as string. In your case the code compiles since nameuser is also of type string. But essentially you are just telling the UI to listen for changes in property whose name is same as value of nameuser.
Changing it to the correct property name as OnPropertyChanged("nameuser"), will get the results you want.
In C# version 6 you can also use the nameof feature as it ensures that there are no magic strings in the code OnPropertyChange(nameof(IsBarNull));
Related
I have exactly the same requirements as this user: MVVM Editable ComboBox Bindings.
I've tried the accepted answer:
"Bind a property like "EditedServerName" to Combobox.Text. When the
"EditedServerName" is changed you can set the value to the
"ServerName" of your SelectedServer."
But it's not working because when I try to intercept "EditedServerName" the "SelectedServer" is null. I believe it's because the control tries to search the "ServerName" that is being edited, in the collection, and obviously fails to retrieve an element. This is very clear when I start editing and the textblock with the "ServerID" just gets immediately empty.
XAML:
<ComboBox IsEditable= "True"
ItemsSource= "{Binding Servers}"
DisplayMemberPath= "ServerName"
SelectedItem="{Binding SelectedServer}"
Text= "{Binding EditedServerName, UpdateSourceTrigger=LostFocus}" />
<TextBlock Text="{Binding SelectedServer.ServerID}"/>
ViewModel:
public List<Server> Servers { get; set; }
public Server SelectedServer { get; set; }
private string editedServerName;
public string EditedServerName
{
get { return editedServerName; }
set
{
editedServerName = value;
SelectedServer.ServerName = value;
}
}
public MainViewModel()
{
Servers = new List<Server>();
Servers.Add(new Server { ServerID = 0, ServerName = "Local" });
Servers.Add(new Server { ServerID = 1, ServerName = "Remote" });
}
I know I could temporarily store the "SelectedServer" on another object, but I would like a better turn-around if possible.
A couple of things:
Your MainViewModel is not making proper use of the INotifyPropertyChanged interface, which is recommended for data binding back to the view. This is the reason that there's no change to the UI with your currently posted code.
You're correct in saying that once the EditedServerName is changed, the SelectedServer will become null, but with point 1, it's getting to that point either. Even if we fix the code to accommodate data binding, the SelectedServer property is not handling null.
With that in mind, here's what the code might look like if we modify the code to accommodate the above points:
Below code helps notify the view of any changes to our Models / ViewModels
public class BaseNotifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Here's what our updated model looks like. Note the use of OnPropertyChanged which is defined in BaseNotifier, as this will update the View with the new server name
public class Server : BaseNotifier
{
private string _serverName;
public string ServerName
{
get { return _serverName; }
set
{
_serverName = value;
OnPropertyChanged();
}
}
private int _serverID;
public int ServerID
{
get { return _serverID; }
set
{
_serverID = value;
OnPropertyChanged();
}
}
}
This is what the ViewModel now looks like. For ViewModels, use ObservableCollection instead of List to allow autmatic view updates when inserting data. You'll also notice that the code checks for null in both the SelectedServer and EditedServerName which should handle your original question.
public class MainViewModel : BaseNotifier
{
private string editedServerName;
private Server selectedServer = null;
public ObservableCollection<Server> Servers { get; set; }
public Server SelectedServer
{
get { return selectedServer; }
set
{
if (value != null)
{
selectedServer = value;
OnPropertyChanged();
}
}
}
public string EditedServerName
{
get { return editedServerName; }
set
{
editedServerName = value;
if (SelectedServer != null)
{
SelectedServer.ServerName = value;
}
OnPropertyChanged();
}
}
public MainViewModel()
{
Servers = new ObservableCollection<Server>();
Servers.Add(new Server { ServerID = 0, ServerName = "Local" });
Servers.Add(new Server { ServerID = 1, ServerName = "Remote" });
}
}
I want to bind my label to my model which works fine but the actual value of the property does not get updated, I also implemented INotifyPropertyChanged
Am I doing something wrong here?
My Model
public string ErgebnisBasisPaketPreisString {
get
{
return _ergebnisBasisPaketPreis = (BasisPaketPreis[(int) Basispaket] * (BasisPaketInterval + 1)).ToString("C0");
}
set
{
_ergebnisBasisPaketPreis = value;
OnPropertyChanged(nameof(ErgebnisBasisPaketPreisString));
}
}
My Label:
<Label x:Name="LabelPreisBasispaketIntervall"
Text="{Binding ErgebnisBasisPaketPreisString}"
Grid.Column="3"
VerticalOptions="Center"/>
And I am setting the binding context to my Content page.
ContentPage:
public partial class GeräteKonfiguration : ContentPage {
public GeräteKonfiguration(User currentUser) {
InitializeComponent();
_currentUser = currentUser;
Initialize();
}
private User _currentUser;
private Gerät _currentGerät;
private void Initialize() {
_currentGerät = _currentUser.ServiceModuleAngebotObj.CurrentGerät;
LabelPreisBasispaketIntervall.SetBinding(Label.TextProperty, _currentGerät.ErgebnisBasisPaketPreisString);
LabelPreisBasispaketIntervall.BindingContext = _currentGerät;
BindingContext = _currentGerät;
ToolBarView.BackButton.Clicked += BackButtonOnClicked;
ToolBarView.BackButton.Text = "blabla";
ToolBarView.TitleText = _currentGerät.Anwendung + " - " + _currentGerät.Antriebsart +
" - " + _currentGerät.Baureihe + " - " + _currentGerät.Typ;
LabelPreisBasispaketIntervall.Text = _currentGerät.ErgebnisBasisPaketPreisString;
}
}
This line in your code behind is overwriting your binding by binding the label text to whatever the value of the property is.
LabelPreisBasispaketIntervall.SetBinding(Label.TextProperty, _currentGerät.ErgebnisBasisPaketPreisString)
This isn't likely to be a valid binding path (which would be ErgebnisBasisPaketPreisString), so the binding won't work.
You then get the correct initial value by setting it here:
LabelPreisBasispaketIntervall.Text = _currentGerät.ErgebnisBasisPaketPreisString;
Remove both of these lines, and the binding should work - your XAML looks correct.
Secondly, your property getter doesn't use the value you set it to - you re-compute it each time. Your property should probably just be:
public string ErgebnisBasisPaketPreisString
{
get { return _ergebnisBasisPaketPreisString; }
set
{
_ergebnisBasisPaketPreisString = value;
OnPropertyChanged(nameof(ErgebnisBasisPaketPreisString));
}
}
And you can set the initial computed value in the constructor. Or stick to the computed property and raise the property changed in the dependent properties:
public string ErgebnisBasisPaketPreisString
{
get { return BasisPaketPreis[(int) Basispaket] * (BasisPaketInterval + 1)).ToString("C0"); }
}
public string Basispaket
{
get { return _basispaket; }
set
{
_basispaket = value;
OnPropertyChanged(nameof(Basispaket));
OnPropertyChanged(nameof(ErgebnisBasisPaketPreisString));
}
}
// and similar for the other properties that are used in the computation
I need to show the WCF service Return Value(LIST) in Silverlight Listbox.
create GetAllAgents Class like,
public class GetAllAgents
{
public List<string> FirstName { get; set; }
public GetAllAgents(List<string> firstName)
{
FirstName = firstName;
}
}
The Following Method used for Consume the WCF Service
public partial class AgentQueue : UserControl
{
ChatACDService.ChatACDServiceClient ChatAcdClient = new ChatACDService.ChatACDServiceClient();
public ObservableCollection<GetAllAgents> _GetAllAgents = new ObservableCollection<GetAllAgents>();
public AgentQueue()
{
InitializeComponent();
LoadAgentList();
this.AllList.DataContext = _GetAllAgents;
}
private void LoadAgentList()
{
ChatAcdClient.GetAllAgentListCompleted += new EventHandler<GetAllAgentListCompletedEventArgs>(ChatAcdClient_GetAllAgentListCompleted);
ChatAcdClient.GetAllAgentListAsync();
}
void ChatAcdClient_GetAllAgentListCompleted(object sender, GetAllAgentListCompletedEventArgs e)
{
if (e.Error != null)
{
}
else
{
// AllAgents.ItemsSource = e.Result;
_GetAllAgents.Add(new GetAllAgents(e.Result.ToList()));
}
}
I use the following code For create List Box in XAML page
<ListBox x:Name="AllList" ItemsSource="{Binding}"
DisplayMemberPath="FirstName"
Margin="403,54,0,35" HorizontalAlignment="Left" Width="101" />
But The Output like ,
I need to show the WCF method's result(return type is list) in Listbox by using ObservableCollection.What are the changes are need to make in above Program?
Actually it works pretty well:
You ask to display the Member Path "FirstName" of your object GetAllAgents.
But the Member Path "FirstName" is a list of string.
So your XAML display what you expect from it: the toString() conversion of your memberPath.
And the default toString of your member path which is FirstName which is a list of string is: System.Collection.Generic.List[System.String]
I guess what you expect is that your list of first name should be the item source of your ListBox.
So if your only need is to display their firstName, just replace
public ObservableCollection<GetAllAgents> _GetAllAgents = new ObservableCollection<GetAllAgents>();
By
public ObservableCollection<string> _GetAllAgents = new ObservableCollection<string>();
and
_GetAllAgents.Add(new GetAllAgents(e.Result.ToList()));
By
foreach (var agentName in e.Result.ToList())
{
_GetAllAgents.Add(agentName);
}
And it will display the name of your agent.
If you need mor than that, you will need to create a viewModel per agent object and a dataTemplate to let know Silverlight how you want it to be display.
Hope it helps.
I have a WPF application that includes ~50 controls that are bound to properties on my business object which implements INotifyPropertyChanged. Here's a quick snippet of my business object:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
// properties begin here
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
// constructor, etc. not shown
}
I also have several validation rules that are used to validate the user input in these controls. I'm using command binding to prevent my user from saving the data if there are any validation errors. My application also includes a "Reset default values" button which, obviously, will reset the default value for all of the properties on my business object. This all works exactly as I'd like it to with one exception. If my user enters invalid data into one or more controls and then clicks the "Reset default values" button, the controls that contain invalid data don't always update as I'd expect. This happens because of the following code in my property setters:
if (_name == value)
{
return;
}
This code exists to prevent unnecessary property changed notifications from occurring when the value entered by my user in the bound UI control is the same value that the property is already set to. As an example, I have an IntegerUpDown control in my UI (this control is part of the Extended WPF Toolkit from Xceed). The default value of the property that my control is bound to is 10. My user deletes the value from the control and my validation rule is triggered which results in a validation error and the UI is updated appropriately with an error adorner, etc. The value of the property that this control is mapped to hasn't been changed so it's still set to 10. Now my user clicks the "Reset default values" button which will result in the default value (10) for the property being reset. However, the value for the property is already set to 10 so the short circuit logic in my setter will return instead of setting the property value.
So now, after my user clicks "Reset default values", I am also forcing an update on my binding target like this:
this.myIntegerUpDown.GetBindingExpression(Xceed.Wpf.Toolkit.IntegerUpDown.ValueProperty).UpdateTarget();
This solves my problem but only for this particular control. Is there any easy way to do this for all of my bound controls without having to specify each one? Thanks.
OnPropertyChanged(new PropertyChangedEventArgs(string.Empty));
This is intended to imply that ALL properties on that object have changed.
Could you do one of the following?
1) Reset the DataContext - Either recreate it, or re-set the property
var context = this.DataContext;
this.DataContext = null;
this.DataContext = context;
2) Loop through all properties programmatically via reflection and manually call OnPropertyChanged with the relevant property names.
var properties = typeof(ViewModel).GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(property.Name));
}
You've mentioned validation and reset values, and of course the obvious one is to persist it.
Why don't you implement IEditableObject Interface on your entity that has three signature methods. BeginEdit(), CancelEdit() and EndEdit()
That way you can easily roll back your entity to the whatever you want, or validate it and lastly persist it. A good example is found here
Sample code
public class Customer : IEditableObject
{
struct CustomerData
{
internal string id ;
internal string firstName ;
internal string lastName ;
}
private CustomersList parent;
private CustomerData custData;
private CustomerData backupData;
private bool inTxn = false;
// Implements IEditableObject
void IEditableObject.BeginEdit()
{
Console.WriteLine("Start BeginEdit");
if (!inTxn)
{
this.backupData = custData;
inTxn = true;
Console.WriteLine("BeginEdit - " + this.backupData.lastName);
}
Console.WriteLine("End BeginEdit");
}
void IEditableObject.CancelEdit()
{
Console.WriteLine("Start CancelEdit");
if (inTxn)
{
this.custData = backupData;
inTxn = false;
Console.WriteLine("CancelEdit - " + this.custData.lastName);
}
Console.WriteLine("End CancelEdit");
}
void IEditableObject.EndEdit()
{
Console.WriteLine("Start EndEdit" + this.custData.id + this.custData.lastName);
if (inTxn)
{
backupData = new CustomerData();
inTxn = false;
Console.WriteLine("Done EndEdit - " + this.custData.id + this.custData.lastName);
}
Console.WriteLine("End EndEdit");
}
public Customer(string ID) : base()
{
this.custData = new CustomerData();
this.custData.id = ID;
this.custData.firstName = "";
this.custData.lastName = "";
}
public string ID
{
get
{
return this.custData.id;
}
}
public string FirstName
{
get
{
return this.custData.firstName;
}
set
{
this.custData.firstName = value;
this.OnCustomerChanged();
}
}
public string LastName
{
get
{
return this.custData.lastName;
}
set
{
this.custData.lastName = value;
this.OnCustomerChanged();
}
}
internal CustomersList Parent
{
get
{
return parent;
}
set
{
parent = value ;
}
}
private void OnCustomerChanged()
{
if (!inTxn && Parent != null)
{
Parent.CustomerChanged(this);
}
}
public override string ToString()
{
StringWriter sb = new StringWriter();
sb.Write(this.FirstName);
sb.Write(" ");
sb.Write(this.LastName);
return sb.ToString();
}
}
Wouldn't it be easier to just always call OnPropertyChanged regardless of whether its the same? How much of a performance boost does that give you?
I am learning to use DelgateCommand from Prism....
In my UI, I have my UserName textbox and PasswordBox:
<TextBox Name="_UserNameTextBox" Text="{Binding UserName, Mode=TwoWay}" />
<PasswordBox Name="_PasswordBox"></PasswordBox>
And my Login Button:
<Button Name="button1" Command="{Binding LoginCommand, Mode=TwoWay}" CommandTarget="{Binding ElementName=_UserNameTextBox, Path=Text}">Login</Button>
Then my ViewModel I have:
string _UserName = string.Empty;
public string UserName
{
get
{
return _UserName;
}
set
{
if (value != _UserName)
{
_UserName = value;
RaisePropertyChanged("UserName");
}
}
}
//For reference the password
PasswordBox _PasswordBox { get; set; }
public DelegateCommand<string> LoginCommand { get; set; }
public LoginViewModel(PasswordBox passwordBox)
{
_PasswordBox = passwordBox;
LoginCommand = new DelegateCommand<string>(
(
//Execute
(str) =>
{
Login(_PasswordBox.Password);
}
),
//CanExecute Delgate
(usr) =>
{
if (string.IsNullOrEmpty(usr) || string.IsNullOrEmpty(_PasswordBox.Password))
return false;
return true;
}
);
}
I can see my UserName is binding properly and I did pass my PasswordBox as referece in ViewModel constructor. When I execute the application the Button is disabled, so I know is binded to the command.
But I never see the CanExecute delgate that I wrote is being check after I type things in UserName and PasswordBox.... And is never enabled...
So what did I done wrong?
EDIT:
=====
So end result is...this?
string _UserName = string.Empty;
public string UserName
{
get
{
return _UserName;
}
set
{
if (value != _UserName)
{
_UserName = value;
RaisePropertyChanged("UserName");
LoginCommand.RaiseCanExecuteChanged();
}
}
}
//For reference the password
PasswordBox _PasswordBox { get; set; }
public DelegateCommand<string> LoginCommand { get; set; }
public LoginViewModel(PasswordBox passwordBox)
{
_PasswordBox = passwordBox;
_PasswordBox.PasswordChanged += delegate(object sender, System.Windows.RoutedEventArgs e)
{
LoginCommand.RaiseCanExecuteChanged();
};
LoginCommand = new DelegateCommand<string>(
(
(str) =>
{
Login(_PasswordBox.Password);
}
),
(usr) =>
{
if (string.IsNullOrEmpty(usr) || string.IsNullOrEmpty(_PasswordBox.Password))
return false;
return true;
}
);
}
Generally speaking, you have to call RaiseCanExecuteChanged whenever the effecting value returned by CanExecute changes. In this specific case you would need to call it whenever the value of the user or password fields changes. But that is exceedingly difficult, because your ViewModel implementation is totally wrong.
Here's what you should do instead:
Expose a Username and a Password property inside your ViewModel. You will need to implement the getters and setters explicitly (i.e. it cannot be an automatic property).
From within your view, bind the contents of the username and password input fields to these properties.
Inside the property setters, call LoginCommand.RaiseCanExecuteChanged.
Here's what will happen when you do this (let's pick the password box for an example):
The user types a character inside the password box.
WPF sets the value of LoginViewModel.Password because of the two-way binding.
The password setter calls RaiseCanExecuteChanged, which raises the CanExecuteChanged event on your command.
The submit button (which has subscribed to that event when you bound it to the command) gets notified.
The button calls CanExecute to see if executing the command is now allowed.
Your delegate runs and returns true, so the button activates itself.
You need to bind the Button.CommandParameter (which will be passed to the Execute and CanExecute), if that binding changes the CanExecute is reevaluted as far as i know.
(I think you are confusing the CommandParameter with the CommandTarget, the CommandTarget is not used inside the command, it is only used to raise a command on a certain element (which can be relevant in terms of command routing and such)