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();
}
}
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 have a WPF form that I want to use to manipulate a database table. To get the data out of the database I use Entity Framework Core. I try to follow the MVVM pattern.
So I have a Model called Employee with a number of string properties like Firstname and so on.
The form has a ListView that shows an ObservableCollection of Employee. Further I have a number of TextBoxes to display and manipulate the properties of single Employee objects. The selectedMemberPath of the ListView is bound to a property SelectedEmployee. The setter of SelectedEmployee than copies it's own properties to EditedEmployee. The TextBoxes are bound to EditedEmployee. The idea is, that I don't want to directly edit the ObservableCollection. This should only happen if a save button is clicked.
This is my view model.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using XdcCore.Models;
using XdcCore.ViewModels.Commands;
namespace XdcCore.ViewModels
{
class EmployeeEditorVM : List<Employee>, INotifyPropertyChanged
{
public EmployeeSaveCommand EmployeeSaveCommand { get; set; }
public EmployeeEditorVM()
{
EmployeeSearch();
EmployeeSaveCommand = new EmployeeSaveCommand(this);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string employeeSearchText;
public string EmployeeSearchText
{
get { return employeeSearchText; }
set
{
employeeSearchText = value;
OnPropertyChanged("EmployeeSearch");
EmployeeSearch();
}
}
private Employee selectedEmployee;
public Employee SelectedEmployee
{
get { return selectedEmployee; }
set
{
selectedEmployee = value;
OnPropertyChanged("SelectedEmployee");
EditedEmployee = SelectedEmployee;
}
}
private Employee editedEmployee;
public Employee EditedEmployee
{
get { return editedEmployee; }
set
{
editedEmployee = value;
OnPropertyChanged("EditedEmployee");
}
}
private ObservableCollection<Employee> employees;
public ObservableCollection<Employee> Employees
{
get { return employees; }
set
{
employees = value;
OnPropertyChanged("Employees");
}
}
public void EmployeeSearch()
{
using (var context = new XdcContext())
{
if (employeeSearchText != null)
{
var _employees = context.Employee
.Where(x => x.Firstname.Contains(employeeSearchText));
Employees = new ObservableCollection<Employee>(_employees);
}
else
{
var _employees = context.Employee.Select(x => x);
Employees = new ObservableCollection<Employee>(_employees);
}
}
}
public void EmployeeSave()
{
using (var context = new XdcContext())
{
var entity = context.Employee.First(x => x.IdEmployee == SelectedEmployee.IdEmployee);
{
entity.Firstname = SelectedEmployee.Firstname;
entity.Lastname = SelectedEmployee.Lastname;
entity.Initials = SelectedEmployee.Initials;
context.SaveChanges();
}
EmployeeSearch();
}
}
}
}
this is my View:
<Window
x:Class="XdcCore.Views.Editors.EmployeeEditor"
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:local="clr-namespace:XdcCore.Views.Editors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:XdcCore.ViewModels"
Title="Employee"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.Resources>
<vm:EmployeeEditorVM x:Key="vm" />
</Window.Resources>
<Grid DataContext="{StaticResource vm}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="115*" />
<ColumnDefinition Width="277*" />
</Grid.ColumnDefinitions>
<!-- left column -->
<Border BorderBrush="DarkGray" BorderThickness="1">
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="aUTO" />
</Grid.RowDefinitions>
<Label
Grid.Row="0"
Margin="3,3,3,3"
Content="Filter:" />
<TextBox
x:Name="TextBoxSearch"
Grid.Row="1"
Height="25"
Margin="3,0,3,3"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
Text="{Binding EmployeeSearchText, Mode=TwoWay}" />
<ListView
x:Name="ListViewOverview"
Grid.Row="2"
Margin="3,0,3,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding Employees, Mode=OneWay}"
SelectedItem="{Binding SelectedEmployee, Mode=OneWayToSource}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Initials}" />
<TextBlock Text=" : " />
<TextBlock Text="{Binding Firstname}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding Lastname}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button
x:Name="ButtonDelete"
Grid.Column="0"
Height="25"
Margin="3,3,3,3"
Content="Delete"
IsEnabled="False" />
<Button
x:Name="ButtonNew"
Grid.Column="1"
Height="25"
Margin="3,3,3,3"
Content="Add New ..."
IsEnabled="True" />
</Grid>
</Grid>
</Border>
<!-- right Column -->
<Grid Grid.Column="1" Grid.ColumnSpan="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- right column, first row -->
<Border
Grid.Row="0"
BorderBrush="DarkGray"
BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Right"
Content="First Name" />
<Label
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Right"
Content="Last Name" />
<Label
Grid.Row="2"
Grid.Column="0"
HorizontalAlignment="Right"
Content="Initials" />
<Label
Grid.Row="3"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalContentAlignment="Center"
Content="ID" />
<Label
Grid.Row="4"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalContentAlignment="Center"
Content="RCU" />
<Label
Grid.Row="5"
Grid.Column="0"
HorizontalAlignment="Right"
VerticalContentAlignment="Center"
Content="RCT" />
<TextBox
x:Name="TextBoxFirstName"
Grid.Row="0"
Grid.Column="1"
Margin="0,3,3,3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
Text="{Binding EditedEmployee.Firstname, Mode=TwoWay}" />
<TextBox
x:Name="TextBoxLastName"
Grid.Row="1"
Grid.Column="1"
Margin="0,3,3,3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
Text="{Binding EditedEmployee.Lastname, Mode=TwoWay}" />
<TextBox
x:Name="TextBoxInitials"
Grid.Row="2"
Grid.Column="1"
Margin="0,3,3,3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
Text="{Binding EditedEmployee.Initials, Mode=TwoWay}" />
<TextBox
x:Name="TextBoxId"
Grid.Row="3"
Grid.Column="1"
Margin="0,3,3,3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
IsReadOnly="True" />
<TextBox
x:Name="TextBoxRCU"
Grid.Row="4"
Grid.Column="1"
Margin="0,3,3,3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
IsReadOnly="True" />
<TextBox
x:Name="TextBoxRCT"
Grid.Row="5"
Grid.Column="1"
Margin="0,3,3,3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
IsReadOnly="True" />
</Grid>
</Border>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button
x:Name="ButnSave"
Grid.Column="1"
Width="76"
Height="25"
Margin="60,3,60,0"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Command="{Binding EmployeeSaveCommand}"
Content="Save"
IsEnabled="True" />
</Grid>
<!-- Add new -->
</Grid>
</Grid>
</Grid>
</Window>
In other words: I want to click on an item in the list view, it's details are shown in the text boxes. I can change the text in the text boxes but the changes are not applied to the observable collection (or even database) until I click "save". The problem is, that exactly that happens: when I edit text in the text boxes the changes are also shown in the list view. And I can#t see why.
So, I got the concept of "copy vs point" of a variable wrong.
I had two solutions for my problem.
To my Employee model I added the method Copy:
public partial class Employee
{
public int IdEmployee { get; set; }
public string Initials { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Rcu { get; set; }
public DateTime Rct { get; set; }
public Employee Copy()
{
return (Employee)this.MemberwiseClone();
}
}
Then I Changed my ViewModel so that EditedEmployee is a copy of SelectedEmployee
private Employee selectedEmployee;
public Employee SelectedEmployee
{
get { return selectedEmployee; }
set
{
selectedEmployee = value;
OnPropertyChanged("SelectedEmployee");
if (SelectedEmployee != null)
{
EditedEmployee = SelectedEmployee.Copy();
}
}
}
private Employee editedEmployee;
public Employee EditedEmployee
{
get { return editedEmployee; }
set
{
editedEmployee = value;
OnPropertyChanged("EditedEmployee");
}
}
The problem with this approach is, that if I mess around with the database and then need to run Scaffold-DbContext (I prefer the code-first approach of EFC) again, I would have to modify all models again. A solution to that might be some a second, modified Model that inherits from the original Model and then adds the Copy method.
My second approach is to bind the ListView via SelectedIndex instead of SelectedItem.
<ListView
...
ItemsSource="{Binding Employees, Mode=OneWay}"
SelectedIndex="{Binding SelectedItemIndex, Mode=TwoWay}">
In the ViewModel this is then represented by an int property. If this int is set (and >= 0) Employee gets copied EmployeeWorkingCopy. Furthermore if EmployeeWorkingCopy is set a single Employee dubbed EditedEmployee is set as the Employee of the working copy with the selected index.
private int selectedItemIndex;
public int SelectedItemIndex
{
get { return selectedItemIndex; }
set
{
selectedItemIndex = value;
if (SelectedItemIndex >= 0)
EmployeesWorkingCopy = new ObservableCollection<Employee>(Employees);
}
}
private Employee editedEmployee;
public Employee EditedEmployee
{
get { return editedEmployee; }
set
{
editedEmployee = value;
OnPropertyChanged("EditedEmployee");
}
}
private ObservableCollection<Employee> employeesWorkingCopy;
public ObservableCollection<Employee> EmployeesWorkingCopy
{
get { return employeesWorkingCopy; }
set
{
employeesWorkingCopy = value;
EditedEmployee = EmployeesWorkingCopy[SelectedItemIndex];
}
}
Thanks a lot for pointing out my misconception.
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).
I have a ListView with textBox in one of the columns. User input on the textbox supposed to update the underlying data, that's what my requirement is, but it's not happening. I tried a work around as shown all the way below, it seems to work but I am guessing that the binding itself should take care of it without the work around.
XAML:
<ListView x:Name="listView" Grid.Row="1" ItemsSource="{Binding}" SelectionMode="Multiple">
<ListView.ItemTemplate>
<DataTemplate>
<Border Background="{Binding BackGround}">
<Grid x:Name="row">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock x:Name="tblDescription"
Grid.Row="0"
Grid.Column="0"
Margin="1"
TextWrapping="Wrap"
Text="{Binding Description}"
FontSize="{StaticResource TextStyleMediumFontSize}"
Foreground="Black"/>
<TextBlock x:Name="tblItemNumber"
Grid.Row="0"
Grid.Column="1"
Margin="1"
Text="{Binding ItemNumber}"
FontSize="{StaticResource TextStyleMediumFontSize}"
FontStyle="Italic"
Foreground="Gray"/>
<TextBox x:Name="tbQuantity"
Grid.Row="1"
Grid.Column="1"
Margin="1"
Text="{Binding Quantity}"
IsEnabled="{Binding IsEnabled}"
FontSize="{StaticResource TextStyleLargeFontSize}"
BorderBrush="DarkGray"
Foreground="Black"
InputScope="Number"
Tag="{Binding RowNumber}"
TextChanged="tbQuantity_TextChanged"
>
<TextBox.Header>Quantity</TextBox.Header>
</TextBox>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
DataRow class:
public class DataRowBase : INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Properties
public int RowNumber { get; set; }
public string ItemNumber { get; set; }
public string Description { get; set; }
private string _Quantity;
public string Quantity
{
get { return _Quantity; }
set {
_Quantity = value;
RaisePropertyChanged("Quantity");
}
}
#endregion
}
Work Around:
private void tbQuantity_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox tb = (TextBox)sender;
int rownumber = tb.Tag.ToString().ToInt();
UpdateQuantity(rownumber, value: tb.Text);
}
private void UpdateQuantity(int rownumber, string value)
{
try
{
DataRow datarow = OriginalSource.Where(o => o.RowNumber == rownumber).FirstOrDefault();
if (datarow != null)
{
datarow.Quantity = value;
}
}
catch (Exception ex)
{
}
}
The default Binding is OneWay, for a TwoWay you will have to declare it, for example:
Text="{Binding ItemNumber, Mode=TwoWay}"
<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.