Binding does not work in ViewModel command method MVVM - c#

I'm working on a MVVM WPF application where i bind several values from ViewModel to View.
Now i have created a new ViewModel where i have to bind a value to a TextBox after a Button click. When i tried this simple binding it didn't work for me. To my surprise the binding works when the value is assigned in the constructor.
I'm confused.
ViewModel:
public ABCViewModel{
txtItems = "Hello world"; //this works
}
private string m_stxtItem = "";
public string txtItems
{
get { return this.m_stxtItem; }
set
{
if (this.m_stxtItem != value)
{
this.m_stxtItem = value;
}
}
}
public ICommand BindTextValue { get { return new RelayCommand(SeriesBinding); } }
private void SeriesBinding()
{
txtItems = "Hi"; //does not work
}
XAML:
<TextBox Text="{Binding txtItems,Source={StaticResource ABCViewModel}}" />
<Button Command="{Binding BindTextValue,Source={StaticResource ABCViewModel}}">Click</Button>
Why this didn't work and where am i wrong?

Simply answer: you are missing the INotifyPropertyChanged-implementation required for automatic data binding.
Extended answer to why it works when setting the value in the constructor:
the initial binding (reading of the value) from the view happens AFTER your ViewModel-constructor was called and your value was set

You need to implement the INotifyPropertyChanged interface on your ViewModel.
Here's some MSDN links on what the interface is and how to implement it:
INotifyPropertyChanged Interface
How to: Implement the INotifyPropertyChanged Interface
Basically because you've set the ViewModel as the DataSource for the View, upon construction, the View will look to the ViewModel for its values. From this point onwards the ViewModel needs some mechanism for notifying the View of changes. The View is not periodically updated or anything like that. This is where the INotifyPropertyChanged interface steps in. The WPF framework looks out for these events being fired and makes the View refresh its value.

Related

x:bind UI not update when PropertyChanged in UWP

I'm using x:Bind and INotifyPropertyChanged to update UI in UWP application. But it behaves like OneTime binding even though I set it to OneWay.
Bindings.Update() works, but I want to know why INotifyPropertyChanged fails.
XAML
<TextBlock Text="{x:Bind staffVM.Name, Mode=OneWay}"/>
Code-behind:
private StaffViewModel staffVM;
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// I want to change staffVM according to ListView's selection.
staffVM = staffListView.SelectedItem as StaffViewModel;
staffVM.Update(); // If change this to Bindings.Update(), It works.
}
ViewModel:
public class StaffViewModel: INotifyPropertyChanged
{
private Character character;
public string Name => character.name == string.Empty ? null : character.name;
public void Update()
{
RaisePropertyChanged(string.Empty);
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName]string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
Firstly, you need to specify the name of the variable that you want to update:
public void Update()
{
RaisePropertyChanged(nameof(Name));
}
Documentation and sample: https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.inotifypropertychanged.propertychanged
Secondly, by default x:Bind is OneTime
To fix it, add Mode="OneWay"
Mode Specifies the binding mode, as one of these strings: "OneTime", "OneWay", or "TwoWay". The default is "OneTime". Note that this differs from the default for {Binding}, which is "OneWay" in most cases.
Please read documentation
https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
The problem here is not on the level of the StaffViewModel class, but on the level of the page. When you do:
staffVM = staffListView.SelectedItem as StaffViewModel;
The UI has no notification about the fact that the staffVM field has changed. So the binding is still pointing to the old instance of StaffViewModel. Hence when you do staffVM.Update(), it does notify about changes, but the UI is not listening to that instance - it is still listening to notifications on the first selected item. Bindings.Update() fixes this because it completely re-evaluates all bindings so it will "get" the new value of staffVM field.
Solution would be to implement INotifyPropertyChanged on the Page and encapsulate the staffVM in a property which raises PropertyChanged event.
Ideally I would however suggest creating a "root" view model, which you will set only once and will not change and which will contain the selected item as its property. This way you don't have to implement INotifyPropertyChanged in the Page and its code-behind will be simpler. As a result you will have something like the following in the code-behind:
public RootViewModel VM {get;} = new RootViewModel();
And in XAML:
<TextBlock Text="{x:Bind VM.SelectedStaff.Name, Mode=OneWay}"/>

How to achieve dynamic binding in WPF/MVVC C#

I am rather new to MVVC/wpf, having mostly worked with winforms.
What I want to accomplish is dynamic databinding without using code behind in WPF. The user interface consists of a devexpress grid and a couple of buttons. Each button press loads an object list and presents the objects in the grid. The lists contain different object types depending on the button pressed. For this example I have two classes to present: FatCat and FatDog.
In winforms this works:
private void button1_Click(object sender, EventArgs e)
{
((GridView)gridCtrl.MainView).Columns.Clear();
gridCtrl.DataSource = new BindingSource(itsModel.GetAll<FatDog>(), null);
}
private void button2_Click(object sender, EventArgs e)
{
((GridView)gridCtrl.MainView).Columns.Clear();
gridCtrl.DataSource = new BindingSource(itsModel.GetAll<FatCat>(), null);
}
I have configured the grid to create columns dynamically, so everything just works. itsModel is of type CatClientModel.
In wpf I have defined the DataContext to be CatClientModel.
What should I use for ItemsSource in the grid to achieve the same behaviour as my winforms solution?
dxg:GridControl ItemsSource="{Binding SomeDynamicList}"
In other words, what should SomeDynamicList be in the code above? Or am I going about this the wrong way?
I am, as I stated, using the DevExpress wpf grid control, but the question ought to be general and apply to any control presenting object lists.
In other words, what should SomeDynamicList be in the code above?
SomeDynamicList should be an ObservableCollection<T> property to which you can add any objects of type T that you want to display in the GridControl.
Set the DataContext of the GridControl, or any of its parent elements, to an instance of a class where this property is defined:
public class CatClientModel
{
public ObservableCollection<Animal> SomeDynamicList { get; } = new ObservableCollection<Animal>();
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new CatClientModel();
}
}
Ok. But the thing is that the ObservableCollection contains different types. Unfortunately there is no feasible class to inherit from. I want to bind to either ObservableCollection or ObservableCollection depending on which button was pressed
Switch the DataContext then, or change the property into an IEnumerable and set it to a new collection each time the button is clicked. This requires you to implement the INotifyPropertyChanged interface in your view model
private System.Collections.IEnumerable _collection;
public System.Collections.IEnumerable MyProperty
{
get { return _collection; }
set { _collection = value; OnPropertyChanged(); }
}
If you want to use XAML to define which data sources your code maps to for each grid that is possible. That does require at least some method of MVVM manager either prism or mvvmlight to connect the view model to the view.
so if you do go the MVVM model route, the Model would contain a description for each of your grids like this:
public BulkObservableCollection<icd10facet> FacetList
{
get { return this._facets; }
set { SetProperty(ref this._facets, value); }
}
public INotifyTaskCompletion<BulkObservableCollection<PetsConvert>> ConceptList
{
get { return this._concept; }
set
{
SetProperty(ref this._concept, value);
}
}
In the XAML for your code the grid woud bind to the grid defined by ConceptList in this way:
ItemsSource="{Binding ConceptList.Result}"
this answer does NOT address how to wire up Prism 6.0 for example to use a view model but for examples see:
https://github.com/PrismLibrary/Prism
Which contains documentation and starter code. Keep in mind that there is not any specific reason that putting code in the code behind for the view is a problem, first solve the problem and then refactor if separation of concerns is an issue for you.
Using this technique you can bind each grid to its own data source. In the MVVM space buttons and other things use a commanding model to communicate with the view model.
<Button Content="Load Rule Data" Width="100" Height="40" HorizontalAlignment="Left" Margin="5px" Command="{Binding LoadRuleData }"/>
this requires defining a command delegate in the viewmodel for LoadRuleData
public DelegateCommand LoadRuleData { get; private set; }
and then (usually in the constructor) wire the DelegateCommand to the method that is going to do the work.
this.LoadRuleData = new DelegateCommand(this.loadRules);

UWP - Change button.Visibility from another class

I am trying to change multiple buttons visibility from another class. This is supposed to be easy, but I just don't understand it.
The xaml part is straight forward:
<button x:Name="whatever" Visibility="{Binding whateverName}"
The view-model could be something like this?
private Visibility vis;
public Visibility Vis
{
get { return vis; }
set { vis = value; }
}
But if that is the case, how do I pass my button name?
To go a bit further, a services file is trying to modify the visibility value..
Thanks in advance.
Since you're using Bindings, you don't need the button name identifier.
The connection is made in the Binding part of the XAML:
<Button x:Name="whatever" Visibility="{Binding whateverName}"/>
What is happening there is that you are saying the Visibility property of the whatever button will be bound to the whateverName property value in your view model.
So your View model needs to look like this:
private Visibility vis;
public Visibility whateverName
{
get { return vis; }
set { vis = value; }
}
To change the visibility of your button you need to change the value of whateverName in your view model.
However, if you try, you'll notice that that won't work. The reason is that in order for the change to take effect on the button, the View model must notify the view that its property has changed. This is done with the INotifyPropertyChanged interface.
So your view model will need to look something like this:
public class Viewmodel : INotifyPropertyChanged
{
private Visibility vis;
public Visibility whateverName
{
get { return vis; }
set
{
vis = value;
OnPropertyChanged("whateverName");
}
}
public void OnPropertyChanged(string pName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
In the PropertyChanged event you must pass the property name that you want to notify. In my example I just used a string value that matches the property name but there are various techniques to eliminate that "magic string".
Here's one SO question that has good answers.

TextBox.Text binding to a ViewModel's property

I have a problem with two way binding a TextBox content to a property in another class. Searching stackoverflow gave a lot of tips/solutions but none seem to work.
In my XAML code I have:
< TextBox ... Width="336" IsReadOnly="True"
Text="{Binding Path=AssignedClearProgram, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
(... I removed all non important items)
In the accompanies cs code I have:
public CombiWindow(Combi combi)
{
ViewModel = new CombiViewModel(combi);
DataContext = ViewModel;
}
In the CombiViewModel:
[UsedImplicitly]
public string AssignedClearProgram { get; set; }
It seems that the first time I assign AssignedClearProgram, the textbox is filled with the text that I set, however after the window is displayed and AssignedClearProgram gets updated from the code (i.e. the set method is called), the data is not updated in the screen.
Does anybody have a solution to update the textbox when this variable is changed?
Kind regards,
Michel
Your viewmodel class needs to implement INotifyPropertyChanged and you need to raise that interface's event whenever you change the property. Then the binding will spot changes and update the textbox.
Your view model class should implement the INotifyPropertyChanged interface.
Your property would then look like the following:
private string assignedClearProgram;
public string AssignedClearProgram
{
get { return assignedClearProgram; }
set
{
if (assignedClearProgram != value)
{
assignedClearProgram = value;
// Notify property has changed here using PropertyChanged event from INotifyPropertyChanged.
}
}
}
Read this article for an example of how to implement the INotifyPropertyChanged interface and utilize its PropertyChanged event.

Is there an easy way to bind a command to a listbox in MVVM WPF?

I have been trying to create a fairly simple application in WPF following the MVVM development pattern but I have been going crazy over how difficult it seems to be to do simple things. I have already created this app in Forms and had it successfully running, but my boss requested I rewrite the interface in WPF as a demo of the technology. I decided to try to follow as many best practices as I can in order to make the app and code as educational as possible. My current dilemma is using a listbox to run some code every time the selection changes. I'm ready to just use the code-behind with an event to call the method on the view-model. To me this seems to still be essentially MVVM since no logic is executing. Thanks for any help/insight.
You can do that simply binding selecteditem property of listbox... on selection change a setter in the view model will be called and you can do what ever you want...
Here is a sample which will help you
XAML
<Grid Canvas.Left="0" Canvas.Bottom="0" Height="300" Width="300" Background="Bisque">
<ListBox ItemsSource="{Binding Employes}" SelectedItem="{Binding SelectedEmploye}"/>
</Grid>
View Model
public class ViewModel : ViewModelBase
{
private List<Employee> _employes;
public List<Employee> Employes
{
get { return _employes; }
set { _employees = value; OnPropertyChanged("Employes"); }
}
private Employee _selectedEmploye;
public Employee SelectedEmploye
{
get { return _selectedEmploye; }
set
{
_selectedEmployee = value;
OnPropertyChanged("SelectedEmploye");
}
}
}
View model base
public class ViewModelBase : INotifyPropertyChanged
{
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Employee Class
public class Employee : ViewModelBase
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
In your ViewModel you can create a Property "SelectedItem". Bind then the SelectedItem-property if your ListBox to your property.
If it's a POCO clr-property (INotifyPropertyChanged), then you can trigger your code from the properties setter.
IF it's a DependencyProperty, you have to add a PropertyChangedCallback and trigger your code from there.
Don't be afraid to use code-behind. No code-behind is a guideline to avoid too much logic being placed in the view, not a hard and fast rule. In this case (as others have suggested) you can bind the SelectedItem property to some property on your viewmodel. With non-data-related events, my recommendation would be to handle the event as normal and delegate execution logic to the viewmodel.
In my opinion, design patterns should always be taken as rule of thumb and used with some judgement as it's quite easy to apply them too strictly in areas where they don't belong, which usually makes things worse.
Checkout the EventToCommand behavior in Galasoft MVVM Light
Here's the SO post
you can bind to ListBox.SelectedItem to get the selected item in your vm.

Categories