I have the following requirements:
Show TreeView of items
Show Details of selected item in TreeView.
A dialog to edit the selected item.
I've implemented these requirements but the third doesn't do what it is supposed to do so I'm stuck.
What I want it to do :
The edit dialog should be able to edit an item. This isn't a TreeViewItem but an instance of one of my classes.
Save the edits - A button that will just close the dialog.
Discard the edits - A button to reset the fields changed in the item and close dialog.
The second requirement does not work. If I edit a field and hit Cancel, the item details panel still shows the edits. I have debugged but I find that the underlying item is unchanged - however the item is displayed with the changed values.
Code:
Item class (Category)
public class Category
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual Unit Unit { get; set; }
public virtual List<Category> ChildCategories { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual bool IsMainCategory { get; set; }
public Category()
{
ChildCategories = new List<Category>();
}
public virtual void AddChild(Category child)
{
ChildCategories.Add(child);
child.ParentCategory = this;
}
}
Item (Category) Details are shown in a label:
<DataTemplate DataType="{x:Type local:Category}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*" SharedSizeGroup="a" />
<ColumnDefinition Width="6*" SharedSizeGroup="b" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock Text="Name" Grid.Column="0" Grid.Row="0" Padding="5"/>
<TextBlock Text="{Binding Path=Name}" Grid.Column="1" Grid.Row="0" Padding="5"/>
<TextBlock Text="Description" Grid.Column="0" Grid.Row="1" Padding="5"/>
<TextBlock Text="{Binding Path=Description}" Grid.Column="1" Grid.Row="1" Padding="5"/>
</Grid>
</DataTemplate>
Event handler for Edit item in Main Window :
private void EditCategory(object sender, RoutedEventArgs e)
{
Category ctg = _tree.SelectedItem as Category;
if (ctg != null)
{
CategoryDefineWindow cdw = new CategoryDefineWindow();
cdw.CategoryObject = ctg;
cdw.ShowDialog();
}
}
Item editor window xaml:
<Window x:Class="BSRCat.View.CategoryDefineWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="150" Width="500">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" SharedSizeGroup="a" />
<ColumnDefinition Width="7*" SharedSizeGroup="b" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock Text="Name" Grid.Column="0" Grid.Row="0" Padding="5"/>
<TextBox Text="{Binding Path=CategoryObject.Name, RelativeSource={RelativeSource AncestorType=Window}}" Grid.Column="1" Grid.Row="0" Padding="5"/>
<TextBlock Text="Description" Grid.Column="0" Grid.Row="1" Padding="5"/>
<TextBox Text="{Binding Path=CategoryObject.Description, RelativeSource={RelativeSource AncestorType=Window}}" Grid.Column="1" Grid.Row="1" Padding="5"/>
<StackPanel Orientation="Horizontal" Grid.Row="2" Grid.ColumnSpan="2" HorizontalAlignment="Right">
<Button Content="Ok" Margin="5" Height="20" Width="30" Click="Confirmed"/>
<Button Content="Cancel" Margin="5" Height="20" Width="50" Click="Cancelled"/>
</StackPanel>
</Grid>
</Window>
Item editor window code behind :
public partial class CategoryDefineWindow : Window
{
public Category CategoryObject
{
get
{
return _category;
}
set
{
_category = value;
_initial = new Category() { Name = value.Name, Description = value.Description };
}
}
private Category _category;
private Category _initial;
public CategoryDefineWindow()
{
InitializeComponent();
}
private void Confirmed(object sender, RoutedEventArgs e)
{
Close();
}
private void Cancelled(object sender, RoutedEventArgs e)
{
_category.Name = _initial.Name;
_category.Description = _initial.Description;
Close();
}
}
I've debugged the CategoryDefineWindow.Cancelled method and the _category object is reset correctly. I can't find where it goes wrong.
Category class should be implemented INotifyPropertyChanged interface. WPF will be notified once the value of properties have changed.
public class Category : INotifyPropertyChanged
{
private string _Name;
private string _Description;
public virtual int Id { get; set; }
public virtual string Name
{
get
{
return _Name;
}
set
{
if (_Name == value)
return;
_Name = value;
NotifyPropertyChanged("Name");
}
}
public virtual string Description
{
get
{
return _Description;
}
set
{
if (_Description == value)
return;
_Description = value;
NotifyPropertyChanged("Description");
}
}
public virtual Unit Unit { get; set; }
public virtual List<Category> ChildCategories { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual bool IsMainCategory { get; set; }
public Category()
{
ChildCategories = new List<Category>();
}
public virtual void AddChild(Category child)
{
ChildCategories.Add(child);
child.ParentCategory = this;
}
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Related
Good morning everyone, I have a sensor model class that, when the state changes, starts a timer that measures random values at intervals. How can I change the measured values on the main form with the same interval?
This is my window xaml markup
<ListBox Width="100" Name="Lst" Grid.Column="0" Grid.Row="1" SelectionChanged="Lst_OnSelectionChanged"/>
<Label Width="244" Style="{StaticResource SideText}" x:Name ="LavalIntervalValue" Grid.Row="1" Grid.Column="1" Margin="215,18,0,344" Height="40"/>
<Label Width="244" Style="{StaticResource SideText}" x:Name ="LabelIdValue" Grid.Row="1" Grid.Column="1" Margin="215,73,0,289" Height="40"/>
<Label Width="244" Style="{StaticResource SideText}" x:Name ="LabelStateValue" Grid.Row="1" Grid.Column="1" Margin="215,130,0,232" Height="40"/>
<Label Width="244" Style="{StaticResource SideText}" x:Name ="LabelMeasuredValue" Grid.Row="1" Grid.Column="1" Margin="215,188,0,174" Height="40" />
<Label Width="186" Style="{StaticResource MainText}" Content="Interval" x:Name ="LabelInterval" Grid.Row="1" Grid.Column="1" Margin="11,18,663,344" Height="40"/>
<Label Width="187" Style="{StaticResource MainText}" Content="Id" x:Name ="LabelId" Grid.Row="1" Grid.Column="1" Margin="10,73,663,289" Height="40"/>
<Label Width="186" Style="{StaticResource MainText}" Content="State" x:Name ="LabelState" Grid.Row="1" Grid.Column="1" Margin="11,130,663,233" Height="39"/>
<Label Width="186" Style="{StaticResource MainText}" Content="Measured Value" x:Name ="LabelMeasured" Grid.Row="1" Grid.Column="1" Margin="11,188,663,175" Height="39"/>
This is my model
public class Sensor:IObservable
{
private int _measuredValue;
private List<IObserver> _observers = new List<IObserver>();
private IMeasuringState _state = new SimpleState();
public Sensor(int measuringInterval, SensorType sensorType, int uniqueId)
{
MeasuringInterval = measuringInterval;
SensorType = sensorType;
UniqueId = uniqueId;
}
[JsonIgnore]
public IMeasuringState MeasuringState
{
get => _state;
set
{
if (value!=null)
{
_state = value;
}
NotifyObservers();
}
}
public int UniqueId { get; }
public int MeasuredValue
{
get => _measuredValue;
set
{
_measuredValue = value;
NotifyObservers();
}
}
public SensorType SensorType { get; }
public int MeasuringInterval { get; }
public void ChangeState()
{
MeasuringState.Handle(this);
MeasuringState.StartMeasure(this);
}
}
Calibration state
public void StartMeasure(Sensor sensor)
{
_calibrationValue = 0;
_timer = new Timer(SetCalibration, sensor, 0, 1000);
}
public void Handle(Sensor sensor)
{
_timer.Dispose();
sensor.MeasuringState = new WorkState();
}
private void SetCalibration(object obj)
{
var sensor = obj as Sensor;
sensor.MeasuredValue = ++_calibrationValue;
}
This is my interface that I implemented in my main window
In property I set my label content value with presenter.
public interface IMainWindowView
{
List<string> SensorsType { get; set; }
int SelectedSensor { get; set; }
int MeasuredValue { get; set; }
string State { get; set; }
int Id { get; set; }
int MeasuringInterval { get; set; }
event EventHandler<EventArgs> OpenFile;
event EventHandler<StateEventArgs> DeleteSensorById;
event EventHandler<EventArgs> SaveFile;
event EventHandler<EventArgs> SelectedItem;
event EventHandler<StateEventArgs> ChangeStateSensor;
event EventHandler<EventArgs> OpenAddWindow;
void ShowWarning(string message);
}
I am using Xamarin Forms and I have created a collection view, I am getting the data from an api after getting the device location, my question is how can I reload the collectionview? Here is my code:
public partial class HomePage : ContentPage
{
APIClass aPIClass = new APIClass();
public HomePage()
{
InitializeComponent();
getLocation();
}
public async void getLocation()
{
try
{
var location = await Geolocation.GetLastKnownLocationAsync();
if (location != null)
{
HomeClass places = aPIClass.getUserPlace(location.Latitude.ToString(), location.Longitude.ToString());
HomeCollection.ItemsSource = places.results;
Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}");
}
}
catch(Exception e)
{
Console.WriteLine(e.Message.ToString());
}
}
}
And my view:
<CollectionView x:Name="HomeCollection">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="150" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<BoxView Color="White" Grid.Column="1" Grid.RowSpan="2"/>
<ContentView Padding="10" Grid.Row="0" Grid.Column="1">
<Image Grid.Row="0" Grid.Column="1" Source="{Binding photos[0].name}" Aspect="AspectFill" />
</ContentView>
<ContentView Padding="10" Grid.Row="1" Grid.Column="1">
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding name}" />
</ContentView>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Classes involved:
public class HomeClass
{
public List<PlacesClass> results { get; set; }
}
public class PlacesClass
{
public string name { get; set; }
public List<PlacesImageClass> photos { get; set; }
}
public class PlacesImageClass
{
public int height { get; set; }
public List<string> html_attributions { get; set; }
public string photo_reference { get; set; }
public int width { get; set; }
public string name { get; set; }
}
I have a problem. I created a ListView with a ViewModel. In my ListView I have a few Labels with Text that is bound to the objects in the ItemSource. Now when I change a value in the ViewModel of an item in the ObservableCollection, nothing changes on the screen!
Here is my ViewModel:
public class VM_DeviceList : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand cmdDeleteDevice
{
get
{
return new Command<int>((x) => DeleteDevice_Handler(x));
}
}
public ICommand cmdTogglePower
{
get
{
return new Command<int>((x) => TogglePower_Handler(x));
}
}
private ObservableCollection<DisplayedDevice> _knownDeviceList;
public ObservableCollection<DisplayedDevice> knownDeviceList
{
get
{
return _knownDeviceList;
}
set
{
if (_knownDeviceList != value)
{
_knownDeviceList = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("knownDeviceList"));
}
}
}
}
Here is my class for the ObservableCollection:
public class DisplayedDevice : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public int Id { get; set; }
public string Name { get; set; }
public string State { get; set; }
public string StateShown { get; set; }
public string deviceImage { get; set; }
public string Color { get; set; }
public string PowerStateColor { get; set; }
public string DeviceImageColor { get; set; }
public string DeviceImage
{
get
{
return deviceImage;
}
set
{
deviceImage = value;
OnPropertyChanged();
}
}
}
And here is the xaml:
<ListView ItemsSource="{Binding knownDeviceList}" SelectionMode="None" RowHeight="90" ItemTapped="device_Clicked" x:Name="MyListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<AbsoluteLayout HeightRequest="70" Margin="20,10,20,10">
<StackLayout Opacity="0.3" BackgroundColor="White"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All" />
<StackLayout AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All">
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="70" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding Name}" Margin="0,3,0,0"
FontAttributes="Bold" FontSize="24" TextColor="White" />
<Label Grid.Row="1" Grid.Column="1" Text="{Binding StateShown}" FontSize="18" TextColor="White" />
<!-- <Image Source="power" Grid.RowSpan="2" Grid.Column="2" Margin="5" /> -->
<controls:IconView x:Name="btnPower" Source="power" Grid.RowSpan="2" Grid.Column="2" Margin="5"
Foreground="{Binding PowerStateColor}">
<controls:IconView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.cmdTogglePower, Source={x:Reference MyListView}}" CommandParameter="{Binding Id}" />
</controls:IconView.GestureRecognizers>
</controls:IconView>
</Grid>
</StackLayout>
</AbsoluteLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now what I expected to happen, was that when I click on the IconView I change the IconView color and the label with the StateShown binding. But nothing changes when I click on the IconView!
What am I doing wrong?
Add OnPropertyChanged method call to every property on DisplayedDevice for those you want the UI to notice.
public class DisplayedDevice : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged();
}
}
private int id;
public int Id
{
get
{
return id;
}
set
{
Id = value;
OnPropertyChanged();
}
}
private string state;
public string State
{
get
{
return state;
}
set
{
state = value;
OnPropertyChanged();
}
}
......
}
You can use MVVMHelpers NuGet Package and implement the ObservableObject Class from the NuGet directly to DisplayedDevice class and you need to make all the properties with a reference to the private variable.
public class DisplayedDevice: ObservableObject
{
string _textField = string.Empty;
public string TextField
{
get => _textField;
set => SetProperty(ref _textField, value);
}
bool _isBarChartVisible = false;
public bool IsBarChartVisible
{
get => _isBarChartVisible;
set => SetProperty(ref _isBarChartVisible, value);
}
}
}
Every public property should be backed by a private property of the same type. This is important so that any change in any property will be reflected on the UI using INotifyPropertyChanged
I have a database which contains two Columns CoourseID and Course_Name. From the Database i am getting my values in my ObservableCollection but i am not able to display the values of ObservableCollection FillCourseID which is bound to Combobox. I want CourseId and CourseName Both displayed in Combobox. i referred this link https://www.c-sharpcorner.com/article/explain-combo-box-binding-in-mvvm-wpf/
Xaml
<Window x:Class="MVVMDemo.UserRegistrationView"
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:viewmodel="clr-namespace:MVVMDemo"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<viewmodel:ViewModel x:Key="ViewModel"/>
<viewmodel:DatetimeToDateConverter x:Key="MyConverter"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource ViewModel}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Name" HorizontalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1" Width="100" HorizontalAlignment="Center" Text="{Binding Student.Name, Mode=TwoWay}" Margin="76,0"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Age" HorizontalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="1" Width="100" HorizontalAlignment="Center" Text="{Binding Student.Age, Mode=TwoWay}"/>
<Button Content="Submit" Command="{Binding SubmitCommand}" HorizontalAlignment="Right" Grid.Row="4" Grid.Column="0" RenderTransformOrigin="0.61,1.96" Margin="0,27.348,0,159.452"/>
<ComboBox Grid.Column="1" ItemsSource="{Binding Path=FillCourseId}" Name="cmb_CourseIDName" HorizontalAlignment="Left" Margin="76,5,0,0"
Grid.Row="3" VerticalAlignment="Top" Width="120" Grid.RowSpan="2">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=CourseName}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Path=CourseID}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ListView ItemsSource="{Binding Students}" Grid.Row="4" Grid.Column="1" Width="200"
Margin="27.657,34.948,24.342,159.452">
<ListView.View >
<GridView >
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="60"/>
<GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}" Width="60"/>
<GridViewColumn Header="Joining Date" DisplayMemberBinding="{Binding JoiningDate, Converter={StaticResource MyConverter}}" Width="80" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
ViewModel Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data.SqlClient;
using System.Data;
namespace MVVMDemo
{
public class ViewModel : ViewModelBase, INotifyPropertyChanged
{
private Student _student;
private ObservableCollection<Student> _students;
private ICommand _SubmitCommand;
private ObservableCollection<Student> _fillCourseId = new ObservableCollection<Student>();
static String connectionString = #"Data Source=Ramco-PC\SQLEXPRESS;Initial Catalog=SIT_Ramco_DB;Integrated Security=True;";
SqlConnection con;
SqlCommand cmd;
// SqlDataAdapter adapter;
// DataSet ds;
//SqlDataReader reader;
public ObservableCollection<Student> FillCourseId
{
get { return _fillCourseId; }
set
{
_fillCourseId = value;
OnPropertyChanged("SystemStatusData");
}
}
public Student Student
{
get
{
return _student;
}
set
{
_student = value;
NotifyPropertyChanged("Student");
}
}
public ObservableCollection<Student> Students
{
get
{
return _students;
}
set
{
_students = value;
NotifyPropertyChanged("Students");
}
}
public ICommand SubmitCommand
{
get
{
if (_SubmitCommand == null)
{
_SubmitCommand = new RelayCommand(param => this.Submit(),
null);
}
return _SubmitCommand;
}
}
//********************************************* Functions*******************************************//
public void GetCourseIdFromDB()
{
try
{
con = new SqlConnection(connectionString);
con.Open();
cmd = new SqlCommand("select * from dev_Course", con);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
adapter.Fill(dt);
// Student Student = new Student();
for (int i = 0; i < dt.Rows.Count; ++i)
FillCourseId.Add(new Student
{
CourseID = dt.Rows[i][0].ToString(),
CourseName =dt.Rows[i][1].ToString()
});
}
catch (Exception ex)
{
}
}
public ViewModel()
{
Student = new Student();
Students = new ObservableCollection<Student>();
Students.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Students_CollectionChanged);
}
void Students_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("Students");
}
private void Submit()
{
Student.JoiningDate = DateTime.Today.Date;
Students.Add(Student);
Student = new Student();
}
// Property Changed Event
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyname)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyname));
}
}
}
Model Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MVVMDemo
{
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Course { get; set; }
public string CourseID { get; set; }
public string CourseName { get; set; }
public DateTime JoiningDate { get; set; }
}
}
i want both CourseID and CourseName To be displayed in the combobox
The simplest solution is to override the ToString() member of the Student class to give the Student the desired string representation:
public class Student
{
public override string ToString() => $"Course Name: {this.CourseName} ({this.CourseID})";
public string Name { get; set; }
public int Age { get; set; }
public string Course { get; set; }
public string CourseID { get; set; }
public string CourseName { get; set; }
public DateTime JoiningDate { get; set; }
}
Alternatively bind directly to a ObservableCollection<string> and maybe use a Dictionary<string, Student> to map the SelectedItem to a Student
or override the Style of the ComboBox to display the SelectedItem instead of the SelectionBoxItem and then set the ComboBox.ItemTemplate to add a DataTemplate that describes the ComboBoxItem to display the required combination of properties with a special layout.
The issue was that GetCourseIdFromDB() was being called earlier in app.xaml.cs class means calling it in different instance. The Change that i made was that I called GetCourseIdFromDB() function inside a cinstructor in ViewModel Class.
and it fetched the data and displayed in my ComboBox in desired style.
public ViewModel()
{
Student = new Student();
Students = new ObservableCollection<Student>();
Students.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Students_CollectionChanged);
GetCourseIdFromDB();
}
In the following example, the Message property binds correctly but the FirstName, LastName and Age properties of the Customer object are blank. Why is that?
XAML:
<Window x:Class="TestBinding9922.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<TextBlock Text="{Binding Message}"/>
<Grid DataContext="{Binding Customer}" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="First Name:" />
<Label Grid.Column="0" Grid.Row="1" Content="Last Name:" />
<Label Grid.Column="0" Grid.Row="2" Content="Age:" />
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding FirstName}" />
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding LastName}" />
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Age}" />
</Grid>
</StackPanel>
</Window>
Code-Behind:
using System.Windows;
using System.ComponentModel;
namespace TestBinding9922
{
public partial class Window1 : Window, INotifyPropertyChanged
{
public string Message { get; set; }
public Customer Customer { get; set; }
public Window1()
{
InitializeComponent();
DataContext = this;
Message = "this works";
Customer customer = new Customer() { FirstName = "Jim", LastName = "Smith", Age = 45 };
}
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
}
Addendum:
Even when I use INotifyPropertyChanged, the textboxes are still blank:
using System.Windows;
using System.ComponentModel;
namespace TestBinding9922
{
public partial class Window1 : Window, INotifyPropertyChanged
{
#region ViewModelProperty: Message
private string _message;
public string Message
{
get
{
return _message;
}
set
{
_message = value;
OnPropertyChanged("Message");
}
}
#endregion
#region ViewModelProperty: Customer
private Customer _customer;
public Customer Customer
{
get
{
return _customer;
}
set
{
_customer = value;
OnPropertyChanged("Customer");
}
}
#endregion
public Window1()
{
InitializeComponent();
DataContext = this;
Message = "this works";
Customer customer = new Customer() { FirstName = "Jim", LastName = "Smith", Age = 45 };
}
#region INotifiedProperty Block
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
}
Your Customer class needs to implement INotifyPropertyChanged and subsequently the properties that are being changed need to notify when they get changed.
You can find an example of this here, among other places.
Also, you're not instantiating your Customer property, you're creating a local variable; it should be:
Customer = new Customer()
{ FirstName = "Jim", LastName = "Smith", Age = 45 };
It looks like you're assigning the new Customer instance to a local variable "customer" instead of to your Window's property.
You could remove the setting of the DataContext for the grid in the XAML, and then bind the textboxes to Customer.FirstName, etc.
Or, you could remove the setting of the DataContext for the grid in the XAML, as before, but give the grid a name, and then set its DataContext in the code-behind, like you did for the window.