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>
Related
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!
Problem
I am trying to bind a ComboBox's SelectedItem to a custom class but this does not update when the property is changed.INotifyPropertyChanged is implemented.
The DataContext
The DataContext is a custom class which contains many properties, but an extract of this is below. You can see it implements INotifyPropertyChanged and this called when the two properties are changed.
public class BctsChange : INotifyPropertyChanged
{
#region declarations
private byContact _Engineer;
public byContact Engineer
{
get { return _Engineer; }
set
{
_Engineer = value;
NotifyPropertyChanged("Engineer");
OnEngineerChanged();
}
}
private BctsSvc.DOSets _LeadingSet;
public BctsSvc.DOSets LeadingSet
{
get { return _LeadingSet; }
set { _LeadingSet = value; NotifyPropertyChanged("LeadingSet"); }
}
#endregion
#region INotify
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
public BctsChange()
{
Engineer = new byContact(Environment.UserName);
}
private void OnEngineerChanged()
{
if (Engineer != null)
{
BctsSvc.DOSets leadSet = GetLeadingSetFromDeptCode(Engineer.DeptCode);
if (leadSet == null) return;
LeadingSet = leadSet;
}
}
private static BctsSvc.DOSets GetLeadingSetFromDeptCode(string DeptCode)
{
BctsSvc.BctsServiceSoapClient svc = new BctsSvc.BctsServiceSoapClient();
BctsSvc.DOSets setX = svc.GetSetFromDeptCode(DeptCode);
return setX;
}
}
The Window XAML
I have several controls on the window, but to keep the code simple I believe the following extract will suffice.
<Window x:Class="MyNamespace.wdSubmit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:MyNamespace"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
x:Name="ucReqForm"
Title="wdSubmit" >
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
<GroupBox Header="Engineer Details" Name="grpOwnerDetails" >
<StackPanel Orientation="Vertical">
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="35"/>
</Grid.ColumnDefinitions>
<Label Content="{Binding Engineer.FullName, FallbackValue='Please select an engineer by clicking →', Mode=OneWay}" Margin="5,0" IsEnabled="True" FontStyle="Italic" />
<Button Content="{StaticResource icoSearch}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Column="1" Height="23" Name="btnSelectEngineer" Margin="0,0,5,0" HorizontalAlignment="Stretch" ToolTip="Search for an engineer responsible" Click="btnSelectEngineer_Click" />
</Grid>
<ComboBox Height="23" x:Name="ddSet2" Margin="5,0" ItemsSource="{Binding LeadingSets, Mode=OneWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" SelectedItem="{Binding LeadingSet, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,NotifyOnTargetUpdated=True}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SetName}" ToolTip="{Binding HelpInfo}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<my:LabelledDropdown Height="23" x:Name="ddSet" Margin="5,0" ItemsSource="{Binding LeadingSets, Mode=OneWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" SelectedItem="{Binding LeadingSet, Mode=TwoWay,NotifyOnTargetUpdated=True,NotifyOnSourceUpdated=True}" Label="e.g. BodyHardware">
<my:LabelledDropdown.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SetName}" ToolTip="{Binding HelpInfo}"/>
</DataTemplate>
</my:LabelledDropdown.ItemTemplate>
</my:LabelledDropdown>
</StackPanel>
</GroupBox>
</StackPanel>
</Window>
The above extract contains:
A Label that contains a contact's name, and a button to search for a contact, bound to the FullName of the Engineer
A ComboBox that contains departments within the company, bound to an ObservableCollection<DOSets>, which contains a list of departments
Two ComboBoxes, one which is a custom one and the other which is temporary to ensure the bug is not within the control. These are Databound to LeadingSet
Window Code Behind
In the code behind I set the DataContext to CurrentChange. When the user wants to select a different Engineer then this will update the selected department for the engineer in CurrentChange.
When the user changes the engineer, the data binding for the engineer is updated, but the selected department (Leading Set) isn't.
//Usings here
namespace MyNamespace
{
public partial class wdSubmit : Window, INotifyPropertyChanged
{
private BctsSvc.BctsServiceSoapClient svc;
private BctsChange _CurrentChange;
public BctsChange CurrentChange
{
get { return _CurrentChange; }
set { _CurrentChange = value; OnPropertyChanged("CurrentChange"); }
}
private List<BctsSvc.DOSets> _LeadingSets;
public List<BctsSvc.DOSets> LeadingSets
{
get
{
return _LeadingSets;
}
}
public wdSubmit()
{
InitializeComponent();
svc = new BctsSvc.BctsServiceSoapClient();
_LeadingSets = svc.GetLeadSets().ToList();
OnPropertyChanged("LeadingSets");
this._CurrentChange = new BctsChange();
this.DataContext = CurrentChange;
CurrentChange.PropertyChanged += new PropertyChangedEventHandler(CurrentChange_PropertyChanged);
}
void CurrentChange_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("CurrentChange");
OnPropertyChanged(e.PropertyName);
}
private void btnSelectEngineer_Click(object sender, RoutedEventArgs e)
{
byContact newContact = new frmSearchEngineer().ShowSearch();
if (newContact != null)
{
CurrentChange.Engineer = newContact;
PropertyChanged(CurrentChange, new PropertyChangedEventArgs("LeadingSet"));
PropertyChanged(CurrentChange.LeadingSet, new PropertyChangedEventArgs("LeadingSet"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(CurrentChange, new PropertyChangedEventArgs(propertyName));
}
}
}
I've realised the problem may be due to the LeadingSet, returned when the engineer is changed, being a different instance to that in the ObservableCollection.
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.
I have a ListBox created by ItemTemplate and Binding
<controls:PanoramaItem Header="{Binding AppResources.SettingsSubpage2, Source={StaticResource LocalizedStrings}}" HeaderTemplate="{StaticResource HeaderTemplate}">
<Grid>
<ListBox x:Name="DayOfWeekSelector" ItemTemplate="{StaticResource DayOfWeekTemplate}" ItemsSource="{Binding DayOfWeekElementList}" Foreground="{StaticResource AppForegroundColor}" LostFocus="DayOfWeekSelector_LostFocus" HorizontalAlignment="Left" Width="420" />
</Grid>
</controls:PanoramaItem>
Template code:
<phone:PhoneApplicationPage.Resources>
<!--- ... --->
<DataTemplate x:Key="DayOfWeekTemplate">
<Grid Height="65" Width="332">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox IsChecked="{Binding IsActive, Mode=TwoWay}" Tag="{Binding}" d:LayoutOverrides="Width, Height" BorderBrush="{StaticResource AppBackgroundColor}" Background="{StaticResource ScheduleBackgroundAccentsColor}" Grid.Column="0" Unchecked="CheckBox_Unchecked" />
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" d:LayoutOverrides="Width"/>
<TextBlock Text="{Binding TaskCounter, Mode=OneWay, Converter={StaticResource DayOfWeekCounter}}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0"/>
</StackPanel>
</Grid>
</DataTemplate>
<!--- ... --->
And it's working fine. I've got all my items on the list. Checkboxes are binded to appropriate elements (clicking on it is changing proper value).
But by default ListBox can be also selected. Selection high-light TextBox binded to Name but don't change CheckBox (binded to IsActive). How can I tie item selection changing to checkbox state changing (in Silverlight)?
Edit:
public partial class SettingsPage : PhoneApplicationPage, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public List<DayOfWeekElement> DayOfWeekList
{
get
{
return CyberSyncPlanBase.Instance.DayOfWeekElementList;
}
set
{
CyberSyncPlanBase.Instance.DayOfWeekElementList = value;
NotifyPropertyChanged("DayOfWeekList");
}
}
public SettingsPage()
{
InitializeComponent();
DayOfWeekSelector.DataContext = CyberSyncPlanBase.Instance;
}
private void DayOfWeekSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DayOfWeekElement dowe = (DayOfWeekElement) DayOfWeekSelector.SelectedItem;
if (dowe != null)
dowe.IsActive = (dowe.IsActive) ? false : true;
}
And in singleton INotifyPropertyChanged i've implemented in the same way:
private List<DayOfWeekElement> dayOfWeekElementList;
public List<DayOfWeekElement> DayOfWeekElementList
{
get { return dayOfWeekElementList; }
set
{
dayOfWeekElementList = value;
RecalcWeekTasks();
NotifyPropertyChanged("DayOfWeekElementList");
}
}
Bottom class:
public class DayOfWeekElement
{
public string Name { get { return this.DayOfWeek.ToStringValue(); } }
public bool IsNotEmpty { get { return (TaskCounter > 0); } }
public int TaskCounter { get; set; }
public bool IsActive { get; set; }
public DayOfWeek DayOfWeek { get; set; }
}
I think you could use the SelectedItem property of the ListBox control.
A possible implementation could be this:
Subscribe to the event SelectedIndexChanged of the ListBox.
Get the selected item.
For the selected item, change its IsActive property to true.
This works if the interface INotifyPropertyChanged is implemented in your data class.
E.g.:
public class DayOfWeekElement : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool isActive = false;
public bool IsActive {
get
{
return this.isActive;
}
set
{
if (value != this.isActive)
{
this.isActive= value;
NotifyPropertyChanged("IsActive");
}
}
}
}
I am having trouble binding my View Model to my View. I am a beginner with MVVM, but I believe I am implementing my system (almost) correctly. I have a Model that contains data, which I am getting in my View Model, and then when my page is navigated to, I am attempting to grab that View Model data and binding it to the View.
My issue is that I have a ListBox in my View with 3 objects per item, and I cannot seem to bind to it correctly for each item in my list.
MainPage.xaml
<ListBox x:Name="FavoritesListBox" ItemsSource="{Binding FavoriteItems}"
SelectionChanged="FavoritesListBox_SelectionChanged">
<StackPanel Orientation="Horizontal" Margin="12,0,12,0">
<Image x:Name="favicon" Source="{Binding Favicon}"
Width="50" Height="50"/>
<StackPanel>
<TextBlock x:Name="favoritesName" Text="{Binding Name}"
FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
<TextBlock x:Name="favoritesAddress"
Text="{Binding Address}" Margin="12,0,0,0"/>
</StackPanel>
</StackPanel>
</ListBox>
MainPage.xaml.cs
public FavoritesPage()
{
InitializeComponent();
// Set the data context of the listbox control to the sample data
FavoritesListBox.DataContext = App.ViewModel;
}
App.xaml.cs
private static MainViewModel viewModel = null;
public static MainViewModel ViewModel
{
get
{
// Delay creation of the view model until necessary
if (viewModel == null)
viewModel = new MainViewModel();
return viewModel;
}
}
MainViewModel.cs
public ObservableCollection<ItemViewModel> FavoriteItems { get; private set; }
public MainViewModel()
{
//FavoriteItems = new ObservableCollection<ItemViewModel>();
FavoriteItems = Settings.FavoritesList.Value;
}
Settings.cs (The Model)
public static Setting<ObservableCollection<ItemViewModel>> FavoritesList =
new Setting<ObservableCollection<ItemViewModel>>(
"Favorites",
new ObservableCollection<ItemViewModel>());
ItemViewModel.cs
private string _favicon;
public string Favicon
{
get
{
return _favicon;
}
set
{
if (value != _favicon)
{
_favicon = value;
NotifyPropertyChanged("Favicon");
}
}
}
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
private string _address;
public string Address
{
get
{
return _address;
}
set
{
if (value != _address)
{
_address = value;
NotifyPropertyChanged("Address");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
..and this is where and how I am saving each item (which should have three properties listed in the ItemViewModel
void addToFavorites_Click(object sender, EventArgs e)
{
var favoriteItem =
new ItemViewModel{
Favicon = "",
Name = "",
Address = TheBrowser.currentUrl() };
Settings.FavoritesList.Value.Add(favoriteItem);
}
Where FavoritesList is populated using an ItemViewModel containing 3 objects. The list is being populated correctly because during debugging I can see the entities in FavoritesList, but I am having an issue calling these entities in the view model to show up in my ListBox in the view?
I believe I am binding incorrectly but I'm not sure how to fix this?
In your XAML you bind to paths Name and Address do you have these 2 properties defined in your ItemViewModel?
Update after reading your code properly:
You are not updating the datatemplate of the Items of the Listbox. This is what you need to do:
<ListBox x:Name="FavoritesListBox" ItemsSource="{Binding FavoriteItems}" SelectionChanged="FavoritesListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="12,0,12,0">
<Image x:Name="favicon" Source="{Binding Favicon}" Width="50" Height="50"/>
<StackPanel>
<TextBlock x:Name="favoritesName" Text="{Binding Name}" FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
<TextBlock x:Name="favoritesAddress" Text="{Binding Address}" Margin="12,0,0,0"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In addition to setting your DataContext to your viewmodel, (as mentioned in the comment linking to Creating ContextBinding XAML) , you also need to have your view model implement INotifyPropertyChanged (including ItemViewModel, which you don't show in your question)