I'm using MVVMlight in windows phone. The model is bound via xaml through the locator. The view model loads a new instance of the model from a webrequest then assigns it. I'm not sure why the view is not being updated, is it because its a new instance being assigned? If I update a property of the model instead of assigning a new instance, it updates on the view.
How do I update the view when assigning a new instance of the model?
View Model:
public class MyViewModel : ViewModelBase
{
public MyModel Model { get; set; }
public MyViewModel ()
{
if (IsInDesignMode)
{
}
else
{
//async call
api.GetModel(response =>
Deployment.Current.Dispatcher.BeginInvoke
(() =>
{
//this works.
//Model.Property1 = "Some Text";
//this doesn't work
Model = response.Data;
}
));
}
}
}
View.xaml
<UserControl x:Class="Grik.WindowsPhone.CardDetailsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
DataContext="{Binding MyModel, Source={StaticResource Locator}}" >
<Grid x:Name="LayoutRoot" >
<TextBlock Text="{Binding Model.Property1}" />
</Grid>
</UserControl>
I would like to see your code in View Model Locator class
In MyViewModel class you may need to rewrite this property
public MyModel Model { get; set; }
to
private MyModel _model;
public MyModel Model {
get{return this._model;}
set{
this._model = value;
this.NotifyPropertyChanged("Model");
}
}
the ViewModel does not implement INotifyPropertyChanged interface.
The MyModel as well as all of its properties need to use the property changed event as well.
You show you use Model.Property1 in the xaml, Model.Property1 needs to also raise the property changed event.
public class MyModel : INotifyPropertyChanged
{
private string _Property1 = "";
public string Property1
{
get { return this._Property1; }
set
{
if (value != this._Property1)
{
this._Property1 = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Related
I am struggling with this for a while and I cannot figure it out. I have a button and a textBox. The textBox is linked to a property named: MessageDisplay. I want to be able to access this property and update the textBox in several places. Sadly, the PropertyChanged is null. The weird thing is that if I copy/paste the MessageDisplayModel class into the *MessageViewModel * class, it works ...
here is my code :
XAMLfile :
<Grid>
<Button Command="{Binding DisplayTextCommand}" Name="DisplayTextCommand" Margin="53,72,544.6,286" Width="Auto">Push</Button>
<TextBox Name="MessageDisplay" Text="{Binding MessageDisplay, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</Grid>
MessageDisplayModel file
public class MessageDisplayModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _message;
public string MessageDisplay
{
get { return _message; }
set
{
this._message = value;
this.OnPropertyChanged("MessageDisplay");
}
}
public void UpdateTextBox(string output)
{
MessageDisplay = output;
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}//class
MessageViewModel file:
public class MessageViewModel
{
private ICommand _testCommand;
public MessageDisplayModel MessageDisplaySmt = new MessageDisplayModel();
public ICommand DisplayTextCommand
{
get
{
return new DelegateCommand(DisplayMessage);
}
set
{
if (_testCommand == value) return;
_testCommand = value;
}
}
public void DisplayMessage()
{
MessageDisplaySmt.UpdateTextBox("Successfuly downloaded");
}
}//class
MainWindow file
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MessageDisplay.DataContext = new MessageDisplayModel();
DisplayTextCommand.DataContext = new MessageViewModel();
}
}//class
I update the MessageDisplay property by using the method UpdateTextBox(string). I call this method on the click of the button. When debugging the property gets updated but when time comes to notify the UI that the property has changed, PropertyChangedEventHandler PropertyChanged has its value null ... But if I write something in the textBox, the PropertyChangedEventHandler PropertyChanged gets changed and isn't null anymore. All I want is to be able to change the textBox's property whenever I want and from anywhere I want to.
Thank you
You are using two different instances of MessageDisplayModel. You must use a shared instance.
Also the DisplayTextCommand is implemented "wrong". The set method is redundant as the property's get always returns a new instance of the ICommand.
MessageViewModel.cs
public class MessageViewModel
{
pulic MessageViewModel()
{
}
pulic MessageViewModel(MessageDisplayViewModel messageDisplayViewModel)
{
this.MessageDisplaySmt = messageDisplayViewModel;
}
public void DisplayMessage()
{
this.MessageDisplaySmt.UpdateTextBox("Successfuly downloaded");
}
public MessageDisplayViewModel MessageDisplaySmt { get; set; }
public ICommand DisplayTextCommand { get => new DelegateCommand(DisplayMessage); }
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Alternatively use XAML to set the DataContext (see MainWindow.xaml). Would require a parameterless constructor.
this.DataContext = new MessageViewModel(new MessageDisplayViewModel());
}
}
MainWindow.xaml
<Window>
<!--
Alternative DataContext declaration using XAML instead of C#.
Requires a parameterless constructor for both view model objects.
-->
<Window.DataContext>
<MessageViewModel>
<MessageViewModel.MessageDisplaySmt>
<MessageDisplayViewModel />
</MessageViewModel.MessageDisplaySmt>
</MessageViewModel>
</Window.DataContext>
<StackPanel>
<Button Command="{Binding DisplayTextCommand}"
Content="Push" />
<TextBox Text="{Binding MessageDisplaySmt.MessageDisplay}" />
</StackPanel>
</Window>
I have dynamic listbox contains textbox to display list items and so I can edit listbox item. My application setting file contains string collection which I want to bind for that listbox. I also want to update that setting files on every change of listbox item, I created class which implements INotifyProprtyChanged. I have converted string collection from settings file into observable collection of custom type which has string property. I bind textbox to property of that custom class and update source property on property change. I want to update observable collection as well. and that updates my app setting file as well. Please help me on this. Any help would be really appreciated. My code:
public class WindowViewModel : INotifyPropertyChanged
{
private ObservableCollection<UrlModel> customcollection;
public ObservableCollection<UrlModel> CustomCollection
{
get { return customcollection; }
set
{
customcollection = value;
NotifyPropertyChanged("CustomCollection");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
public WindowViewModel()
{
List<string> customlist = Properties.Settings.Default.CustomList.Cast<string>().ToList();
List<UrlModel> urllist = new List<UrlModel>();
urllist = customlist.Select(item => new UrlModel() { urlString = item }).ToList();
CustomCollection = new ObservableCollection<UrlModel>(urllist);
}
}
public class UrlModel : INotifyPropertyChanged
{
private string url;
public string urlString
{
get { return url; }
set
{
url = value;
NotifyPropertyChanged("urlString");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel = new WindowViewModel();
ListTwo.ItemsSource = ViewModel.CustomCollection;
}
private WindowViewModel viewModel;
public WindowViewModel ViewModel
{
get { return viewModel; }
set{
viewModel = value;
DataContext = value;
}
}
}
}
Add property changed event handler for each url when inserting into ObservableCollection
public WindowViewModel()
{
List<string> customlist = Properties.Settings.Default.CustomList.Cast<string>().ToList();
List<UrlModel> urllist = new List<UrlModel>();
urllist = customlist.Select(item => new UrlModel() { urlString = item }).ToList();
CustomCollection = new ObservableCollection<UrlModel>(urllist);
foreach(var model in CustomCollection)
{
model.PropertyChaged += SettingsUpdater; //Settings update fucntion
}
}
A naive implementation of SettingsUpdater would just update the whole list of urls in settings whenever one of them changes.
I believe you are using data template to make your listbox editable. If that is the case, while binding the text, include Text="{Binding urlString,UpdateSourceTrigger=PropertyChanged}" in the Xaml code.
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="EditableText" Text="{Binding urlString,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
I've created the simplest binding. A textbox bound to an object in the code behind.
Event though - the textbox remains empty.
The window's DataContext is set, and the binding path is present.
Can you say what's wrong?
XAML
<Window x:Class="Anecdotes.SimpleBinding"
x:Name="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SimpleBinding" Height="300" Width="300" DataContext="MainWindow">
<Grid>
<TextBox Text="{Binding Path=BookName, ElementName=TheBook}" />
</Grid>
</Window>
Code behind
public partial class SimpleBinding : Window
{
public Book TheBook;
public SimpleBinding()
{
TheBook = new Book() { BookName = "The Mythical Man Month" };
InitializeComponent();
}
}
The book object
public class Book : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
private string bookName;
public string BookName
{
get { return bookName; }
set
{
if (bookName != value)
{
bookName = value;
OnPropertyChanged("BookName");
}
}
}
}
First of all remove DataContext="MainWindow" as this sets DataContext of a Window to a string MainWindow, then you specify ElementName for your binding which defines binding source as another control with x:Name="TheBook" which does not exist in your Window. You can make your code work by removing ElementName=TheBook from your binding and either by assigning DataContext, which is default source if none is specified, of a Window to TheBook
public SimpleBinding()
{
...
this.DataContext = TheBook;
}
or by specifying RelativeSource of your binding to the Window which exposes TheBook:
<TextBox Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=TheBook.BookName}"/>
but since you cannot bind to fields you will need to convert TheBook into property:
public partial class SimpleBinding : Window
{
public Book TheBook { get; set; }
...
}
I have two List<ColumnClass>. one for left side listview and another for right side list view. these listviews are in a pop up box. I am modifying the List of both the Listviews and again assigning that to the Listview's ItemsSource. But this doesn't reflect in the UI immediatly. When I close the popup and open again it reflects the changes. What am I missing?
You should replace the List<T> with ObservableCollection<T>, ObservableCollections will update your ListView whenever an Item is removed, If you are just modifing properties your ColumnClass make sure your ColumnClass implements INotifyPropertyChanged this will allow the UI to update when a property changes.
Example:
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyColumns.Add(new ColumnClass { Name = "Column1" });
MyColumns.Add(new ColumnClass { Name = "Column2" });
MyColumns.Add(new ColumnClass { Name = "Column3" });
}
private ObservableCollection<ColumnClass> _myColumns = new ObservableCollection<ColumnClass>();
public ObservableCollection<ColumnClass> MyColumns
{
get { return _myColumns; }
set { _myColumns = value; }
}
}
xaml:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WpfApplication8" Height="368" Width="486" Name="UI" >
<Grid>
<ListView ItemsSource="{Binding ElementName=UI, Path=MyColumns}" DisplayMemberPath="Name" />
</Grid>
</Window>
Model:
public class ColumnClass : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notifies the property changed.
/// </summary>
/// <param name="property">The info.</param>
public void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
You should change List<T> to ObservableCollection<T> or BindingList<T>.
Reason, List doesnot implement INotifyPropertyChanged.
I'm trying to create a user control with dependency properties to bind to. Internally I have a ComboBox that is bound to these same properties, but the binding only works one way. The ComboBox fills from the ItemsSource, but SelectedItem doesn't get updated back to the viewmodel I'm binding to.
A simplified example:
This is the view model to bind with the user control:
public class PeopleViewModel : INotifyPropertyChanged
{
public PeopleViewModel()
{
People = new List<string>( new [] {"John", "Alfred","Dave"});
SelectedPerson = People.FirstOrDefault();
}
public event PropertyChangedEventHandler PropertyChanged;
private IEnumerable<string> _people;
public IEnumerable<string> People
{
get { return _people; }
set
{
_people = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("People"));
}
}
}
private string _selectedPerson;
public string SelectedPerson
{
get { return _selectedPerson; }
set
{
_selectedPerson = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedPerson"));
}
}
}
}
This is the User control:
<UserControl x:Class="PeopleControlTest.PeopleControl"
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"
mc:Ignorable="d" d:DesignHeight="56" d:DesignWidth="637">
<StackPanel >
<ComboBox Margin="11"
ItemsSource="{Binding BoundPeople, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedItem="{Binding BoundSelectedPerson, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</StackPanel>
with code behind
public partial class PeopleControl : UserControl
{
public PeopleControl()
{
InitializeComponent();
}
public static readonly DependencyProperty BoundPeopleProperty =
DependencyProperty.Register("BoundPeople", typeof(IEnumerable<string>), typeof(PeopleControl), new UIPropertyMetadata(null));
public static readonly DependencyProperty BoundSelectedPersonProperty =
DependencyProperty.Register("BoundSelectedPerson", typeof(string), typeof(PeopleControl), new UIPropertyMetadata(""));
public IEnumerable<string> BoundPeople
{
get { return (IEnumerable<string>)GetValue(BoundPeopleProperty); }
set { SetValue(BoundPeopleProperty, value); }
}
public string BoundSelectedPerson
{
get { return (string)GetValue(BoundSelectedPersonProperty); }
set { SetValue(BoundSelectedPersonProperty, value); }
}
}
And this is how I bind the user control in the main window (with the windows data context set to an instance of the viewmodel)
<Window x:Class="PeopleControlTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:PeopleControlTest"
Title="MainWindow" Height="350" Width="525">
<Grid>
<controls:PeopleControl
BoundPeople="{Binding People}"
BoundSelectedPerson="{Binding SelectedPerson}"/>
</Grid>
</Window>
The combobox in the user control fills with the names, but when I select a different name this doesn't get updated back to the view model. Any idea what I'm missing here?
Thanks!
Some properties bind two-way by default (Including SelectedItem) but your BoundSelectedPerson does not. You can set the Mode of the binding:
<controls:PeopleControl
BoundPeople="{Binding People}"
BoundSelectedPerson="{Binding SelectedPerson, Mode=TwoWay}"/>
Or you can make it TwoWay by default by setting a flag on the DependencyProperty:
public static readonly DependencyProperty BoundSelectedPersonProperty =
DependencyProperty.Register("BoundSelectedPerson", typeof(string), typeof(PeopleControl), new FrameworkPropertyMetadata("",FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));