I have a login page in my application and on the page i have a textbox that shows the error in case of wrong username/password, etc. I am updating the error from view model, but the view is not changing automatically.
The viewmodel implements INotifyPropertyChanged interface.
Error definition.
string _error;
public string Error
{
get { return _error; }
set
{
_error = value;
NotifyPropertyChanged("Error");
}
}
INotify Event Handlers
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
update
_error = "Wrong username/password!";
xaml
<TextBlock Text="{Binding Error, UpdateSourceTrigger=PropertyChanged}"/>
What else am i missing?
set your error like this, you're just changing the private member that isn't being binded to:
Error="Wrong username/password!";
not _error
Related
How to correctly setup toggleswitch with command? I am using behavior to launch command using Toggled event. However I have problem when user changed state of switch, but my model rejects it. I do not know how to correctly program it, so view rejects update if it was rejected by model.
Here are my attempts to do it (INotifyPropertyChanged details skipped for clarity):
1. One way command
class ToggleSwitchVm {
public bool IsOn => Model.IsOn;
public ICommand SwitchManipulated {get;}
}
When user manipulates ToggleSwitch state in UI changes, but it is not related to state in model. Also toggleswitch ignores PropertyChanged events, while it is calling command:
async void SwitchManipulatedCommand(bool? state) {
if(!Model.SetNewState(state.Value)) { // if failed to set state raise PropertyChanged to refresh view
RaisePropertyChanged(nameof(IsOn)); // this call is ignored :(
}
}
2. Two way property?
class ToggleSwitchVm {
private void ModelOnPropertyChanged(object sender, PropertyChangedArgs args){
if(args.PropertyName == nameof(Model.IsOn)){
IsOn = Model.IsOn;
}
}
private bool _isOn;
public bool IsOn
{
get => _isOn;
set {
if(SetValue(ref _isOn, value))
{ // true if model was manipulated
// View set value, or model set value?
if(!Model.SetState(value))
{
RaisePropertyChanged(nameof(IsON)); // ignored by view
}
}
}
}
}
This is a mess, because both Model and View set IsOn property and there is no way to know which one set it.
How to MVVM toggleswitch in UWP
The ToggleSwitch IsOnProperty is DependencyProperty, that means it could use to bind data with two way model. And you have no need to process the IsOn proprty in command method. Please refer the following code to implement the feature.
Xaml Code
<Page.DataContext>
<local:ViewModel />
</Page.DataContext>
<Grid>
<ToggleSwitch
Header="Toggle work"
IsOn="{Binding IsOn, Mode=TwoWay}"
OffContent="Do work"
OnContent="Working"
/>
</Grid>
Code behind
public class ViewModel: INotifyPropertyChanged
{
private bool _isOn;
public bool IsOn
{
get { return _isOn; }
set { Set(ref _isOn, value); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I have a GridControl(Devexpress v13) in view(WPF). A Datatable set values in ViewModel and assigned to ItemsSource. But ItemsSource filled only initialize. Later Datatable's value changes but it doesn't refresh.
How to ItemsSource refresh?
<dxg:GridControl Name="GridControlData" DataSource="{Binding DtCriterias, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" AutoGenerateColumns="AddNew" Width="400" Height="100">
I hope you know what I mean.
Any help will be much appreciated.
Thanks in advance.
Edit:
Property changed using:
public DataTable DtCriterias {
get { return _dtCriterias; }
set
{
_dtCriterias = value;
Notify(() => DtCriterias);
}
}
protected void Notify(Expression<Func<object>> expression)
{
if (_propertyChangedEvent == null) return;
Notify(GetPropertyName(expression));
}
protected void Notify(string propertyName)
{
if (_propertyChangedEvent != null)
{
_propertyChangedEvent(this, new PropertyChangedEventArgs(propertyName));
}
}
public ObservableCollection<ClientB2B> Clients
{
get
{
return _clients;
}
set
{
if (_clients == value) return;
_clients = value;
OnPropertyChanged(); // This is what you need
}
}
Implement this interface - INotifyPropertyChanged
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
I found reason of problem.
I am using encapsulation and I was set private value(_dtCriterias). Therefore Property Changed Event wasn't work.
Definitions:
private DataTable _dtCriterias;
public DataTable DtCriterias {
get { return _dtCriterias; }
set
{
_dtCriterias = value;
Notify(() => DtCriterias);
}
}
When I have problem datatable set:
_dtCriterias = GetValue().DefaultView.ToTable("FooTable");
Solution:
DtCriterias = GetValue().DefaultView.ToTable("FooTable");
So I've got the following ComboBox with the SelectedValue bound to the Property below. With the following binding, when I set value, the binding/RaisePropertyChanged combination is throwing a StackOverflow Exception.
Here's the ComboBox
<ComboBox x:Name="WireType" ItemsSource="{x:Bind ViewModel.WireTypes}" SelectedValue="{x:Bind ViewModel.WireType, Mode=TwoWay}"/>
Here's the Property
public string WireType
{
get
{
return _wireType;
}
set
{
_wireType = value;
RaisePropertyChanged();
}
}
And here's the RaisePropertyChanged method.
private void RaisePropertyChanged([CallerMemberName] string caller = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(caller));
}
}
I'm pretty sure I've done this before. What am I missing?
My psychic powers suggest that the PropertyChanged event is trying to set the property value.
The setter should protect against the case where the value didn't change. ie-
set
{
if (_wireType != value) // or the appropriate comparison for your specific case
{
_wireType = value;
RaisePropertyChanged();
}
}
Of course a stack trace would confirm what's actually happening.
Try this
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string caller = "")
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
My problem is, that the UI isn't updating if they call the setter of the property which they binded to.
Here's a sample to make it clear:
Let's say I have a textbox binded to a property like this.
<TextBox PlaceholderText="Task Name..." FontSize="24"
Text="{Binding TaskName, Mode=TwoWay}" />
And this is my property:
public string TaskName
{
get
{
return _taskName;
}
set
{
_taskName = "something";
RaisePropertyChanged();
}
}
If I write something into the textbox then "something" should appear inside of it, after it loses focus, but there isn't any change. However, if I change the value of the property with code, like this:
TaskName = "something";
Then the change will appear on the UI as well.
Some further information.
This is how I implemented the INotifyPropertyChange interface:
public class ViewModelBase : INotifyPropertyChanged
{
public static Navigator NavigationService;
public static void SetNavigationService(Navigator service)
{
NavigationService = service;
}
protected void GoBack()
{
NavigationService.GoBack();
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I really don't know why is it behave like this. I search for it for hours, but can't find anything.
in the setter of the property you need to call
RaisePropertyChanged(x => x.TaskName)
I'm new to WPF, so there's probably something basic I'm missing here. I have an application that looks like this:
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test Application" Height="647" Width="723" Background="#88B0FF">
<DockPanel Name="MainDock">
<Button DockPanel.Dock="Top" Margin="5,0,5,0" x:Name="PingButton" Click="PingButton_OnClick">Ping</Button>
<TextBox Text="{Binding Path=Output}" />
</DockPanel>
</Window>
The code-behind is like this:
public partial class MainWindow : Window
{
private Model _applicationModel = new Model();
public Model ApplicationModel {
get { return _applicationModel; }
set { _applicationModel = value; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = ApplicationModel;
ApplicationModel.Output = "Not clicked";
}
private void PingButton_OnClick(object sender, RoutedEventArgs e)
{
ApplicationModel.Output = "Clicked";
}
}
I have a small class called Model that implements INotifyPropertyChanged.
public class Model : INotifyPropertyChanged
{
public string Output { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I run this application, and the text box displays the text "Not clicked". When I click the button, I would expect that the text would change. It does not. The "ApplicationModel" object is updated, and this is reflected in the DataContext; I have a breakpoint in the OnPropertyChanged() method, however, and it appears that it's never being called.
What am I doing wrong?
OnPropertyChanged() isn't being called because you're not calling it.
There is no special magic that wires up calls to OnPropertyChanged by itself, so you need to do it yourself.
Specifically, you should modify your Output property to call it when it changes (and it wouldn't hurt to do the same for your ApplicationModel property:
private string output;
public string Output
{
get { return output; }
set
{
if (output != value)
{
output = value;
OnPropertyChanged("Output");
}
}
}
If you're targeting .NET 4.5 you can utilize the CallerMemberName attribute to reduce boilerplate code; This article explains how to do so. Then you'll have something like this:
private string output;
public string Output
{
get { return output; }
set { SetProperty(ref output, value); }
}
If you're using .NET 4.0 or below, you can use expression trees, as described in this answer.