On my WPF app page i have 2 bindings
1. for a list of items as an observable collection
2. just to show some values on a lable using binding
My class struct like this
public DbVersionModel DbVersion { get; set; }
public ObservableCollection<BookStore> StoreCollection
{ get { return thisApp.app_Stores; } }
public class DbVersionModel : INotifyPropertyChanged
{
private int _LocalVersion { get; set; }
private int _ServerVersion { get; set; }
private int _ActiveStores { get; set; }
private string _LastModifiedLocal { get; set; }
private string _LastModifiedServer { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyChange(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
public int LocalVersion
{
get { return _LocalVersion; }
set
{
_LocalVersion = value;
NotifyChange(new PropertyChangedEventArgs("LocalVersion"));
}
}
public int ServerVersion
{
get { return _ServerVersion; }
set
{
_ServerVersion = value;
NotifyChange(new PropertyChangedEventArgs("ServerVersion"));
}
}
public int ActiveStores
{
get { return _ActiveStores; }
set
{
_ActiveStores = value;
NotifyChange(new PropertyChangedEventArgs("ActiveStores"));
}
}
public string LastModifiedLocal
{
get { return _LastModifiedLocal; }
set
{
_LastModifiedLocal = value;
NotifyChange(new PropertyChangedEventArgs("LastModifiedLocal"));
}
}
public string LastModifiedServer
{
get { return _LastModifiedServer; }
set
{
_LastModifiedServer = value;
NotifyChange(new PropertyChangedEventArgs("LastModifiedServer"));
}
}
} public ManagePage()
{
InitializeComponent();
setContext();
}
private void setContext()
{
try
{
DbVersionModel db_version = new DbVersionModel();
db_version.LastModifiedServer = //set with value;
db_version.ServerVersion = //set with value;
db_version.LocalVersion = //set with value;
db_version.LastModifiedLocal = //set with value;
db_version.ActiveStores = //set with value;
this.DbVersion = db_version;
}
catch
{
}
}
xaml like this
<Page x:Class="App.ManagePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Page.Resources>
</Page.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" >
<Grid Margin="5 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"></ColumnDefinition>
<ColumnDefinition ></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<!--// -->
<Label Content="Active stores[Local]" Grid.Column="0" Grid.Row="0" ></Label>
<Label Content="{Binding Source=DbVersion, Path=ActiveStores}" Grid.Column="1" Grid.Row="0" ></Label>
<Label Content="Current version [Local]" Grid.Column="0" Grid.Row="1" ></Label>
<Label Content="{Binding Source=DbVersion, Path=LocalVersion}" Grid.Column="1" Grid.Row="1" ></Label>
<Label Content="Curretn Version [Server]" Grid.Column="0" Grid.Row="2" ></Label>
<Label Content="{Binding Source=DbVersion, Path=ServerVersion}" Grid.Column="1" Grid.Row="2" ></Label>
<Label Content="Last modified [Local]" Grid.Column="0" Grid.Row="3" ></Label>
<Label Content="{Binding Source=DbVersion, Path=LastModifiedLocal}" Grid.Column="1" Grid.Row="3" ></Label>
<Label Content="Last modified [Server]" Grid.Column="0" Grid.Row="4" ></Label>
<Label Content="{Binding Source=DbVersion, Path=LastModifiedServer}" Grid.Column="1" Grid.Row="4" ></Label>
<StackPanel Grid.Column="0" Grid.ColumnSpan="5" Grid.Row="6" HorizontalAlignment="Center">
<Button Name="btnSync" Content="Sync stores" Height="30" Click="btnSync_Click" Style="{StaticResource ActionButtons}"></Button>
</StackPanel>
</Grid>
</ScrollViewer>
Also have a ItemsControl which is getting loaded like this and binding is working fine here
<ItemsControl Name="StoreListView" ItemsSource="{Binding StoreCollection}" Margin="5" VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling" >
But ObservableCollection list binding is fine , but the label bindings are not working. How i can solve this?
How can i set binding on label based on properties of a class
I think WPF does not notice the change of the DbVersion property because you set it after calling InitializeComponent(). Either you implement it as a DependencyProperty, which automatically notices if it is changed (see: https://msdn.microsoft.com/en-us/library/ms750428(v=vs.110).aspx), or you use the INotifyPropertyChanged interface in your ManagePage class (see: https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx).
Related
I have a user list in MainWindow. After pressing the preview button, a non-modal window for data editing opens. Updated data are changed in real time in the main window. The question is how to bind the windows so that because the user changes from the list in the main window, he changes in real time in an already open non-modal window.
WPF does not recommend coding business logic directly in xaml.cs
files.
It is recommended that you write code using the MVVM pattern
ViewModel
public class podgladUzytkownika : INotifyPropertyChanged
{
private string imie;
private string nazwisko;
private string mail;
public string Mail
{
get => mail;
set => this.SetValue(ref mail, value);
}
public string Nazwisko
{
get => nazwisko;
set => this.SetValue(ref nazwisko, value);
}
public string Imie
{
get => imie;
set => this.SetValue(ref imie, value);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void SetValue<T>(ref T oldValue, T newValue, [CallerMemberName] string propertyName = null)
{
oldValue = newValue;
OnPropertyChanged(propertyName);
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MainWindowViewModel
{
private int refreshCount;
public ICommand RefreshCommand { get; }
public ICommand PodgladUzytkownikaShow { get; }
public podgladUzytkownika data { get; }
public MainWindowViewModel()
{
data = new podgladUzytkownika();
PodgladUzytkownikaShow = new Command(PodgladUzytkownikaShowExecute);
RefreshCommand = new Command(RefreshCommandExecute);
}
private void PodgladUzytkownikaShowExecute(object obj)
{
var window = new Window();
window.DataContext = data;
window.Show();
}
private void RefreshCommandExecute(object obj)
{
// Data updates are passed to the view
refreshCount++;
data.Imie = nameof(data.Imie) + refreshCount;
data.Nazwisko = nameof(data.Nazwisko) + refreshCount;
data.Mail = nameof(data.Mail) + refreshCount;
}
}
View
// MainWindow.xaml
<StackPanel x:Name="StackPanel1">
<Button Content="PodgladUzytkownika" Command="{Binding Path=PodgladUzytkownikaShow}"/>
<Button Content="Refresh" Command="{Binding Path=RefreshCommand}"/>
</StackPanel>
// window.xaml
<StackPanel >
<TextBlock Text="{Binding Path=Imie }"/>
<TextBlock Text="{Binding Path=Nazwisko }"/>
<TextBlock Text="{Binding Path=Mail }"/>
</StackPanel>
Demo
this.MainWindow.DataContext =new MainWindowViewModel();
After chasing one problem after another with your comments, the problem is your code is not well-designed. Using data bindings (one of the prime benefits of WPF), you can stop chasing your tail with trying to figure out how to update one UI when data changes. Here is a simplified version of your code that will always ensure the UI matches the data you wish to manipulate.
MainWindow.xaml
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Label Margin="3" Grid.ColumnSpan="3">Lista użytkowników</Label>
<Button Margin="3" Padding="3" Grid.Row="2" Grid.ColumnSpan="3" Click="Zamknij_Click">Zamknij</Button>
<StackPanel Margin="3" Grid.Column="2" Grid.Row="1">
<!--<Button Name="Dodaj" Click="Dodaj_Click" Margin="3" Padding="10,3" >Dodaj...</Button>-->
<!--<Button Name="Usun" IsEnabled="False" Click="Usun_Click" Margin="3" Padding="10,3" >Usuń</Button>-->
<!--<Button Name="Edytuj" IsEnabled="False" Click="Edytuj_Click" Margin="3" Padding="10,3" >Edytuj...</Button>-->
<Button Name="Podglad" IsEnabled="False" Click="Podglad_Click" Margin="3" Padding="10,3" >Podgląd...</Button>
</StackPanel>
<ListView SelectionMode="Single" SelectionChanged="Selection_Changed" Name="lv_uzytkownicy" Margin="3" Grid.Row="1">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Imię"
DisplayMemberBinding="{Binding Imie}"/>
<GridViewColumn Header="Nazwisko"
DisplayMemberBinding="{Binding Nazwisko}" />
<GridViewColumn Header="Mail"
DisplayMemberBinding="{Binding Mail}"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<GridSplitter Grid.Column="1" Grid.Row="1" Width="5" ResizeDirection="Columns" HorizontalAlignment="Center"/>
</Grid>
MainWindow.xaml.cs
using System.Collections.ObjectModel;
public partial class MainWindow : Window
{
public ObservableCollection<Uzytkownik> listaUzytkownikow = new ObservableCollection<Uzytkownik>();
Podglad_Uzytkownika podgladUzytkownika;
public MainWindow()
{
InitializeComponent();
lv_uzytkownicy.ItemsSource = listaUzytkownikow;
listaUzytkownikow.Add(new Uzytkownik("Mietek", "Żul", "sikalafa#wp.pl"));
listaUzytkownikow.Add(new Uzytkownik("Franek", "Alpinista", "halo#gmail.pl"));
listaUzytkownikow.Add(new Uzytkownik("Stefan", "Ulążka", "mam.to#o2.pl"));
this.DataContext = this;
}
private void Podglad_Click(object sender, RoutedEventArgs e)
{
podgladUzytkownika = new Podglad_Uzytkownika();
podgladUzytkownika.DataContext = lv_uzytkownicy.SelectedItem;
podgladUzytkownika.Show();
}
private void Zamknij_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void Selection_Changed(object sender, SelectionChangedEventArgs e)
{
if (lv_uzytkownicy.SelectedItems.Count > 0) Podglad.IsEnabled = true;
else Podglad.IsEnabled = false;
if (podgladUzytkownika != null && podgladUzytkownika.IsVisible)
{
podgladUzytkownika.DataContext = lv_uzytkownicy.SelectedItem;
}
}
}
Podglad_Uzytkownika.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Margin="3">Imię</Label>
<Label Margin="3" Grid.Row="1">Nazwisko</Label>
<Label Margin="3" Grid.Row="2">Email</Label>
<TextBox Name="imieTextBox" Text="{Binding Imie, UpdateSourceTrigger=PropertyChanged}" Margin="3" Grid.Column="1"/>
<TextBox Name="nazwiskoTextBox" Text="{Binding Nazwisko, UpdateSourceTrigger=PropertyChanged}" Margin="3" Grid.Column="1" Grid.Row="1"/>
<TextBox Name="mailTextBox" Text="{Binding Mail, UpdateSourceTrigger=PropertyChanged}" Margin="3" Grid.Column="1" Grid.Row="2"/>
<Grid HorizontalAlignment="Center" Grid.ColumnSpan="2" Grid.Row="3" Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="pierwsza" />
</Grid.ColumnDefinitions>
<Button Margin="3" Padding="20, 5" Name="Podglad" Click="Podglad_Click" IsDefault="True">Zamknij</Button>
</Grid>
</Grid>
Podglad_Uzytkownika.xaml.cs
public partial class Podglad_Uzytkownika : Window
{
public Podglad_Uzytkownika()
{
InitializeComponent();
}
private void Podglad_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
Uzytkownik.cs
public class Uzytkownik : INotifyPropertyChanged
{
private string imie;
private string nazwisko;
private string mail;
public Uzytkownik(string imie, string nazwisko, string mail)
{
this.Imie = imie;
this.Nazwisko = nazwisko;
this.Mail = mail;
}
public string Imie { get => this.imie; set { this.imie = value; OnPropertyChanged(); } }
public string Nazwisko { get => this.nazwisko; set { this.nazwisko = value; OnPropertyChanged(); } }
public string Mail { get => this.mail; set { this.mail = value; OnPropertyChanged(); } }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I'm need to show data from ViewModel in simple grid which contain labels and textBlock for data. I'm try to bind data to grid and use ElementName to bind grid's DataContext with textBlocks.
Book ViewModel:
namespace Books.ViewModels
{
public class BookViewModel : IViewModel, INotifyPropertyChanged
{
private Book book = new Book();
private ICommand AddCommand;
private ICommand RemoveCommand;
private ICommand ChangeCommand;
public event PropertyChangedEventHandler PropertyChanged;
public BookViewModel()
{
//initialize commands;
}
public string Name
{
get { return book.Name; }
set { book.Name = value; }
}
public string Authors
{
get
{
return string.Join(", ", book.Authors.Select(x => x.Name));
}
set
{
//need to implemented
}
}
public string ISBN
{
get { return book.ISBN; }
set { book.ISBN = value; }
}
public int Pages
{
get { return book.Pages; }
set { book.Pages = value; }
}
public string Tags
{
get
{
return string.Join(", ", book.Tags);
}
set
{
//not implemented
}
}
public int PublicationYear
{
get { return book.PublicationYear; }
set { book.PublicationYear = value; }
}
public string House
{
get
{
return book.House.Name;
}
set
{
//not implemented
}
}
public ICommand AddClick
{
get
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Add"));
return AddCommand;
}
}
public ICommand RemoveClick
{
get
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Remove"));
return RemoveCommand;
}
}
public ICommand ChangeClick
{
get
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Change"));
return ChangeCommand;
}
}
}
}
grid in Xaml:
<Grid x:Name="bookGrid"
Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Title:"/>
<Label Grid.Row="1" Grid.Column="0" Content="Author(s):"/>
<Label Grid.Row="2" Grid.Column="0" Content="ISBN:"/>
<Label Grid.Row="3" Grid.Column="0" Content="Pages:"/>
<Label Grid.Row="4" Grid.Column="0" Content="Tags:"/>
<Label Grid.Row="5" Grid.Column="0" Content="Publication Year:"/>
<Label Grid.Row="6" Grid.Column="0" Content="Publication House:"/>
<TextBlock Grid.Column="1" Grid.Row="0" Margin="3" Text="{Binding ElementName=bookGrid, Path=Name}"/>
<TextBlock Grid.Column="1" Grid.Row="1" Margin="3" Text="{Binding ElementName=bookGrid, Path=Authors}"/>
<TextBlock Grid.Column="1" Grid.Row="2" Margin="3" Text="{Binding ElementName=bookGrid, Path=ISBN}"/>
<TextBlock Grid.Column="1" Grid.Row="3" Margin="3" Text="{Binding ElementName=bookGrid, Path=Pages}"/>
<TextBlock Grid.Column="1" Grid.Row="4" Margin="3" Text="{Binding ElementName=bookGrid, Path=Tags}"/>
<TextBlock Grid.Column="1" Grid.Row="5" Margin="3" Text="{Binding ElementName=bookGrid, Path=PublicationYear}"/>
<TextBlock Grid.Column="1" Grid.Row="6" Margin="3" Text="{Binding ElementName=bookGrid, Path=House}"/>
</Grid>
Code behind:
bookGrid.DataContext = Manager.books.First();
Just change the TextBlock bindings to something like:
<TextBlock Grid.Column="1" Grid.Row="0" Margin="3" Text="{Binding Name}"/>
Add a notifier method tho the viewmodel:
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And call this method after each property change like:
public string Name
{
get { return book.Name; }
set
{
book.Name = value;
this.NotifyPropertyChanged();
}
}
I have the Contact class that implements the INotifyPropertyChanged:
public class Contact : INotifyPropertyChanged
{
public Contact(Contact contact)
{
this.Username = contact.Username;
this.GUID = contact.GUID;
this.Msg = contact.Msg;
this.Ring = contact.Ring;
}
private string username;
public string Username
{
get { return username; }
set
{
username = value;
NotifyPropertyChanged(nameof(Username));
}
}
public Guid GUID { get; set; }
public bool Msg { get; set; }
private bool ring;
public bool Ring
{
get { return ring; }
set
{
ring = value;
NotifyPropertyChanged(nameof(Ring));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This is the main page:
public sealed partial class MainPage : Page
{
public ObservableCollection<Contact> Contacts = new ObservableCollection<Contact>();
public MainPage()
{
this.InitializeComponent();
Contacts.Add(new Contact("Contact001", Guid.NewGuid(), false, false));
Contacts.Add(new Contact("Contact002", Guid.NewGuid(), false, false));
}
private void AddContactButton_Click(object sender, RoutedEventArgs e)
{
Contacts.Add(new Contact("ContactN", Guid.NewGuid(), false, false));
}
private void ContactsListView_ItemClick(object sender, ItemClickEventArgs e)
{
Contact clickedContact = (Contact)e.ClickedItem;
int index = Contacts.IndexOf(clickedContact);
Contacts.ElementAt(index).Username = "Qwerty";
}
}
}
This is the XAML:
<Page
x:Class="ContactsListBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ContactsListBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:data="using:ContactsListBinding.Models"
xmlns:namespace="ContactsListBinding.Models">
<Page.Resources>
<data:MessageToImageConverter x:Key="MessageToImageConverter" />
<data:RingToImageConverter x:Key="RingToImageConverter" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0">
<Button Name="AddContactButton" Content="Add Contact" Click="AddContactButton_Click" />
<CheckBox Name="MessageMeCheckBox" Content="Message me" />
<CheckBox Name="DeleteMeCheckBox" Content="Delete me" />
</StackPanel>
<ListView Grid.Row="1" Grid.Column="0" Name="ContactsListView"
IsItemClickEnabled="True"
ItemClick="ContactsListView_ItemClick"
ItemsSource="{x:Bind Contacts}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Contact">
<Grid Width="500">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Bind Username}" VerticalAlignment="Center" />
<Image Grid.Row="0" Grid.Column="1" Width="15" Height="15" Source="{x:Bind Msg, Converter={StaticResource MessageToImageConverter}}" />
<Image Grid.Row="0" Grid.Column="2" Width="15" Height="15" Source="{x:Bind Ring, Converter={StaticResource RingToImageConverter}}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
So, when I click on an item, I change it's Ring property to true. I have debugged and it is changing to true. The only problem is that my UI isn't updating. Any ideas why?
Okay guys, I finally got it to work. It seems that the Binding Mode should be set to OneWay, instead of the default OneTime. For example the correct xaml should be:
<TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Bind Username, Mode=OneWay}" VerticalAlignment="Center" />
Thank you very much for all your help! :)
<Window x:Class="RMT_MMO.Starter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="304" Width="472"
ResizeMode="NoResize">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*"/>
<ColumnDefinition Width="200*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="40*"/>
<RowDefinition Height="40*"/>
<RowDefinition Height="80*"/>
</Grid.RowDefinitions>
<ProgressBar Grid.ColumnSpan="2" Maximum="100" Value="{Binding Percent}"/>
<Label Grid.ColumnSpan="2" Content="{Binding Status }" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="14" FontWeight="Bold" Margin="0,0,0,10"/>
<Label Grid.Row="2" Content="Username" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" FontSize="14" FontWeight="Bold"/>
<TextBox IsEnabled="{Binding CanLogin}" Grid.Row="2" Grid.Column="1" Margin="5" Text="{Binding Username}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
<Label Grid.Row="3" Content="Password" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" FontSize="14" FontWeight="Bold"/>
<PasswordBox IsEnabled="{Binding CanLogin}" Grid.Row="3" Grid.Column="1" Margin="5" x:Name="Password" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" />
<Button Click="Login" IsDefault="True" IsEnabled="{Binding CanLogin}" Content="asdasd" Grid.Row="4" HorizontalAlignment="Left" Padding="10" Margin="10,25,0,19" RenderTransformOrigin="7.273,0.484" Width="247" Grid.Column="1"/>
</Grid>
</Window>
So here how MainWindow look, when I click to button this code execute
using System.Windows;
namespace RMT_MMO.Starter
{
public partial class MainWindow
{
private readonly MainWindowViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new MainWindowViewModel();
DataContext = _viewModel;
_viewModel.CanLogin = true;
}
public void Login(object sender,RoutedEventArgs e)
{
// MessageBox.Show(_viewModel.Percent.ToString());
_viewModel.CanLogin = false;
_viewModel.Percent =50;
// _viewModel.Status = "Stuff happened";
}
}
}
And MainWindowViewModel class looks like this:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace RMT_MMO.Starter
{
public class MainWindowViewModel:INotifyPropertyChanging
{
private bool _canLogin;
private string _status;
private double _percent;
private string _username;
public event PropertyChangingEventHandler PropertyChanging;
public bool CanLogin
{
get { return _canLogin; }
set
{
_canLogin = value;
OnPropertyChanged();
}
}
public string Status
{
get { return _status; }
set
{
_status = value;
OnPropertyChanged();
}
}
public double Percent
{
get { return _percent; }
set
{
_percent = value;
OnPropertyChanged();
}
}
public string Username
{
get { return _username; }
set
{
_username = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanging;
if (handler != null)
handler(this, new PropertyChangingEventArgs(propertyName));
}
}
}
What I am trying to do is to bind xmla and this code, when I hit button binding is not performed.
I know it it is trivial mistake but I simple can't fined it, I tried searching online but I failed. All help is welcome.
It could be because you are using INotifyPropertyChanging when you should be using INotifyPropertyChanged interface.
I'm pretty new to MVVM so bear with me.
I'm working on an application that contains a contacts list. I've defined a contact user control with the following model:
public class Client
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public int CustomerID { get; set; }
public string FullName
{
get
{
string result = FirstName;
if (string.IsNullOrEmpty(result))
result = MiddleName;
else if(!string.IsNullOrEmpty(MiddleName))
result += string.Format(" {0}", MiddleName);
if (string.IsNullOrEmpty(result))
result = LastName;
else if (!string.IsNullOrEmpty(LastName))
result += string.Format(" {0}", LastName);
if (string.IsNullOrEmpty(result))
result = "";
return result;
}
}
}
And the following ViewModel:
public class ClientViewModel : ObservableObject
{
public Client Customer
{
get
{
return _customer;
}
set
{
_customer = value;
RaisePropertyChangedEvent("Customer");
}
}
public string FullName { get { return _customer.FullName; } }
public string PhoneNumber { get { return _customer.PhoneNumber; } }
public string Address1 { get { return _customer.Address1; } }
public string Address2 { get { return _customer.Address2; } }
public string City { get { return _customer.City; } }
public string State { get { return _customer.State; } }
public string ZipCode { get { return _customer.ZipCode; } }
Client _customer = new Client();
}
And the following View:
<UserControl x:Class="LawnCareManager.Views.ClientView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:LawnCareManager.ViewModels"
mc:Ignorable="d"
d:DesignHeight="180" d:DesignWidth="300">
<UserControl.DataContext>
<local:ClientViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Margin="10,0,0,0"
Content="Name: "/>
<Label Grid.Row="0"
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
Content="{Binding FullName}"/>
<Label Grid.Row="1"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Margin="10,0,0,0"
Content="Phone Number: "/>
<Label Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
Content="{Binding PhoneNumber}"/>
<Label Grid.Row="2"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Margin="10,0,0,0"
Content="Address 1: "/>
<Label Grid.Row="2"
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
Content="{Binding Address1}"/>
<Label Grid.Row="3"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Margin="10,0,0,0"
Content="Address 2: "/>
<Label Grid.Row="3"
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
Content="{Binding Address2}"/>
<Label Grid.Row="4"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Margin="10,0,0,0"
Content="City: "/>
<Label Grid.Row="4"
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
Content="{Binding City}"/>
<Label Grid.Row="5"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Margin="10,0,0,0"
Content="State: "/>
<Label Grid.Row="5"
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
Content="{Binding State}"/>
<Label Grid.Row="6"
Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Margin="10,0,0,0"
Content="Zip Code: "/>
<Label Grid.Row="6"
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
Content="{Binding ZipCode}"/>
</Grid>
</UserControl>
Now, I want to create a user control that contains a list of contacts defined in the ViewModel below:
public class ClientsListViewModel : ObservableObject
{
public ObservableCollection<ClientViewModel> Clients
{
get { return _clients; }
set { _clients = value; }
}
ObservableCollection<ClientViewModel> _clients = new ObservableCollection<ClientViewModel>();
public ClientsListViewModel()
{
ClientViewModel client = new ClientViewModel();
client.Customer.FirstName = "John";
client.Customer.LastName = "Doe";
client.Customer.PhoneNumber = "555-555-5555";
client.Customer.Address1 = "1234 Fake Street";
client.Customer.City = "Springfield";
_clients.Add(client);
}
}
And the View below:
<UserControl x:Class="LawnCareManager.Views.ClientsListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:LawnCareManager.ViewModels"
xmlns:views="clr-namespace:LawnCareManager.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:ClientsListViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Label Content="Contacts"
Grid.Row="0"
Grid.Column="0"/>
<ListView Grid.Column="1" Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding Clients}" x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<views:ClientView DataContext="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>
The problem is that the ClientView ListViewItems in the ClientsListView are not binding correctly to the ObservableCollection of ClientViewModels. The correct number of ClientViews shows up in the list if I add more ClientViewModels, but none of the labels in the ClientView are populating.
Could somebody tell me what I'm doing wrong? Any feedback is greatly appreciated!
The issue here is that you are constructing the data context for client view within the InitializeComponent method. That's due to the static declaration in your Xaml. You can prove this empirically by adding this line of code to the ClientView constructor...
var dc = this.DataContext;
and observe that it gets created with null values at the "Wrong time".
If you change these lines in your ClientView.xaml...
<UserControl.DataContext>
<genericMvvm1:ClientViewModel/>
</UserControl.DataContext>
to this...
<!--<UserControl.DataContext>
<genericMvvm1:ClientViewModel/>
</UserControl.DataContext>-->
You will see your clients getting populated and displayed the way you were expecting. You'll need to change your design strategy to take account of the way InitializeComponent behaves, but this answer gets you 'unstuck'.