Save Changes of View in Viewmodel? - c#

I'm trying to display a model and I want to save it when the user clicks a button.
(Bind ModelView OneWay to Model and if the save button was pressed I update the source manually.)
Is that possible?
Here I created a small example with my problem. (not MVVM!)
MainWindow.xaml
<Grid>
<Grid.DataContext>
<ViewModel:MainViewModel/>
</Grid.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<local:PersonListView Grid.Column="0" DataContext="{Binding ViewModel_PersonList}"/>
<local:PersonView Grid.Column="1" DataContext="{Binding ViewModel_Person}"/>
</Grid>
PersonListView.xaml
<ListView ItemsSource="{Binding PersonList}" SelectedItem="{Binding SelectedPerson}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding LastName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
PersonView.xaml
<Grid DataContext="{Binding}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="First Name:" Grid.Column="0" Grid.Row="0" Margin="5"/>
<TextBlock Text="Last Name:" Grid.Column="0" Grid.Row="1" Margin="5"/>
<TextBlock Text="Age:" Grid.Column="0" Grid.Row="2" Margin="5"/>
<TextBox Text="{Binding Path=Person.FirstName, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="0" Margin="5"/>
<TextBox Text="{Binding Path=Person.LastName, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="1" Margin="5"/>
<TextBox Text="{Binding Path=Person.Age, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="2" Margin="5"/>
<Button Content="Save" Command="{Binding SaveCommand}" Grid.Column="1" Grid.Row="4"/>
</Grid>
Person.cs
public class Person : INotifyPropertyChanged
{
private string firstName;
public string FirstName
{
get { return firstName; }
set
{
firstName = value;
OnPropertyChanged("FirstName");
}
}
private string lastName;
public string LastName
{
get { return lastName; }
set
{
lastName = value;
OnPropertyChanged("LastName");
}
}
private int age;
public int Age
{
get { return age; }
set
{
age = value;
OnPropertyChanged("Age");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
ViewModel_PersonList.PersonList.Add(new Model.Person()
{
LastName = "Test",
FirstName = "John",
Age = 20
});
ViewModel_PersonList.PersonList.Add(new Model.Person()
{
LastName = "Example",
FirstName = "Luke",
Age = 25
});
ViewModel_PersonList.SelectedPerson = ViewModel_PersonList.PersonList[1];
ViewModel_Person.Person = ViewModel_PersonList.SelectedPerson;
}
ViewModel.PersonViewModel _ViewModel_Person = new ViewModel.PersonViewModel();
public ViewModel.PersonViewModel ViewModel_Person
{
get
{
return _ViewModel_Person;
}
set
{
_ViewModel_Person = value;
RaisePropertyChanged("ViewModel_Person");
}
}
ViewModel.PersonListViewModel _ViewModel_PersonList = new ViewModel.PersonListViewModel();
public ViewModel.PersonListViewModel ViewModel_PersonList
{
get
{
return _ViewModel_PersonList;
}
set
{
_ViewModel_PersonList = value;
RaisePropertyChanged("ViewModel_PersonList");
}
}
}
PersonListViewModel.css
public class PersonListViewModel : ViewModelBase
{
ObservableCollection<Model.Person> _PersonList = new ObservableCollection<Model.Person>();
public ObservableCollection<Model.Person> PersonList
{
get
{
return _PersonList;
}
set
{
_PersonList = value;
RaisePropertyChanged("PersonList");
}
}
Model.Person _SelectedPerson = new Model.Person();
public Model.Person SelectedPerson
{
get
{
return _SelectedPerson;
}
set
{
MessengerInstance.Send<Model.Person>(value);
_SelectedPerson = value;
RaisePropertyChanged("SelectedPerson");
}
}
}
PersonViewModel.cs
public class PersonViewModel : ViewModelBase
{
public PersonViewModel()
{
MessengerInstance.Register<Model.Person>(this, per => Person = per);
}
Model.Person _Person = new Model.Person();
public Model.Person Person
{
get
{
return _Person;
}
set
{
_Person = value;
RaisePropertyChanged("Person");
}
}
private RelayCommand<Model.Person> _SaveCommand;
public ICommand SaveCommand
{
get
{
if (_SaveCommand == null)
_SaveCommand = new RelayCommand<Model.Person>(SaveCommand_Execute, SaveCommand_CanExecute);
return _SaveCommand;
}
}
private bool SaveCommand_CanExecute(Model.Person newPerson)
{
return true;
}
private void SaveCommand_Execute(Model.Person newPerson)
{
// Do my saving!
}
}
Thank you!

You can make the Mode=TwoWay and use UpdateSourceTrigger=Explicit. Refer below code.
<StackPanel>
<TextBox Name="tb_1" Text="HelloWorld!"/>
<TextBox Name="tb_2" Text="{Binding Path=Text, ElementName=tb_1, Mode=TwoWay,UpdateSourceTrigger=Explicit}"/>
<Button Content="Update Source" Click="Button_Click"/>
</StackPanel>

Related

Dynamic ViewModel's bound values becomes null in WPF

I followed few online tutorials to switch between dynamic views from a ListView.
In my MainWindow, I have a ListView in the left pane with ItemsSource of list of sub ViewModels. (Each sub ViewModel implements an Interface)
Each ViewModel have its own View as a DataTemplate.
I'm calling the GenerateReport() method of the selected view from the MainWindow. But the values of selected views becomes null.
Download my source from Github.
Steps to reproduce the issue:
Run the application and type text in the Students Report's Id and Name. (The breakpoints in StudentReportViewModels's properties are properly hitting and values updated.)
Then click Generate Report button. The StudentReportViewModels's properties values becomes null.
How to fix the issue? Please help.
Source:
MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type vm:StudentsReportViewModel}">
<view:StudentsReport/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MarksReportViewModel}">
<view:MarksReport />
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding Reports}" SelectedItem="{Binding SelectedReport, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<GridSplitter Grid.Row="1" Grid.Column="1" Width="5" ResizeBehavior="PreviousAndNext" HorizontalAlignment="Stretch"/>
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ContentControl Content="{Binding SelectedReport.ViewModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</ScrollViewer>
<Button Content="Generate Report" Grid.Row="2" Margin="5" HorizontalAlignment="Right" Command="{Binding GenerateReportCommand}"/>
</Grid>
</Grid>
MainWindowViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
private ICommand _generateReportCommand;
private Report selectedReport;
public Report SelectedReport
{
get
{
return this.selectedReport;
}
set
{
if (value != this.selectedReport)
{
this.selectedReport = value;
NotifyPropertyChanged();
}
}
}
public List<Report> Reports { get; set; }
public ICommand GenerateReportCommand
{
get
{
if (_generateReportCommand == null)
{
_generateReportCommand = new RelayCommand(
p => GenerateReport()
);
}
return _generateReportCommand;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public MainWindowViewModel()
{
Reports = new List<Report>
{
new Report{ Name = "Students Report", ViewModel = new StudentsReportViewModel()},
new Report{ Name = "Marks Report", ViewModel = new MarksReportViewModel()}
};
SelectedReport = Reports[0];
}
public void GenerateReport()
{
SelectedReport.ViewModel.GenerateReport();
}
}
StudentsReport.xaml
<TextBox Height="25" Width="100" Margin="5" Text="{Binding Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Height="25" Width="100" Margin="5" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
StudentsReportViewModel:
public class StudentsReportViewModel : INotifyPropertyChanged, IReportViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string id;
public string Id
{
get
{
return this.id;
}
set
{
if (value != this.id)
{
this.id = value;
NotifyPropertyChanged();
}
}
}
private string name;
public string Name
{
get
{
return this.name;
}
set
{
if (value != this.name)
{
this.name = value;
NotifyPropertyChanged();
}
}
}
public StudentsReportViewModel()
{
}
public void GenerateReport()
{
System.Windows.MessageBox.Show($"Id = {Id}, Name = {Name}");
}
}
Interface:
public interface IReportViewModel
{
void GenerateReport();
}
Model:
public class Report
{
public string Name { get; set; }
public IReportViewModel ViewModel { get; set; }
}
Your StudentsReport.xaml UserControl is binding to an instance of the StudentsReportViewModel created in the XAML:
<UserControl.DataContext>
<vm:StudentsReportViewModel/>
</UserControl.DataContext>
The Generate Report button however is calling into another instance of the StudentsReportViewModel that is create in the MainWindowVieModel constructor and is stored in the Report class.
Reports = new List<Report>
{
new Report{ Name = "Students Report", ViewModel = new StudentsReportViewModel()},
new Report{ Name = "Marks Report", ViewModel = new MarksReportViewModel()}
};
You need to remove one of these instances so that the UserControl's DataContext is bound to the same view model instance that you are generating the report message from.
I suggest deleting this code from StudentsReport.xaml:
<UserControl.DataContext>
<vm:StudentsReportViewModel/>
</UserControl.DataContext>

Binding multiple WPF user defined combo boxes to same single Observable collection

In my code I have one observable collection that has many combo boxes inside that. Now I have to add List for each combo boxes using MVVM (Model-View-View Model) i.e., No code behind
In View.xaml:
<Window.... xmlns:VM="clr-namespace:myproject.myViewModel"
... >
<Window.DataContext><VM:myViewModel/>
</Window.DataContext>
<ItemsControl ItemsSource="{Binding myCollection}" >
<ItemsControl.ItemTemplate >
<DataTemplate>
<DockPanel>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Margin="0,20,0,0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="City" Margin="20" VerticalAlignment="Center"/>
<ComboBox KeyboardNavigation.TabIndex="0" Grid.Column="1" Margin="45,10,10,10" Height="30" Width="200" ItemsSource="{Binding City}" />
<TextBlock Text="temperature" Grid.Row="1" VerticalAlignment="Center" Margin="20" />
<ComboBox KeyboardNavigation.TabIndex="3" Grid.Column="1" Grid.Row="1" Margin="45,20,10,10" Height="30" Width="200" SelectedIndex="0" HorizontalContentAlignment="Right"
VerticalAlignment="Center" ItemsSource="{Binding Temperature}">
</ComboBox>
<TextBlock Text="State" Grid.Row="1" VerticalAlignment="Center" Margin="10" Grid.Column="2"/>
<ComboBox KeyboardNavigation.TabIndex="3" Grid.Column="3" Grid.Row="1" Margin="10" Height="30" Width="200" HorizontalContentAlignment="Center" ItemsSource="{Binding State}" >
</ComboBox>
<TextBlock Text="Open Files " VerticalAlignment="Center" Grid.Row="0" Grid.Column="2" Margin="10" />
<TextBox Grid.Column="3" Text="" Height="30" Grid.Row="0" IsReadOnly="True"
TextAlignment="Right" VerticalContentAlignment="Center" Width="200" /> <Button Grid.Column="4" Content="Browse" Height="30" VerticalAlignment="Bottom" MinWidth="41" />
</Grid>
</Window>
In **Model.cs**:
namespace myproject.Models
{
public class projectmodel : INotifyPropertyChanged
{
private ObservableCollection<projectmodel> city;
private ObservableCollection<projectmodel> temperature;
private ObservableCollection<projectmodel> state;
public ObservableCollection<projectmodel> City
{
get { return city; }
set
{
city = value;
NotifyPropertyChanged("City");
}
}
public ObservableCollection<projectmodel> Temperature
{
get { return temperature; }
set
{
temperature = value;
NotifyPropertyChanged("Temperature");
}
}
public ObservableCollection<projectmodel> State
{
get { return state; }
set
{
state = value;
NotifyPropertyChanged("State");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Private Helpers
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}}
In ViewModel.cs:
namespace myproject.ViewModels
{
public class projectViewModel
{
public ObservableCollection<T> myCollection
{
get; set;
}
public projectViewModel()
{
myCollection = new ObservableCollection<T>();
List<string> lstCity = new List<string>();
lstCity = new List<string> { "Coimbatore", "Chennai", "Bangalore" };
List<string> lstTemperature = new List<string>();
lstTemperature = new List<string> { "17c", "18c", "15c" };
List<string> lstState = new List<string>();
lstState = new List<string> { "Andhra", "karnataka", "TamilNadu" };
}
}myCollection.Add(new projectmodel
{
City = lstCity.ToArray(),
Temperature = lstTemperature.ToArray(),
State= lstState.ToArray()
});
}
}}
This is my code,I didn't get anything if I select my combo boxes. Please suggest me how should I write my viewmodel.cs ,and also correct me if I'm wrong anywhere else.
You have to declare all your collections inside your projectmodel to be of type ObservableCollection<string> instead of sting[]. When ever you bind to a collection use ObservableCollection<T>.
Your view models must also all implement INotifyPropertyChanged
Changes:
ProjectModel:
namespace Myproject.Models
{
public class ProjectModel : INotifyPropertyChanged
{
private ObservableCollection<string> city;
private ObservableCollection<string> temperature;
private ObservableCollection<string> state;
public ObservableCollection<string> City
{
get { return city; }
set
{
city = value;
NotifyPropertyChanged("City");
}
}
public ObservableCollection<string> Temperature
{
get { return temperature; }
set
{
temperature = value;
NotifyPropertyChanged("Temperature");
}
}
public ObservableCollection<string> State
{
get { return state; }
set
{
state = value;
NotifyPropertyChanged("State");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Private Helpers
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
ProjectViewModel:
namespace MyProject.ViewModels
{
public class ProjectViewModel : INotifyPropertyChanged
{
private ObservableCollection<Projectmodel> myCollection;
public ObservableCollection<Projectmodel> MyCollection
{
get => this.myCollection;
set
{
if (Equals(value, this.myCollection)) return;
this.myCollection = value;
OnPropertyChanged();
}
}
public ProjectViewModel()
{
MyCollection = new ObservableCollection<Projectmodel>()
{
new ProjectModel()
{
City = new ObservableCollection<string>()
{
"Coimbatore", "Chennai", "Bangalore"
},
Temperature = new ObservableCollection<string>()
{
"17c", "18c", "15c"
},
State = new ObservableCollection<string>()
{
"Andhra", "karnataka", "TamilNadu"
}
}
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Edit:
I updated the code to meet the common naming conventions using PascalCase. It is recommended to follow them: C# Conventions

Having problems binding my datagrid to data from my database

i've a problem to use datagrid in wpf mvvm project
Here is my xaml :
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
x:Class="noteManager.MainWindow"
xmlns:vm="clr-namespace:noteManager.ViewModel"
DataContext="{StaticResource noteManagerViewModel}"
Title="NoteManager" Height="490" Width="525">
<Grid Margin="0,0,0,-132.5">
<Grid.RowDefinitions>
<RowDefinition Height="10"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="200"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="110"></RowDefinition>
<RowDefinition Height="111"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Login :" FontSize="16" Grid.Column="2" Margin="51,9,50,0" Grid.RowSpan="2" Height="23" VerticalAlignment="Top" Grid.ColumnSpan="2"/>
<TextBox Text="{Binding Login}" Grid.Row="1" Grid.Column="3" Margin="14,0,86,29" Grid.ColumnSpan="2"/>
<Button Background="LightGreen" Foreground="Green" Command="{Binding testConnexion}" x:Name="testConnexion" Content="Connexion" Grid.Row="1" Grid.Column="2" Margin="51,29,86,0" Grid.ColumnSpan="3"/>
<Button Command="{Binding addUser}" Content="+" Grid.Row="1" Grid.Column="4" Margin="34,1,20,0" RenderTransformOrigin="0.742,0.468"/>
<DataGrid Name="dataGrid1" Grid.Row="2" Margin="8,7,-22,7" AutoGenerateColumns="False"
ItemsSource="{Binding _DataGridNotes}" SelectedItem="{Binding Path=MySelectedNote}" HorizontalAlignment="Center"
Width="480" Grid.ColumnSpan="6" Grid.Column="1">
<DataGrid.Columns>
<DataGridTextColumn Width="100" Binding="{Binding Path=NoteTitle}" Header="Titre" />
<DataGridTextColumn Width="200" Binding="{Binding Path=NoteContent}" Header="Note" />
<DataGridTextColumn Width="100" Binding="{Binding Path=NoteCreatedAt}" Header="Date de création" />
<DataGridTextColumn Width="100" Binding="{Binding Path=NoteUpdatedAt}" Header="Dat MAJ" />
</DataGrid.Columns>
</DataGrid>
<TextBlock Text="Titre" FontSize="16" Grid.Row="3" Grid.Column="1" Margin="27,8,7,1"/>
<TextBox Text="{Binding Path=titre, Mode=TwoWay}" Grid.Row="3" Grid.Column="2" Margin="17,10,23,10" Grid.ColumnSpan="5"/>
<TextBlock Text="Note" FontSize="16" Grid.Row="4" Grid.Column="1" Margin="27,4,7,0"/>
<TextBox Text="{Binding Path=description, Mode=TwoWay}" Grid.Row="4" Grid.Column="2" Margin="17,10,23,8" Grid.ColumnSpan="5"/>
<Button Command="{Binding Path=DeleteNote}" Background="LightPink" Foreground="red" Content="Supprimer" Grid.Row="5" Grid.Column="1" Margin="55,7,26,81" Grid.ColumnSpan="2"/>
<Button Command="{Binding Path=UpdateANote}" Content="Mettre à jour" Grid.Row="5" Grid.Column="3" Margin="14,7,67,81" Grid.ColumnSpan="2" RenderTransformOrigin="0.5,0.5"/>
<Button Command="{Binding Path=AddNote}" Content="Ajouter" Grid.Row="5" Grid.Column="4" Margin="78,7,10,81" Grid.ColumnSpan="3"/>
</Grid>
</Window>
Here is my viewModel :
namespace noteManager.ViewModel
{
public class noteManagerViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void Notify(string property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
string login;
int currentUser;
public string Login
{
get
{
return login;
}
set
{
login = value; Notify("Login");
}
}
private bool _canExecute;
public noteManagerViewModel()
{
_canExecute = true;
}
private ICommand _testConnexion;
public ICommand testConnexion
{
get
{
return _testConnexion ?? (_testConnexion = new CommandHandler(() => Connexion(), _canExecute));
}
}
private ICommand _addUser;
public ICommand addUser
{
get
{
return _addUser ?? (_addUser = new CommandHandler(() => AjoutUser(), _canExecute));
}
}
private ObservableCollection<DataGridNotes> _DataGridNotes = new ObservableCollection<DataGridNotes>();
public ObservableCollection<DataGridNotes> dataGridNotes
{
// No need for a public setter
get { return _DataGridNotes; }
}
}
the other class that i use :
public class User
{
/*public User()
{
this.Note = new HashSet<Note>();
}*/
public int Id { get; set; }
public string Login { get; set; }
//public virtual ICollection<Note> Note { get; set; }
}
public class Note : INotifyPropertyChanged
{
public int Id { get; set; }
public string NoteText { get; set; }
public string ContentText { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public int UserId { get; set; }
//public virtual User User { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void Notify(string property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class DataGridNotes
{
private string _noteTitle;
private string _noteContent;
private string _noteCreatedAt;
private string _noteUpdatedAt;
public string NoteTitle { get { return _noteTitle; } set { _noteTitle = value; } }
public string NoteContent { get { return _noteContent; } set { _noteContent = value; } }
public string NoteCreatedAt { get { return _noteCreatedAt; } set { _noteCreatedAt = value; } }
public string NoteUpdatedAt { get { return _noteUpdatedAt; } set { _noteUpdatedAt = value; } }
}
sorry for the ugly code, new to c# for a project.
i want to use the datagrid in my viewmodel but don't find a way to make it work (would like to write data from mysql database in the datagrid
Have you an idea to make it work ?
thx in advance
Ok, It's tough to spot what you are doing wrong without seeing the ViewModel, however you may want to check the following:
1) The DataContext is correct.
2) The property _DataGridNotes exists. Check the program output to make sure that there are no warnings informing you that bindings are broken.
The property you are looking to have should look something like this:
List<Note> _DataGridNotes
{
get
{
// get notes from SQL request
// construct list of Note and return list
}
}
You should also make sure that the Note class contains the properties required (NoteTitle, NoteContent, NoteCreatedAt, NoteUpdatedAt).
It might also be worth passing back some dummy notes to debug if the problem lies in the request to the SQL database.
The problem is that you are trying to bind to a private Observable collection _DataGridNotes where you should be binding to the property dataGridNotes:
ItemsSource="{Binding dataGridNotes}"

Getting Values displayed in one method call XAML WPF

As the title says, im trying to display the hardcoded values I typed in my XAML file through databindings but in 1 method call instead of binding the textboxes in every specific property.
Model:
public class Person
{
public string Namn { get; set; }
public DateTime Födelsedatum { get; set; }
public int Betyg { get; set; }
public int AntalBarn { get; set; }
public int Favoritsiffra { get; set; }
public string Kommentar { get; set; }
}
View:
<Window x:Class="PiedPiper.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Inlämningsuppgift WPF" Height="400" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<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="Auto"/>
</Grid.ColumnDefinitions>
<Label Content="Personuppgifter" FontSize="35" Grid.Column="0" Grid.Row="0"></Label>
<Separator Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="10,2,-50,2"/>
<Label Content="Namn:" Grid.Column="0" Grid.Row="2" Margin="5,5,210,0"></Label>
<TextBox Name="NamnTextBox" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="118,5,-50,0"/>
<Label Content="Födelsedatum:" Grid.Column="0" Grid.Row="3" Margin="5,5,210,0"></Label>
<TextBox Name="FödelsedatumTextBox" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Margin="118,5,-50,0"></TextBox>
<Label Content="Betyg" Grid.Column="0" Grid.Row="4" Margin="5,5,210,0"></Label>
<ComboBox Name="BetygComboBox" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Margin="218,5,-50,0"></ComboBox>
<Label Content="Antal barn (0-20):" Grid.Column="0" Grid.Row="5" Margin="5,5,210,0"></Label>
<Slider Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="125,5,25,0"></Slider>
<TextBox Name="AntalBarnTextBox" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="300,5,-50,0"></TextBox>
<Label Content="Favoritsiffra (0-99):" Grid.Column="0" Grid.Row="6" Margin="5,5,210,0"></Label>
<TextBox Name="FavoritsiffraTextBox" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Margin="225,5,-50,0"></TextBox>
<CheckBox Content="Visa kommentar" Grid.Row="7" Grid.Column="0" Margin="5,5,210,0"></CheckBox>
<TextBox Name="KommentarTextBox" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" Margin="125,5,-50,-50" Grid.RowSpan="2"></TextBox>
<Button Content="Spara" Grid.Row="8" Grid.Column="0" Margin="125,55,-50,-80" Grid.ColumnSpan="2"></Button>
<Separator Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Margin="10,85,-50,-80"></Separator>
<Label Content="Andre Kordasti" Grid.Row="10" Grid.Column="0" Margin="10,85,-50,-80"></Label>
<Button Content="Avsluta" Grid.Row="10" Grid.Column="0" Margin="225,85,-50,-80" Grid.ColumnSpan="2"></Button>
</Grid>
ViewModel:
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel(Person person)
{
Namn = person.Namn;
Födelsedatum = person.Födelsedatum;
Betyg = person.Betyg;
AntalBarn = person.AntalBarn;
Favoritsiffra = person.Favoritsiffra;
Kommentar = person.Kommentar;
GetPerson();
}
private void GetPerson()
{
Namn = "KurtSune";
Födelsedatum = new DateTime(1980, 09, 06);
Betyg = 3;
AntalBarn = 7;
Favoritsiffra = 10;
Kommentar = "Kommentar...";
var mainWindow = new MainWindow();
mainWindow.NamnTextBox.Text = Namn;
mainWindow.FödelsedatumTextBox.Text = Convert.ToString(Födelsedatum);
mainWindow.BetygComboBox.SelectedValue = Betyg;
mainWindow.AntalBarnTextBox.Text = Convert.ToString(AntalBarn);
mainWindow.FavoritsiffraTextBox.Text = Convert.ToString(Favoritsiffra);
mainWindow.KommentarTextBox.Text = Kommentar;
}
public ICommand SaveCommand { get; set; }
public ICommand AbortCommand { get; set; }
public bool CanSave
{
get
{
if (Namn == null)
{
return false;
}
return !String.IsNullOrWhiteSpace(Namn);
}
}
private string _namn;
public string Namn
{
get { return _namn; }
set { _namn = value; OnPropertyChanged("Namn"); }
}
private DateTime _födelsedatum;
public DateTime Födelsedatum
{
get { return _födelsedatum; }
set { _födelsedatum = value; OnPropertyChanged("Födelsedatum"); }
}
private int _betyg;
public int Betyg
{
get { return _betyg; }
set { _betyg = value; OnPropertyChanged("Betyg"); }
}
private int _antalBarn;
public int AntalBarn
{
get { return _antalBarn; }
set { _antalBarn = value; OnPropertyChanged("AntalBarn"); }
}
private int _favoritSiffra;
public int Favoritsiffra
{
get { return _favoritSiffra; }
set { _favoritSiffra = value; OnPropertyChanged("Födelsedatum"); }
}
private string _kommentar;
public string Kommentar
{
get { return _kommentar; }
set { _kommentar = value; OnPropertyChanged("Kommentar"); }
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
And to add my App.xaml.cs file:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var person = new Person();
var personViewModel = new PersonViewModel(person);
var mainWindow = new MainWindow();
mainWindow.DataContext = personViewModel;
mainWindow.Show();
}
}
So I dont get the following values to be displayed... Please help, Iam new to this.
Here is a simplified example where,
I made a DataTemplate where controls are bound to the Person properties. Benefits are obvious because as you will see whichever end is modified, the other will know/show these changes.
(added a button and a MessageBox to further demonstrate this)
Code:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var person = new Person {Name = "name1", Address = "address1"};
person.PropertyChanged += person_PropertyChanged;
DataContext = person;
}
private void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
MessageBox.Show("data changed");
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var dataContext = (Person) DataContext;
dataContext.Name = "newName";
dataContext.Address = "newAddress";
}
}
internal class Person : INotifyPropertyChanged
{
private string _address;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public string Address
{
get { return _address; }
set
{
_address = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow"
Width="525"
Height="350">
<Window.Resources>
<DataTemplate x:Key="PersonTemplate" DataType="wpfApplication1:Person">
<StackPanel>
<TextBlock Text="Name" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Address" />
<TextBox Text="{Binding Address, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Click="ButtonBase_OnClick" Content="Modify" />
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource PersonTemplate}" />
</StackPanel>
</Grid>
</Window>
Now as you can see there are no hard-coded values in XAML, the object I have passed to DataContext has sample values instead.

Having problems with WPF Databinding

I have 2 objects, a Customer and a Store. There are multiple store locations, each customer has a property called PreferredStoreId (int?) which relates to a Store's Id (int).
In a WPF application I am attempting to build a form that allows a customer to be edited. A combo box exists on this form which is filled with Stores to act as a way of displaying the currently set PreferredStore and a way of changing the preferred store.
My problem is, whilst I can populate the combobox, I cannot get two way binding between the Customer.PreferredId (the object set to the UserControl's datacontext) and the combobox's SelectedItem (a Store Object)'s .Id property.
Here Is my XAML to help make sense:
<UserControl x:Class="ucCustomerEditor"
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:localViewModels="clr-namespace:ViewModels"
xmlns:qc="clr-namespace:QuickConverter;assembly=QuickConverter"
mc:Ignorable="d" d:DesignWidth="750" Height="334">
<UserControl.DataContext>
<localViewModels:CustomerViewModel x:Name="customerViewModel" />
</UserControl.DataContext>
<StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Height="26" Width="50" Content="Save" Margin="5,10" Click="UserAction_Save" />
<Button Height="26" Width="50" Content="Cancel" Margin="10,10" Click="UserAction_Cancel" />
</StackPanel>
<Grid Height="26" Margin="10" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="209"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding FirstName}" Height="23" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1"/>
<Label Content="First Name:" Margin="10,0" VerticalAlignment="Top" FontWeight="Bold"/>
</Grid>
<Grid Height="26" Margin="10" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="209"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding LastName}" Height="23" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1"/>
<Label Content="Last Name:" Margin="10,0" VerticalAlignment="Top" FontWeight="Bold"/>
</Grid>
<Grid Height="26" Margin="10" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="209"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding EmailAddress}" Height="23" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1"/>
<Label Content="Email Address:" Margin="10,0" VerticalAlignment="Top" FontWeight="Bold"/>
</Grid>
<Grid Height="26" Margin="10" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="209"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding PhoneNumber}" Height="23" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1"/>
<Label Content="Phone Number:" Margin="10,0" VerticalAlignment="Top" FontWeight="Bold"/>
</Grid>
<Grid Height="26" Margin="10" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="209"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox Name="cbPreferredStore"
ItemsSource="{Binding Stores}" DisplayMemberPath="DisplayName" Height="23" Margin="10,0,0,0" VerticalAlignment="Top"
HorizontalAlignment="Stretch" Grid.Column="1" SelectedValue="{Binding ElementName=customerViewModel, Path=PreferredStoreId}">
<ComboBox.DataContext>
<localViewModels:StoreListViewModel />
</ComboBox.DataContext>
</ComboBox>
<Label Content="Preferred Store:" Margin="10,0" VerticalAlignment="Top" FontWeight="Bold"/>
</Grid>
<Grid Height="26" Margin="10" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="209"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Password}" Height="23" Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1"/>
<Label Content="Password:" Margin="10,0" VerticalAlignment="Top" Height="26" FontWeight="Bold"/>
</Grid>
</StackPanel>
StoreViewModel code:
ublic class StoreViewModel : BaseViewModel
{
private enum Modes { CREATE, UPDATE }
private Modes _mode;
private Store _store;
public string DisplayName
{
get { return string.Format("{0} ({1})", this._store.LocationName, this._store.Id); }
}
public int Id
{
get { return this._store.Id; }
set
{
this._store.Id = value;
notifyPropertyChanged("Id");
notifyPropertyChanged("DisplayName");
}
}
public string LocationName
{
get { return this._store.LocationName; }
set
{
this._store.LocationName = value;
notifyPropertyChanged("LocationName");
notifyPropertyChanged("DisplayName");
}
}
public string ImageURL
{
get { return this._store.ImageURL; }
set
{
this._store.ImageURL = value;
notifyPropertyChanged("ImageURL");
}
}
public string AddressLine1
{
get { return this._store.AddressLine1; }
set
{
this._store.AddressLine1 = value;
notifyPropertyChanged("AddressLine1");
}
}
public string AddressLine2
{
get { return this._store.AddressLine2; }
set
{
this._store.AddressLine2 = value;
notifyPropertyChanged("AddressLine2");
}
}
public string AddressLine3
{
get { return this._store.AddressLine3; }
set
{
this._store.AddressLine3 = value;
notifyPropertyChanged("AddressLine3");
}
}
public string Suburb
{
get { return this._store.Suburb; }
set
{
this._store.Suburb = value;
notifyPropertyChanged("Suburb");
}
}
public string State
{
get { return this._store.State; }
set
{
this._store.State = value;
notifyPropertyChanged("State");
}
}
public string Postcode
{
get { return this._store.Postcode; }
set
{
this._store.Postcode = value;
notifyPropertyChanged("Postcode");
}
}
public string Country
{
get { return this._store.Country; }
set
{
this._store.Country = value;
notifyPropertyChanged("Country");
}
}
public string PhoneNumber
{
get { return this._store.PhoneNumber; }
set
{
this._store.PhoneNumber = value;
notifyPropertyChanged("PhoneNumber");
}
}
public string EmailAddress
{
get { return this._store.EmailAddress; }
set
{
this._store.EmailAddress = value;
notifyPropertyChanged("EmailAddress");
}
}
public static explicit operator StoreViewModel(EasyDayTea.Store store)
{
return new StoreViewModel(store) { _mode = Modes.UPDATE };
}
public StoreViewModel()
{
_store = new Store();
_mode = Modes.CREATE;
}
public StoreViewModel(Store store)
{
_store = store;
_mode = Modes.UPDATE;
}
public void Cancel()
{
if (_mode == Modes.CREATE)
{
_store = new Store() { };
}
else
{
EasyDayTea.EasyDayTeaClient client = new EasyDayTeaClient();
_store = client.FetchStore(App.AppUserTeaCredental, _store.Id);
client.Close();
}
notifyAll();
}
public void Save()
{
try
{
EasyDayTeaClient client = new EasyDayTeaClient();
if (_mode == Modes.CREATE)
{
client.AddStore(App.AppUserTeaCredental, ImageURL, LocationName, AddressLine1, AddressLine2, AddressLine3, Suburb, State, Postcode, Country, PhoneNumber, EmailAddress);
}
else
{
client.SetStore(App.AppUserTeaCredental, Id, ImageURL, LocationName, AddressLine1, AddressLine2, AddressLine3, Suburb, State, Postcode, Country, PhoneNumber, EmailAddress);
}
client.Close();
MessageBox.Show("Your customer was saved.");
if (_mode == Modes.CREATE)
{
_store = new Store();
notifyAll();
}
else
{
//do nothing.
}
}
catch (Exception ex)
{
MessageBox.Show("There was a problem saving your customer: \r\n" + ex.Message);
}
}
internal void notifyAll()
{
notifyPropertyChanged("Id");
notifyPropertyChanged("LocationName");
notifyPropertyChanged("ImageURL");
notifyPropertyChanged("AddressLine1");
notifyPropertyChanged("AddressLine2");
notifyPropertyChanged("AddressLine3");
notifyPropertyChanged("Suburb");
notifyPropertyChanged("State");
notifyPropertyChanged("Postcode");
notifyPropertyChanged("Country");
notifyPropertyChanged("PhoneNumber");
notifyPropertyChanged("EmailAddress");
notifyPropertyChanged("DisplayName");
}
}
StoreListViewModel Code:
public class StoreListViewModel : BaseViewModel
{
private List<StoreViewModel> _stores;
public List<StoreViewModel> Stores
{
get { return this._stores; }
set
{
this._stores = value;
notifyPropertyChanged("Stores");
}
}
public StoreListViewModel()
{
EasyDayTea.EasyDayTeaClient client = new EasyDayTea.EasyDayTeaClient();
_stores = client.GetStores(App.AppUserTeaCredental).Select(s => (StoreViewModel)s).ToList();
client.Close();
}
}
I suppose that PreferredStoreId property in CustomerViewModel correctly implement the INotifyPropertyChanged interface.
If it's true, then you need change the SelectedValue to SelectedItem of your ComboBox, because SelectedItem property returns the entire object which it current selected. However, the SelectedValuePath property and the SelectedValue uses together as an alternative to the SelectedItem property and as I understand it was not your choice.
Also here:
SelectedValue="{Binding ElementName=customerViewModel, Path=PreferredStoreId}"
Not need ElementName, because a CustomerViewModel set the DataContext assigned by default.

Categories