XAML GridView, ObservableCollection and changing an item's properties - c#

I have a class, derived from BindableBase, which contains two properties:
private string sourceCallNumber;
public string SourceCallNumber
{
get { return sourceCallNumber; }
set { SetProperty(ref sourceCallNumber, value); }
}
private string sourceMediaType;
public string SourceMediaType
{
get { return sourceMediaType; }
set { SetProperty(ref sourceMediaType, value); }
}
I have an ObservableCollection that contains a number of items using that class.
I have a GridView for which I set the ItemsSource to point to the ObservableCollection.
My problem is that if I change the value of, say, SourceMediaType on one of the items, the display does not update. I have put debugging in and can confirm that changing the value causes OnPropertyChanged to fire for that property.
I've read quite a few SO questions and answers around similar problems and I'm getting quite confused as to what I need to do in order to get this to work.
My understanding was that although ObservableCollection itself doesn't do anything when a property is changed, if the item itself triggers an OnPropertyChanged, that should get the display to update.
(There was one answer I read that proposed the use of code provided called TrulyObservableCollection but the problem I've got there is that everything refreshes rather than just the one item that has been updated).
What am I missing or misunderstanding, please?
Thanks.

C# apps should implement INotifyCollectionChanged and System.Collections.IList (not IList Of T).
public class NameList : ObservableCollection<PersonName>
{
public NameList() : base()
{
Add(new PersonName("Willa", "Cather"));
Add(new PersonName("Isak", "Dinesen"));
Add(new PersonName("Victor", "Hugo"));
Add(new PersonName("Jules", "Verne"));
}
}
public class PersonName
{
private string firstName;
private string lastName;
public PersonName(string first, string last)
{
firstName = first;
lastName = last;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
Look at GridView.

#RodrigoSilva put me on the correct path ... the XAML that references the values was this:
<StackPanel>
<TextBlock Text="{Binding DisplayCallNumber}" Style="{StaticResource TitleTextBlockStyle}" Visibility="{Binding GotCallNumber, Converter={StaticResource DisplayIfTrue}}" Margin="0,0,0,10"/>
<TextBlock Text="{Binding DisplayMediaType}" Style="{StaticResource ItemTextStyle}" Visibility="{Binding GotMediaType, Converter={StaticResource DisplayIfTrue}}" Margin="0,0,0,10"/>
</StackPanel>
which doesn't directly reference the underlying properties SourceCallNumber and SourceMediaType. As a result, although OnPropertyChanged is correctly firing for SourceCallNumber and SourceMediaType, that isn't causing the display to update because that isn't what the XAML is pointing at.
Explicitly changing the call to SetProperty to this:
SetProperty(ref sourceCallNumber, value, "DisplayCallNumber");
fixes the problem but is not a GOOD fix because some other part of the app may actually be binding to SourceCallNumber and won't get a property update after this change. The GOOD fix is to use a converter as explained in http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh464965.aspx.

Related

Raising a property change event for class object

I have a class as follows:
public class UserData : INotifyPropertyChnaged
{
public string strUserName;
public string UserName
{
get { return strUserName; }
set { SetProperty(ref strUserName, value); }
}
private string strPhoneNumber;
public string UserPhoneNumber
{
get { return strPhoneNumber; }
set { SetProperty(ref strPhoneNumber, value); }
}
private List<UserMailID> listUserMailID;
public List<UserMailID> ListOfUserMailID
{
get { return listUserMailID; }
set { SetProperty(ref listUserMailID, value); }
}
}
I'm accessing this class at some location and this class object I am binding to my view:
private UserData cActiverUser;
public UserData ActiverUser
{
get { return cActiverUser; }
set { SetProperty(ref cActiverUser , value); }
}
Suppose there is UserName field in my view; my data binding goes like this:
TextBox.Text="{Binding ActiverUser.StrUserName, UpdateSourceTrigger=PropertyChanged}
Binding works well, but I am unable to raise the property changed event when I change this user name field on my view. I have tried setting Mode="TwoWay" also.
In your XAML binding, try setting NotifyOnTargetUpdated=True, or if that doesn't work NotifyOnSourceUpdated=True.
I had the same issue with a project I did a while ago, and it was one of those that got it working.
Also on this line:
public string strUserName;
Should this be private? and you should be specifying the binding as UserName, not strUserName?
I believe the bindings are also case sensitive.
If you're using Prism, derive UserData and whatever class has UserData as its property from BindableBase, which implements INotifyPropertyChanged, but also lets you use SetProperty(...). I'm not even sure how you were able to get it to work before, but I suspect you're not showing us your original code, since you misspelled INotifyPropertyChanged -- INotifyPropertyChnaged.
Additionally, are you actually instantiating the object? Don't forget to do that.
Lastly, you're binding to ActiverUser.StrUserName, but UserData does not have a property StrUserName. It has UserName, which is what you want to bind to.

Correct way to implement MVVM Design pattern when using SQLite

So lets say I have a SQLite database of Person's with property Name
Public Class Person
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
And now I have a view with a ListBox Displaying those names
<ListBox ItemSource={Binding People}>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and my Views DataContext is PeopleViewVM
Public Class PeopleViewVM
{
Public PeopleViewVM
{
//Do my SQLite stuff,
// Get IEnumerable Person's
// Create Observable Collection of People
}
private ObservableCollection<Person> _people;
public ObservableCollection<Person> People
{
get { return _people; }
set
{
_people = value;
RaisePropertyChanged();
}
}
}
Now I understand this is a simple example. But I am unsure whether this is the correct implementation of the MVVM design pattern.
If Person my model this means that the view is binding directly to the model when it is binding to the property name. If I change the name of a person in code behind this won't be reflected in the view. What is the correct way to do this example using the MVVM design pattern?
That can be the "correct" implementation, based on your requirements. I wouldn't say that there's a "correct", and "incorrect" for this issue. More like: would it be better for my scenario, or not?
People choose to bind models against view directly based on their requirements, and how they feel. Sometimes I like to simplify my models, and wrap them into "PersonViewModel", in order to expose more relevant properties, and not pollute the Model.
If that doesn't suit you, you can download ReSharper(takes care of "trying" to keep the View & viewmodel synchronized), or alternatively you can encapsulate your model further, and create a "proxy" object, as such:
Public Class PersonViewModel
{
readonly Person _person;
Public PersonViewModel(Person person)
{
_person = person;
}
public string Name
{
get { return _person.Name; }
set
{
_person.Name = value;
RaisePropertyChanged();
}
}
which seems to be pointless, but helps to keep the view and model even more separate, in case of model entities that change often. ReSharper does take care of most cases, in my experience.

How can I (cleanly) bind a composed property to a view (MVM)?

For my question, let's assume I have a viewmodel, model and view (MVVM). The viewmodel contains a couple of properties that change (and change the view via INotifyPropertyChanged event where they are binded).
Now I want to do something like a poly-property (i.e. take together a couple of string properties and put it into one property). How can I do this properly?
For example, I have three properties: Prop1, Prop2, Prop3 and want to make one property out of these: PropNew = String.Format("{0}, {1} {2}", Prop1, Prop2, Prop3);.
A possibility is to create a new property in the viewmodel (and update this if another property changes) which is then bound to the view. But I fear this is not clean... Are there other possibilities?
THank you!
If I understand the question correctly, then you want to update the UI binding to a property on the view model that is derived from the current state of the class.
Notifying the UI in WPF is done by raising the PropertyChanged event on the interface INotifyPropertyChanged. In the code snippet below I am using the NotificationObject from the PRISM library:
public class MyViewModel: NotificationObject
{
private string _lastName;
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
RaisePropertyChanged(() => Greeting);
}
}
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
RaisePropertyChanged(() => Greeting);
}
}
public string Greeting
{
get { return string.Format("Hello {0} {1}!", _firstName, _lastName); }
}
}
Updating either of the FirstName or LastName properties will cause the UI to be notified that the Greeting property has changed, and that the data should be rebound.
Have you considered using MutiBinding and interface IMultiValueConverter?

Binding doesn't work with inherited attribute

I've a problem to use binding in a ComboBox.
<ComboBox
Margin="2"
x:Name="itemSelector"
SelectionChanged="itemSelector_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Id}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
My object is public class MyButton : MyElement and the Id attribute is set in MyElement class.
Of course Id is a public attribute: public string Id;.
When I try to access a attribute which is in the MyButton class it works but with the "Id" field I got nothing...
You can't bind to a field; you need to make Id a property instead.
Replace your field with public string Id { get; set; }
It should be property (with getter and setter), not field. Because you should notify the UI that the value of the property changed (and you should implement the INotifyPropertyChanged interface)
The code shold look like for C# 5
public string Id
{
get { return _id; }
set { SetProperty(ref _id, value); }
}
private string _id;
or for C# 4
public string Id
{
get { return _id; }
set
{
_id = value;
RaisePropertyChanged(() => Id);
}
}
private DateTime _id;
the full code you can see e.g. in this blog post (both for 4 and 5 version of C# language) http://jesseliberty.com/2012/06/28/c-5making-inotifypropertychanged-easier/
(Beware that the C# 5 requres .Net 4.5, therefore your application will not run on WinXP. C# 4 requires .Net4.0 so it has not this limitation.)

MVVM DataBinding

I've started an MVVM project and now I'm stucking with correct DataBinding.
My project has:
A UserControl whit a ViewModel as DataContext like:
public partial class TestUserControl: UserControl
{
public TestUserControl()
{
this.DataContext = new TestUserControlViewModel();
}
}
ViewModel code is (BaseViewModel class contains PropertyChangedEventHandler):
public class TestUserControlViewModel : BaseViewModel
{
public KrankenkasseControlViewModel()
{}
public IEnumerable<DataItem> GetAllData
{
get
{
IGetTheData src= new DataRepository();
return src.GetData();
}
}
}
IGetTheData is the interface to DataContext:
public interface IGetTheData
{
IEnumerable<DataItem> GetData();
}
}
and finally the DataRepository code:
public class DataRepository : IGetTheData
{
private TestProjectDataContext dax = new TestProjectDataContext();
public IEnumerable<DataItem> GetData()
{
return (from d in this.dax.TestData
select new DataItem
{
ID = d.ID,
SomeOtherData = d.SomeOtherData
});
}
}
My UserControl has a few TextBoxes, but what's the best way to bind correctly?
Thanks for your help, regards.
EDIT: Binding the data against multiple textboxes
After reading your comment, I will elaborate my example for textboxes.
First important thing is that the ViewModel will model the things in the View, so that the View gets all information it needs in the structure it needs. That means, if you have multiple textboses in the View, you will need multiple string Properties in your ViewModel, one for each textbox.
In your XAML you could have something like
<TextBox Text="{Binding ID, Mode=TwoWay}" />
<TextBox Text="{Binding SomeOtherData, Mode=TwoWay}" />
and in your ViewModel
public class TestUserControlViewModel : BaseViewModel {
private string id;
private string someOtherData;
public TestUserControlViewModel() {
DataItem firstItem = new DataRepository().GetData().First();
this.ID = firstItem.ID;
this.SomeOtherData = firstItem.SomeOtherData;
}
public string ID {
get {
return this.id;
}
set {
if (this.id == value) return;
this.id = value;
this.OnPropertyChangedEvent("ID");
}
}
public string SomeOtherData {
get {
return this.someOtherData;
}
set {
if (this.someOtherData == value) return;
this.someOtherData = value;
this.OnPropertyChangedEvent("SomeOtherData");
}
}
}
Here I assume that in your BaseViewModel there is an OnPropertyChangedEvent method to fire the corresponding event. This tells the View that the property has changed and it must update itself.
Note the Mode=TwoWay in the XAML. This means, that it doesn't matter on which side the value changes, the other side will reflect the change immediately. So if the user changes a value in a TwoWay bound TextBox, then the corresponding ViewModel property will automatically change! And also vice versa: if you change the ViewModel property programmatically, the View will refresh.
If you want to show multiple textboxes for more than one data item, then you must introduce more Properties in the ViewModel and bind them accordingly. Maybe a ListBox with a flexible number of TextBoxes inside is a solution then, like #Haspemulator already answered.
Binding the data against a collection control
In the TestUserControl I guess you have a control (like a ListView) to show the list of loaded things. So bind that control against the list in the ViewModel with
<ListView ... ItemsSource="{Binding GetAllData}" ... />
First you must understand that Binding means not "read the data and then forget the ViewModel". Instead you bind the View to the ViewModel (and its Properties) as long as the View lasts. From this point of view, AllData is a much better name than GetAllData (thanks #Malcolm O'Hare).
Now in your code, every time the View reads the AllData property, a new DataRepository is created. Because of the Binding, that is not what you want, instead you want to have one instance of DataRepository for the whole lifetime of the View, which is used to read the initial data and can later be used to update the View, if the underlying database changes (maybe with an event).
To enable such a behavior you should change the type of the AllData property to an ObservableCollection, so that the View can automatically update the list if changes occur.
public class TestUserControlViewModel : BaseViewModel
private ObservableCollection<DataItem> allData;
public TestUserControlViewModel() {
IGetTheData src = new DataRepository();
this.allData = new ObservableCollection<DataItem>(src.GetData());
}
public ObservableCollection<DataItem> AllData {
get {
return this.allData;
}
}
public void AddDataItem(DataItem item) {
this.allData.Add(item);
}
}
Now if you call AddDataItem later, the ListView will update itself automatically.
Your Property Name is bad. You should call it AllData, not GetAllData.
Since you are returning a collection, you probably should be using some sort of list control (ListBox, ListView).
In that case you'd be doing
<ListBox ItemsSource="{Binding GetAllData}" />
Guten Abend. :) As it already mentioned, since you're returning the collection, it's better to use a ListBox. The comment about having ObservableCollection as a cache is also absolutely valid. I would add that if you need to have your data editable, you should use TextBox inside the ItemTemplate:
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text={Binding SomeOtherData,Mode=TwoWay} />
</DataTemplate>
</ListBox.ItemTemplate>
In this case if user edits the text in the box, data will be updated in your data object, so that it could be saved in the database later.

Categories

Resources