User control binding is not properly working - c#

I kinda stuck on data-binding here. So, let's say I have a following Class :
Student.cs
public class Student : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string _name;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name");
}
public bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set { _isSelected = value; NotifyPropertyChanged("IsSelected");
}
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then, I have a UserControl to visualize the class, with the following ViewModel :
StudentVisualizerVM.cs
public class StudentVisualizerVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Student _student;
public Student Student
{
get { return _student; }
set { _student = value ; NotifyPropertyChanged("Student"); }
}
public StudentVisualizerVM()
{
Student = new Student();
}
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And then, I want to use the UserControl in my MainWindow.
MainWindow.Xaml
<ItemsControl Grid.Column="0" Grid.Row="0" ItemsSource="{Binding Student}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<c:StudentVisualizer/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And finally this is my MainWindow ViewModel look like :
MainWindowVM.cs
public class MainWindowVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Student _student;
public Student Student
{
get { return _student; }
set { _student = value ; NotifyPropertyChanged("Student"); }
}
public MainWindowVM()
{
Student = new Student();
}
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The problem is that the User Control didn't shows up on MainWindow even though I had initialize the Student Property in MainWindow. Why? Isn't MVVM User Control automatically populate its ViewModel? Or maybe it's StudentVisualizerVM that I need to store in MainWindowVM, instead of the Student itself?
Note That (Updated) :
Above code is not the real one, it's just to simplify my project into the main problem.
Assume each XAML's DataContext has been attached correctly from the XAML.CS.
Why I need to do this? Because I need to detect if the window has been clicked outside the StudentVisualizer then IsSelected is set to false. (This property used for certain Command which need the Student to be selected.) - Something like Select-Deselect-esque actio.

ItemsControl is for displaying a collection of items, therefore ItemsControl.ItemsSource has to bind to an IEnumerable property, while you just bind to a single element.
You want something like :
ItemsSource="{Binding StudentCollection}"
public IEnumerable<Student> StudentCollection
{
get { return new List<Student> { Student }; }
}
Now, should you really use an ItemsControl to display just one item.. that's another question and the answer would be : no.

Related

How to update a List when binded to a ListView

I have a ListView which binds each list view item to a Class A which has a List
<Page
DataContext="{Binding MainViewModel , Source={StaticResource Locator}}"
>
<ListView ItemsSource="{Binding A.List}" >
<ListView.ItemTemplate>
....
</ListView.ItemTemplate>
</ListView>
</Page>
And in my ViewModel, I have the class 'A' and it has a property List
public class MainViewModel : ViewModelBase {
private A _a;
public A A {
get {
return _a;
}
}
}
public class A
{
private IList<IList> _lists;
IList<int> List {
get {
return _lists;
};
set {
_lists = value;
RaisePropertyChanged("List");
}
}
In the set() method I has called 'RaisePropertyChanged()' whenever the List is set.
But when I run it, the ListView content does not get update.
Should I raise RaisePropertyChanged("List") or RaisePropertyChanged("A.List") (like what I put in {Binding A.List] in my xaml? In my case, I set the List to another instance of a List.
Try this see if it works.
public class A : INotifyPropertyChanged
{
private IList<int> _lists;
IList<int> List {
get {
return _lists;
};
set {
_lists = value;
OnPropertyChanged("List");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Listbox Databinding Not working

I am unable figure out why the databinding is not working as expected:
I created a Listbox and set its ItemSource to my observable collection
I used this.DataContext = this
I Initialized my public Observable Collection
I filled it with objects that implement INotifyPropertyChanged
Yet, the databinding, still does not work. My Listbox:
<ListBox Height="425" ItemsSource="{Binding headers}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=HeaderInfo}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The code behind:
public partial class cornet_controls : PhoneApplicationPage
{
public ObservableCollection<headerInfo> headers;
public cornet_controls()
{
InitializeComponent();
this.DataContext = this;
headers = new ObservableCollection<headerInfo>();
for (int x = 0; x < 100; x++)
headers.Add((new headerInfo() { HeaderInfo = x.ToString() }));
}
}
My custom class implementing INotifyPropertyChanged:
public class headerInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public headerInfo()
{}
private String _HeaderInfo;
public String HeaderInfo
{
get { return _HeaderInfo; }
set { _HeaderInfo = value; NotifyPropertyChanged("HeaderInfo"); }
}
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You cannot bind to a NonProperty:
<ListBox Height="425" ItemsSource="{Binding headers}">
public ObservableCollection<headerInfo> headers;
you need to bind to a Property like:
public ObservableCollection<headerInfo> headers { get; set; }

Do I need to call NotifyPropertyChange() in code-behind?

In my program I would like to disable a contentPresenter when my other contentPresenter gets focus. Each presenter is represented by a property located in my MainWindowViewModel. This is also where the IsEnabled property is located for both presenters.
Both contentPresenters are created with the following structure: UserControl -> ViewModel -> Data Model.
Right now I am trying to disable the necessary contentPresenter by changing the IsEnabled property in the main window's ViewModel from the code-behind of the contentPresenter that gets focus.
contentPresenter User Control code-behind:
public partial class EditBlockUC : UserControl
{
public EditBlockViewModel ViewModel { get { return DataContext as EditBlockViewModel; } }
public EditBlockUC()
{
InitializeComponent();
}
//Runs when the user control gets focus
private void UserControl_GotFocus(object sender, RoutedEventArgs e)
{
//This UserControl has access to MainWindowViewModel through
//it's own ViewModel, EditBlockViewModel
ViewModel.MainViewModel.LeftWidgetEnabled = false;
}
}
The line: ViewModel.MainViewModel.LeftWidgetEnabled = false; successfully changes the property in the Main window's view model, but the view is not affected. Can I fix this by finding a way to call NotifyPropertyChange()? If so, how would I do that?
If this is the completely wrong solution please let me know, and help me fix it.
Thank you
Update 1:
My complete base class:
public class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public virtual void NotifyPropertyChange<TProperty>(Expression<Func<TProperty>> property)
{
var lambda = (LambdaExpression)property;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
memberExpression = (MemberExpression)lambda.Body;
OnPropertyChanged(memberExpression.Member.Name);
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
Update 2:
My LeftWidgetEnabled property:
public bool LeftWidgetEnabled
{
get { return _leftWidgetEnabled; }
set { SetField(ref _leftWidgetEnabled, value, "LeftWidgetEnabled"); }
}
The LeftWidgetEnabled of your ViewModel.MainViewModel class must be like this:
private bool leftWidgetEnabled;
public bool LeftWidgetEnabled
{
get { return leftWidgetEnabled; }
set { SetField(ref leftWidgetEnabled, value, "LeftWidgetEnabled"); }
}
Also, your MainViewModel must implement INotifyPropertyChanged.
You're better off letting the MainViewModel inherit from a ViewModelBase and let ViewModelBase implement INotifyPropertyChanged.
public class MainViewModel : ViewModelBase
{
private bool leftWidgetEnabled;
public bool LeftWidgetEnabled
{
get { return leftWidgetEnabled; }
set { SetField(ref leftWidgetEnabled, value, "LeftWidgetEnabled"); }
}
}
public class ViewModelBase : INotifyPropertyChanged
{
// boiler-plate
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
Update 1
Your ContentPresenter should then be bound like:
<ContentPresenter IsEnabled="{Binding Path=LeftWidgetEnabled}" />
while the DataContext of your UserControl (where the ContentPresenter is on) should be an instance of MainViewModel.
For instance:
<UserControl
x:Class="MyApplication.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="**PATH TO YOUR VIEWMODELS-ASSEMBLY**"
mc:Ignorable="d">
<UserControl.DataContext>
<viewModels:MainViewModel />
</UserControl.DataContext>
<ContentPresenter IsEnabled="{Binding Path=LeftWidgetEnabled}" />
</UserControl>
You implement INotifyPropertyChanged as below
class ViewModel : INotifyPropertyChanged
{
private bool leftWidgetEnabled;
public bool LeftWidgetEnabled
{
get
{
return leftWidgetEnabled;
}
set
{
leftWidgetEnabled=value
OnPropertyChanged("LeftWidgetEnabled");
}
}
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}

How can i set binding to childproperty from App.xaml

I have a problem with my Binding to a ListBox Control.
Actually i have a Property in App.xaml.cs :
public partial class App : Application, INotifyPropertyChanged
{
ObservableCollection<Panier> _panier = new ObservableCollection<Panier>();
public ObservableCollection<Panier> PanierProperty
{
get { return _panier; }
set
{
if (this._panier != value)
{
this._panier = value;
NotifyPropertyChanged("PanierProperty");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
this property have a child properties in the "Panier class" here:
public class Panier : INotifyPropertyChanged
{
private string _nom;
private string _category;
private int _prix;
public string Nom
{
get { return _nom; }
set
{
if (this._nom != value)
{
this._nom = value;
NotifyPropertyChanged("Nom");
}
}
}
public string Category
{
get { return _category; }
set
{
if (this._category != value)
{
this._category = value;
NotifyPropertyChanged("Category");
}
}
}
public int Prix
{
get { return _prix; }
set
{
if (this._prix != value)
{
this._prix = value;
NotifyPropertyChanged("Prix");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
and in my MainWindow.xaml page i have my ListBox bound to PanierProperty(parent property) :
<telerik:RadListBox x:Name="PanierLB" Grid.Row="1" Height="200" Width="350" Margin="0 300 0 0"
ItemsSource="{Binding PanierProperty, Source={x:Static Application.Current}}"
DisplayMemberPath="{Binding Path=PanierProperty.Nom, Source={x:Static Application.Current}}">
</telerik:RadListBox>
my problem is that PanierProperty is bound to my Listbox i see items in the listbox like Design.Panier
Design.Panier
Design.Panier
etc...
I dont know how to get the PanierProperty.Nom(Nom is the child property) to show on the ListBox.
Someone can help please.
In DisplayMemberPath use the name of property you want to show only:
<telerik:RadListBox
...
DisplayMemberPath="Nom"

How can I bind a field to a user control

In my user control I have this property:
public static DependencyProperty FooListProperty = DependencyProperty.Register(
"FooList", typeof(List<Problem>), typeof(ProblemView));
public List<Problem> FooList
{
get
{
return (List<Problem>)GetValue(FooListProperty);
}
set
{
SetValue(FooListProperty, value);
}
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == FooListProperty)
{
// Do something
}
}
And since another window, I´m trying to set a value for the last user control:
<local:ProblemView HorizontalAlignment="Center"
VerticalAlignment="Center" FooList="{Binding list}" />
And that window in load contains:
public List<Problem> list;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Some processes and it sets to list field
list = a;
}
But in XAML code, binding it isn't working. Don't pass the data. What am I wrong?
You can't bind to a Field in WPF, you'll have to change list to a property instead.
You call the Dependency Property FooList in your UserControl and ResultList in Xaml but I'm guessing that's a typo in the question.
You should implement INotifyPropertyChanged in the Window to let the Bindings know that the value has been updated.
I'm not sure if you have the correct DataContext set in the Xaml ProblemView, if you're unsure you can name the Window and use ElementName in the binding
<Window Name="window"
...>
<!--...-->
<local:ProblemView HorizontalAlignment="Center"
VerticalAlignment="Center"
ResultList="{Binding ElementName=window,
Path=List}" />
<!--...-->
</Window>
Sample code behind
public partial class MainWindow : Window, INotifyPropertyChanged
{
//...
private List<Problem> m_list;
public List<Problem> List
{
get { return m_list; }
set
{
m_list = value;
OnPropertyChanged("List");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}

Categories