DataBinding and iNotifyPropertyChanged in Xamarin - c#

I'm currently making an app using Xamarin Forms. This app will first call a REST service to retrieve the data and display them then store those data into a SQLite Database. I have an update button where if I click on it, it will prompt the REST service once again to retrieve newer data and replace the old data while the app is running. I have tried to implement the INotifyPropertyChanged but the value just wont' change for me. Am I missing anything with my code below? Thanks!
Vitals Object:
public class Vitals
{
public string Height { get; set; }
public string ID { get; set; }
public string Weight { get; set; }
}
Update Method:
async void OnUpdate(object sender, EventArgs e)
{
string tempUser = globalPatient.Username;
string tempPin = globalPatient.PIN;
patUpdate = patientManager.GetPatientByUsername (tempUser, tempPin).Result;
App.PatientDB.DeletePatient(tempID);
App.PatientDB.AddNewPatient (patUpdate, tempPin);
DisplayAlert ("Updated", "Your information has been updated!", "OK");
}
VitalsViewModal:
public class VitalsViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public VitalsViewModel (Patient patient)
{
vitals = patient.Vitals;
}
private List<Vitals> _vitals;
public List<Vitals> vitals {
get {return _vitals; }
set {
if (_vitals != value) {
_vitals = value;
OnPropertyChanged ("vitals");
}
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
VitalsView
public partial class VitalsView : ContentPage, INotifyPropertyChanged
{
PatientManager patientManager = new PatientManager ();
Patient globalPatient;
public event PropertyChangedEventHandler PropertyChanged;
public VitalsView (Patient patientZero)
{
InitializeComponent ();
BindingContext = new VitalsViewModel (patientZero);
}
}
Xaml
<ListView x:Name="Vitals" ItemsSource="{Binding vitals}" RowHeight="80" BackgroundColor="Transparent">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Vertical" Spacing="0" Padding="15">
<Grid>
<Label Font="17" Text="{Binding Height} " FontAttributes="Bold" TextColor="#449BC4" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" />
<Label Font="14" Text="{Binding Weight, StringFormat='Weight: {0}'}" FontAttributes="Bold" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" />
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
</Grid>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

For vitals to have a change in Xaml, something must replace the whole list, of List<vitals with a new list.
Even though the patient changed and its vitals are new from the update, you have bound to an orphaned patient.vitals whose patient reference is still valid. Hence no change.
You need to specifically change the reference of vitals away from the old one to the new one.
I suggest this:
async void OnUpdate(object sender, EventArgs e)
{
string tempUser = globalPatient.Username;
string tempPin = globalPatient.PIN;
patUpdate = patientManager.GetPatientByUsername (tempUser, tempPin).Result;
App.PatientDB.DeletePatient(tempID);
App.PatientDB.AddNewPatient (patUpdate, tempPin);
MyCurrenViewModel.vitals = patUpdate.Vitals; // Replace old vitals
DisplayAlert ("Updated", "Your information has been updated!", "OK");
}
Note In the above example I would create a property named MyCurrentViewModel on the page, and when assigning the datacontext I would have
public partial class VitalsView : ContentPage, INotifyPropertyChanged
{
VitalsViewModel MyCurrentViewModel { get; set; }
PatientManager patientManager = new PatientManager ();
PatientDemo globalPatient;
public event PropertyChangedEventHandler PropertyChanged;
public VitalsView (Patient patientZero)
{
InitializeComponent ();
//BindingContext = new VitalsViewModel (patientZero);
BindingContext = MyCurrentViewModel = new VitalsViewModel (patientZero);
}
}
Code Review Other Errors
OnUpdate is async which is great, but it never awaits any method call; hence making all calls to it synchronous in nature and blocking the gui thread waiting on results. Never block a gui thread, the app will appear to freeze.

As an option, you can use ObservableCollection instead of List.

Related

How to send gridview selected row data to another window with textboxes in wpf using mvvm

I have tried a bunch of solutions about this problem on google but none seem to be helpful.
I have a button on every row which when clicked open a new window with textboxes. This window should display the selected row cells data.
I load the datagrid from mysql database.
VIEW
textboxes (XML) for second window
<Label Content="{Binding sFirstName, Mode=OneWay }" /> <Label Content="{Binding sLastName, Mode=OneWay }" />
Datagrid
<DataGrid ItemsSource="{Binding Path=MM}" SelectionMode="Extended" SelectedItem="{Binding SelectedItem}" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=sFirstName}" />
<DataGridTextColumn Binding="{Binding Path=sLastName}" />
</DataGrid.Columns>
MODEL
public class MM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string PropertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName)); }
private string _sFirstName, _sLastName;
public string sFirstName { get { return _sFirstName; } set { if (_sFirstName != value) { _sFirstName = value; OnPropertyChanged("sFirstName"); } } }
public string sLastName { get { return _sLastName; } set { if (_sLastName != value) { _sLastName = value; OnPropertyChanged("sLastName"); } } }
public DataRowView SelectedRow
{
get { return SelectedRow; }
set { SelectedRow = value; OnPropertyChanged("SelectedItem"); }
}
}
VIEW MODEL
Public class MV : INotifyPropertyChanged
{
private ICommand cmdLoad;
public ICommand CmdLoad { get { if (cmdLoad == null) cmdLoad = new RelayCommand(p => OnLoad()); return cmdLoad; } }
private void OnLoad() { Load(); }
public ObservableCollection<FinTuitionM> finTuitionM { get; set; }
public ClrIdVMD()
{
Load();
}
public void Load()
{
}
}
Code behind (cs)
public partial class Home : Window
{
MV mv;
public Home()
{ InitializeComponent();
mv = new MV(); DataContext = mv;
}
}
You seem to be very confused, so I have prepared a small example of what I think you are trying to achieve.
I am guessing that you want to have a main view that is essentially read only, and you intend to use a popup to make changes. On this basis the View Model for the main window does not need to implement INotifyPropertyChanged. So a simple View Model would look like this:
public class MV
{
public ObservableCollection<MM> MMs { get; set; }
private ICommand cmdShowDetails;
public ICommand CmdShowDetails
{
get
{
if (cmdShowDetails == null) cmdShowDetails = new RelayCommand(p => ShowDetails());
return cmdShowDetails;
}
}
public void ShowDetails()
{
var detVM = new DetailsVM(SelectedItem);
var dets = new DetailsWindow(detVM);
dets.ShowDialog();
}
public MV()
{
MMs = new ObservableCollection<MM>
{
new MM{sFirstName = "Mickey", sLastName = "Mouse"},
new MM{sFirstName = "Donald", sLastName = "Duck"},
new MM{sFirstName = "Roger", sLastName = "Rabbit"},
};
}
public MM SelectedItem { get; set; }
}
Notice that for demonstration purposes, I have loaded the ObservableCollection with some dummy data. In your case, this is replaced with data from the database.
The MM class that this refers to then looks something like this:
public class MM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
if (PropertyChanged != null)
{
var e = new PropertyChangedEventArgs(propertyName);
PropertyChanged(this, e);
}
}
private string firstName;
public string sFirstName
{
get { return firstName; }
set
{
if (firstName == value)
{
return;
}
firstName = value;
RaisePropertyChangedEvent("sFirstName");
}
}
private string lastName;
public string sLastName
{
get { return lastName; }
set
{
if (lastName == value)
{
return;
}
lastName = value;
RaisePropertyChangedEvent("sLastName");
}
}
}
Notice that SelectedItem is in the View Model (MV) and is an object of class MM, so that when the second window is opened, the ShowDetails command can pass the selected details.
This therefore calls for a new very simple view model for the second (details) window:
public class DetailsVM
{
public MM Detail { get; set; }
public DetailsVM(MM detail)
{
Detail = detail;
}
}
The main window grid xaml now looks like this:
<Grid>
<DockPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<Button Content="Show Details" Command="{Binding CmdShowDetails}"></Button>
</StackPanel>
<DataGrid ItemsSource="{Binding MMs}" SelectedItem="{Binding SelectedItem}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="First Name" Binding="{Binding sFirstName}" />
<DataGridTextColumn Header="Last Name" Binding="{Binding sLastName}" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Grid>
Notice here that I only have one button at the bottom of the window to transfer the details. This is because the details come from the selected item, which is the highlighted row.
The code behind is simply:
public partial class MainWindow : Window
{
private MV _mV;
public MainWindow()
{
InitializeComponent();
_mV = new MV();
DataContext = _mV;
}
}
Finally the xaml for the second (details) window
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40*"/>
<RowDefinition Height="40*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70*"/>
<ColumnDefinition Width="200*"/>
</Grid.ColumnDefinitions>
<Label Content="First Name" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Text="{Binding Detail.sFirstName}" Grid.Column="1" Grid.Row="0" Width="150" Height="25" HorizontalAlignment="Left" />
<Label Content="Last Name" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<TextBox Text="{Binding Detail.sLastName}" Grid.Column="1" Grid.Row="1" Width="150" Height="25" HorizontalAlignment="Left" />
</Grid>
Notice here that the binding is to Detail.sFirstName and Detail.sLastName. The DataContext is a DetailsVM object, which has a property Detail of type MM, hence sFirstName and sLastName are sub-properties of Detail.
This window also has a very simple code behind:
public partial class DetailsWindow : Window
{
private DetailsVM _details;
public DetailsWindow(DetailsVM details)
{
_details = details;
DataContext = _details;
InitializeComponent();
}
}
If you now run this, you will find that changes made in the second window are automatically reflected back into the main window. In practice you will probably want Save and Cancel buttons in the second window.
I hope the above is sufficient to point you in the right direction!

Xamarin Forms Bind Image from Resources/Drawable folder

I have a problem. What I am trying to do is bind an ImageSource from a ViewModel. The image file name is called ledstrip.png and is located in the Resources/Drawable folder. I am using the following xaml:
<ListView ItemsSource="{Binding knownDeviceList}" SelectionMode="None" RowHeight="90">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<AbsoluteLayout HeightRequest="70" Margin="20,20,20,0">
<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>
<Image Source="{Binding DeviceImage}" Grid.RowSpan="2" Grid.Column="0" Margin="5" />
</Grid>
</StackLayout>
</AbsoluteLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And in my ViewModel I have the following code:
public class VM_DeviceList : BindableObject
{
private ObservableCollection<DisplayedDevice> _knownDeviceList;
public ObservableCollection<DisplayedDevice> knownDeviceList
{
get
{
return _knownDeviceList;
}
set
{
if (_knownDeviceList != value)
{
_knownDeviceList = value;
OnPropertyChanged();
}
}
}
public VM_DeviceList()
{
knownDeviceList = new ObservableCollection<DisplayedDevice>();
MyHandler();
}
private async Task LoadKnownDeviceList()
{
foreach (KnownDevice device in App.KnownDeviceList)
{
DisplayedDevice displayedDevice = new DisplayedDevice();
displayedDevice.Id = device.Id;
displayedDevice.Name = device.Name;
switch (device.Type)
{
case "ledstrip":
displayedDevice.DeviceImage = "ledstrip.png";
break;
case "triangle":
displayedDevice.DeviceImage = "triangle.png";
break;
}
knownDeviceList.Add(displayedDevice);
}
}
public Task MyHandler()
{
return LoadKnownDeviceList();
}
public class DisplayedDevice
{
public int Id { get; set; }
public string Name { get; set; }
public string DeviceImage { get; set; }
}
}
The problem is that when I type "ledstrip.png" in the xaml ImageSource, the image gets displayed, but when I bind it like the way I show above, no image appears on the screen!
What am I doing wrong and how can I fix this?
Since you are altering the DisplayImage on a separate for loop. You have to Notify the UI that the DisplayImage property value has been changed.
Use INotifyPropertyChanged for notifying that DisplayImage property of DisplayedDevice class has been changed to the UI.
public class DisplayedDevice : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string deviceImage;
public int Id { get; set; }
public string Name { get; set; }
public string DeviceImage
{
get
{
return deviceImage;
}
set
{
deviceImage = value;
OnPropertyChanged();
}
}
}

Observablecollection from interface add to listview via binding

I have an application that draws use of Xabre.ble plugin.
When the application scans for devices, once a device is discovered the application appends it to an ObservableCollection which derives from an interface.
I have made a Data template in Xaml that i wish to populate, but for some reason i can't seem to get the BindingContext right.
Basically, what I want to achieve, is to add device objects to my ObservableCollection IDevice, and fetch just the name of the individual devices in the bindingcontext for the user to see in the listview
Xaml:
<ListView x:Name="deviceList">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<local:IconView Grid.Row="0" Grid.Column="0" Source="BLE.png" Foreground="#3b5998" WidthRequest="30" HeightRequest="30"/>
<Label Grid.Row="1" Text="{Binding DeviceName}" TextColor="Teal"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Cs:
public ObservableCollection<IDevice> Devices { get; set; }
IAdapter adapter = CrossBluetoothLE.Current.Adapter;
public ObservableCollection<string> DeviceName { get; set; }
public MainPage()
{
//Instantiate observable collection
Devices = new ObservableCollection<IDevice>();
DeviceName = new ObservableCollection<string>();
//Appending peripherals to list
BindingContext = DeviceName;
deviceList.ItemsSource = BindingContext;
Content = deviceList;
Refreshcmd();
}
public void Refreshcmd()
{
//DeviceDiscovered is an event, when it discovers a device, we fire the eventhandler
adapter.DeviceDiscovered += (s, a) =>
{
if (a.Device.Name != null)
{
Devices.Add(a.Device);
DeviceName.Add(a.Device.Name);
}
};
adapter.StartScanningForDevicesAsync();
}
As you can see, I have tried to make an object which contains a string with just the name of the IDevice. However, it doesn't append to my databinding DeviceName.
Furthermore, I don't really like this solution, as I would much rather be able to access the Devices.Name from the interface, but that is not allowed apparently.
- the reason why I would much rather like that solution is that the user eventually will have to connect to a device from the list, meaning that it is much easier to have one object for all the "behind the scenes" details.
You should refactor your code, to a MVVM approach.
namespace YourNameSpace.ViewModels
{
public class YourViewModel
{
private ObservableCollection<IDevice> devices;
public ObservableCollection<IDevice> Devices
{
get { return devices; }
set
{
devices = value;
}
}
public YourViewModel()
{
Devices = new ObservableCollection<IDevice>();
}
}
}
In your Page.xaml.cs
public partial class YourPage : ContentPage
{
public YourPage()
{
InitializeComponent();
BindingContext = new YourViewModel();
}
}
public void Refreshcmd()
{
//DeviceDiscovered is an event, when it discovers a device, we fire the eventhandler
adapter.DeviceDiscovered += (s, a) =>
{
if (a.Device.Name != null)
{
(YourViewModel)Devices.Add(a.Device);
//DeviceName.Add(a.Device.Name);
}
};
adapter.StartScanningForDevicesAsync();
}
Then in Page.xaml
<ListView ItemsSource="{Binding Devices}"
CachingStrategy="RecycleElement">

WPF: Bind property to TextBlock not working

I have below xaml file (this a piece):
<Grid Opacity="1" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="ID"/>
<Label Grid.Row="0" Grid.Column="1" Content="Name"/>
<Label Grid.Row="0" Grid.Column="2" Content="Description"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding ID}"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Name}"/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding Description}"/>
</Grid>
Below the Data Class:
public class Data : INotifyPropertyChanged
{
private string id= string.Empty;
private string name = string.Empty;
private string description = string.Empty;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string ID
{
get
{
return this.id;
}
set
{
if (value != this.id)
{
this.id = value;
NotifyPropertyChanged("ID");
}
}
}
public string Name
{
get
{
return this.name;
}
set
{
if (value != this.name)
{
this.name = value;
NotifyPropertyChanged("Name");
}
}
}
public string Description
{
get
{
return this.description;
}
set
{
if (value != this.description)
{
this.description = value;
NotifyPropertyChanged("Description");
}
}
}
}
Also in xaml.cs I implement INotifyPropertyChanged:
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
Furthermore, in the above xaml I have a button defined as:
<Button Click="btn_Click"/>
and the implementation for that is in xaml.cs as below:
private void btn_Click(object sender, System.Windows.RoutedEventArgs e)
{
(DataContext as MyViewModel).SearchInDb('0303003'); // 0303003 -> this is only an example.
}
On button click a method on MyViewModel class is called, and from there it invokes a query to database to retrieve data using ID = 0303003.
Below MyViewModel class (I show only the method):
public void SearchInDb(string id)
{
// request data to sql server database
// and then populate a Data Class Object with the data retrieved
// from database:
Data = new Data(){
ID = sReader[0].ToString().Trim(),
Name = sReader[1].ToString().Trim(),
Description = sReader[2].ToString().Trim()
};
}
Note: MyViewModel class does not implement INotifyPropertyChanged.
My problem is the following:
After populating a new Data object within above method "SearchInDb", my labels in the grid are not updated, they remain empty.
You need to set the View's DataContext:
View.xaml.cs
public View()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
But there is some other issues in your snippets. In the MVVM way, it is the ViewModel which is supposed to implements INotifyPropertyChanged. Not the data class.
Here is how it is supposed to be:
Data Class
public class Data
{
public string Id {get;set;}
public string Name {get;set;}
public string Description {get; set;}
}
MyViewModel
public class MyViewModel : INotifyPropertyChanged
{
private Data _data;
public string ID
{
get { return _data.Id;}
set
{
if(_data.Id != value)
{
_data.Id = value;
NotifyPropertyChanged();
}
}
}
// Same stuff for the other properties
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void SearchInDb(string id)
{
// request data to sql server database
// and then populate a Data Class Object with the data retrieved
// from database:
_data = new Data()
{
Id = sReader[0].ToString().Trim(),
Name = sReader[1].ToString().Trim(),
Description = sReader[2].ToString().Trim()
};
NotifyPropertyChanged(nameof(ID));
NotifyPropertyChanged(nameof(Name));
NotifyPropertyChanged(nameof(Description));
}
}
There was nothing wrong with your NotifyPropertyChanged code. It is just an old way of doing it. This way is more modern and does not require magic strings ;-).
You can also bind the Command dependency property of your button to your SearchInDb methods by using a Command property in you view model. This way, you do not need to write code in your code behind. But that's another question :D.
And there is no need for your View to implements INotifyPropertyChanged (unless your case specifically required this).

Dependency Property won't update after changing datacontext

I have a WPF textbox with a binding to the datacontext.
<TextBox Grid.Column="1" Grid.Row="4" Text="{Binding Path=Density,UpdateSourceTrigger=PropertyChanged}"/>
I set the datacontext in the code of a the container control of the textbox (tabItem in this case)
tiMaterial.DataContext = _materials[0];
I also have a listbox with other materials. I want to update the textfield, when another material is selected, hence I code:
private void lbMaterials_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
_material = (Material) lbMaterials.SelectedValue;
tiMaterial.DataContext = _material;
}
The Material class implements the INotifyPropertyChanged interface. I have the two-way update working, it's just when I change the DataContext, the bindings seem to be lost.
What am I missing?
I tryed to do what you describe in your post but sincerely I didn’t find the problem. In all the cases that I tested my project works perfectly.
I don’t like your solution because I think that MVVM is more clear, but your way works too.
I hope this helps you.
public class Material
{
public string Name { get; set; }
}
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
Materials = new Material[] { new Material { Name = "M1" }, new Material { Name = "M2" }, new Material { Name = "M3" } };
}
private Material[] _materials;
public Material[] Materials
{
get { return _materials; }
set { _materials = value;
NotifyPropertyChanged("Materials");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
gridtext.DataContext = (lbox.SelectedItem);
}
}
.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="gridtext">
<TextBlock Text="{Binding Name}" />
</Grid>
<ListBox x:Name="lbox"
Grid.Row="1"
ItemsSource="{Binding Materials}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>

Categories