I've just started taking up a course about WPF and I'm a bit confuse about some areas related to Data-Binding. I have no syntax issue, but most probably committed some newbie errors and I have couple of questions.
I've done a simple screen with 2 textboxes and I when I click a button these two items are added to a ListBox.
Reference within the Window tag of the XAML to the People class
xmlns:classes="clr-namespace:WPF_Course.Classes"
Added a Window resource
<Window.Resources>
<classes:People x:Key="people"/>
</Window.Resources>
Here's how I've declared my Listbox
<ListBox DataContext="{Binding Source={StaticResource people}}"
ItemsSource="{Binding Persons}"
x:Name="PersonListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel>
<TextBlock Text="{Binding FullName}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So, I've added a DataContext to my ListBox where I bind it to my people resource and also I add an ItemSource which looks on a property within my People .
This is my class
public class People : ObservableCollection<Person>
{
public ObservableCollection<Person> Persons { get { return persons; } set { persons = value; } }
private ObservableCollection<Person> persons = new ObservableCollection<Person>();
public People()
{
for (int i = 0; i < 1; i++)
{
// implicitly I add one item just for messing around with the constructor
Persons.Add(new Person()
{
Name = "Dummy",
LastName = "Dummy",
Age = 15
});
}
}
}
Based on what I've done so far I have the following questions :
1) What's the difference between (they have the same effect, but there's more reasoning behind it ? )
ItemsSource = "{Binding Persons}"
and
ItemsSource = "{Binding Path = Persons }"
Also by leaving the ItemSource = "{Binding}" am I actually just instantiating the a People instance, thus all my logic being treated from the constructor of that class ? I've messed around with it and it appears to do so, but I am not sure.
2) On my Peoples class I've Implemented the ObservableCollection<Person> (where Person is also a class). Initially I was doing a static addition to my list from the constructor itself and I had no properties defined within the class ( ObservableCollection<person> type of properties ) thus needing it ( the implementatiton of the interface) but now using a property do I really need it? , so my question is :
If my class's sole purpose is to load things within it's collection from the constructor only ( and not from an outside class, thus needing some sort of a property ), is it a best practice to implement my class with the ObservableCollection<myClass> or to define properties of the same type that I've done ? (for accessing from an outside class)
I am sorry for the weird questions because I know they sound somewhat silly, I'm looking from a validation because I've just started working with wpf recently.
Thanks
Edit 2 : Thank you for all your answers, I understood now. Also I've forgotten to show you how I insert data in my collection. ( Added this edit for me to remember if I forget it and for others that may stumble upon this thread having a similar confusion )
ObservableCollection<Person> ppl;
public MainWindow()
{
InitializeComponent();
person = new Person();
stackPanelPerson.DataContext = person;
people = new People();
ppl = people.Persons;
PersonListBox.ItemsSource = ppl;
}
Initially I was doing like this
ppl.Add(new Person() { Name = boxFirstName.Text, LastName = boxLastName.Text, Age = Int32.Parse(boxAge.Text) });
Then I realized I was using data-binding on my Person Class(INotifyPropertyChanged) with properties so I changed it to :
ppl.Add(new Person() { Name = person.Name, LastName = person.LastName, Age = person.Age});
Thanks again guys for the replies !!
Have a good day !
Question 1:
None, there is no difference. {Binding xyz} is the same as {Binding Path=xyz}, it's almost like a shortcut. But it can only be used on the first thing you write in your binding, for example, you cannot do this:
{Binding ElementName=myElement, xyz}
Instead, you would do this:
{Binding ElementName=myElement, Path=xyz}
Or even better:
{Binding xyz, ElementName=myElement}
Here's a related question.
Question 2:
What you have done is correct, your collection of people should be exposed as a Property, why? Because then you can bind to it.
There is no need for a static property in this scenario.
I would strongly suggest researching the MVVM Design Pattern. You can find a tutorial here
1) Many markup extensions understand shortened syntax, there is no difference between {Binding Persons} and {Binding Path=Persons}. However, sometimes you must use full syntax.
One example would be to make own
public class ExceptionBinding : Binding
{
public ExceptionBinding()
{
ValidationRules.Add(new ExceptionValidationRule());
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
}
}
then you must use full syntax {l:ExceptionBinding Path=Persons}.
2) It depends. You don't have to use ObservableCollection<> if collection is not going to change after you bind to it. Creating List<>, filling it up and then binding to it will work pretty well.
You have to read about MVVM, because using it will simplify usage scenarios and makes many things more clear.
Mike covered what I wanted to say ...
In addition to the binding, you can also show different things in your bindings. Here's a tutorial I've wrote for the code project: Understanding SelectedValue, SelectedValuePath, SelectedItem & DisplayMemberPath + Demo
You can do mockups of your class with dummy data so you'll see a preview in your XAML designer in VS. MVVM light framework helps out and has some cool features as well. There are other frameworks, and you don't really need one for doing MVVM, but they help.
Other than that, I wish you good luck on your journey ... :) once you'll master it, it'll be fun ...
None, there is no difference. {Binding propertyName} is the same as {Binding Path=propertyName}, it's almost like a shortcut, but Constructor get called because of DataContext="{Binding Source={StaticResource people}}".
It depends. You don't have to use ObservableCollection<> if collection is not going to change after you bind to it. Creating List<>, filling it up and then binding to it will work pretty well.But if you want to change collection from screen and update list then you need to go for ObservableCollection<>
Related
I am currently working on a just for fun solution for myself, to create workout plans for the gym.
Solution on Github
It is a simple WPF solution using Caliburn.Micro to easier get the connections for the MVVM pattern.
On my MainView I have a TabControl:
MainView.xaml:
<TabControl x:Name="Items" />
With the following code for the ViewModel:
public MainViewModel()
{
DisplayName = "Plan2Fit";
Items.Add(new CreatePlanViewModel(_exerciseProviderViewModel));
Items.Add(new ExerciseManagementViewModel(_exerciseProviderViewModel));
}
I only have two ViewModels displayed in the TabControl, one to manage exercises and store them in xml to have kind of a database of your exercises and one where you should later be able to pick exercises for your plan.
Problem:
At startup everything looks ok, but as soon as I switch between the Tabs, one of them might loose its child control for whatever reason.
I have already tried the following:
MainView.xaml:
<TabControl x:Name="Items" cal:Message.Attach="[Event SelectionChanged] = [Action Reload]" />
MainViewModel:
public void Reload()
{
_exerciseProviderViewModel = new ExerciseProviderViewModel();
Items.Refresh();
DisplayName = "Plan2Fit";
}
This makes the error happen less often but it still is existing.
I have already found this question ... the solutions I was able to find are all working with MVVM, but not with Caliburn.Micro, so I am really not havinmg any Idea, how to solve this.
I have tried Avalon dock, but I was not able to get it to work with the Caliburn way of binding x:Name="Items"
Note:
If you want to recreate the Bug using my solution, you have to add some "Exercises" by picking an image in the "Manage Exercise" tab and click Add (You can add the same "Exercise" multiple times).
There is no errorhandling or testing done so far, as this is at the state where I want to validate, if it works at all.
I found the problem, still I dont understand, why it is a problem at all.
Given:
public MainViewModel()
{
DisplayName = "Plan2Fit";
Items.Add(new CreatePlanViewModel(_exerciseProviderViewModel));
Items.Add(new ExerciseManagementViewModel(_exerciseProviderViewModel));
}
The tabcontrol will occasionally loose its childs.
If I pass a new ExerciseProviderViewModel into eacht Items.Add() call, the bug does not occur.
Thus I stored a ExerciseProvider as member and passed this one into my ViewModels I want to add.
public MainViewModel()
{
_exerciseProvider = new ExerciseProvider(new DCSerializer<List<Exercise>>());
DisplayName = "Plan2Fit";
ActivateItem(new CreatePlanViewModel(new ExerciseProviderViewModel(_exerciseProvider)));
ActivateItem(new ExerciseManagementViewModel(new ExerciseProviderViewModel(_exerciseProvider)));
}
This works without any problems.
I'm currently trying to figure out how to use the x:Bind stuff in UWP/xaml.
I'm learning more everyday and the app I'm curently writing is getting way easier to manage since I can implement the MVVM patterns now.
But there is one thing I encountered now...
I have a List<T> which I want to display in my UI. How can I bin this list while using x:Bind?
Or do I have to convert it into something else first?
Best Regards,
Daniel
You can certainly use a List<T> for binding, but usually ObservableCollection<T> is preferable, because it also allows the UI to observe list changes, as opposed to List<T> that will not update after bound first. You can create an ObservableCollection from List using the constructor:
ObservableCollection<T> data = new ObservableCollection<T>( list );
In any case, you first have to create a property (but fields are also supported with x:Bind) in your view model:
public ObservableCollection<T> Data { get; } = new ObservableCollection<T>();
Remember that binding connects to the instance, so if you would set a new instance to Data property, it the binding would not update. For that to work you need to implement INotifyPropertyChanged interface on your view mdoel and raise PropertyChanged event in the setter.
Now, to display the items in your UI you need a list control like ListView or GridView and bind it to your collection:
<ListView ItemsSource="{x:Bind Data, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate>
<!-- your template -->
<TextBlock Text="{Binding SomePropertyOfT}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You can find a thorough walk through data binding in documentation as well.
If your desire is to have a collection which might change over time, and you want the UI to be notified of such modifications, you should not utilize List<T>!
Use instead ObservableCollection<T>, which implements the INotifyPropertyChanged and INotifyCollectionChanged which is the heart of the MVVM, allowing the ViewModel/View to communicate between each other.
Such collection automatically handles the addiction/removal of elements automatically for you!
Anyway, here is a simple example, showing how you can use a List to communicate with your View.
MainPage.xaml.cs
public MainPage()
{
this.InitializeComponent();
RandomList = new List<string>() {
"random 1", "random 2", "random 3"
};
}
List<string> RandomList { get; set; }
MainPage.xaml:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ComboBox ItemsSource="{x:Bind RandomList}"
PlaceholderText="List of Random things"/>
</Grid>
Here we defined a {x:Bind } to populate the ItemsSource's dependency property, which accepts a Collection.
This Binding is defined with the default binding Mode, which is OneTime for compiled bindings. For instance, if you were to perform Bindings with {Binding } markup, the default mode is OneWay;
Since you created a Collection with List rather than ObservableCollection, there would be no reason to specify any other mode than the default, since you haven't implement a mechanism for your CLR collection to actually notify the View of an update.
First a little backstory: I am studying Xamarin for a month now, and I am about to start my first project.
I have a need where I have like 4 nested "generations" of a relational database, that I have to include in one View.
When I start to nest stuff, I am forced to move some Commands (ViewModel code) into the Model.
I want to avoid this at all cost, thus the question arise:
Is it MVVM friendly to use other ViewModels as bindable properties, like in the following Example?
ViewModels:
public class MainViewModel : FreshBasePageModel
{
public ObservableCollection<OtherViewModel> OtherCollection { get; set; }
}
public class OtherViewModel: FreshBasePageModel
{
public Command SomeCommand { get; set; }
}
And use it like this in the Views:
<ContentPage>
<ListView ItemsSource={Binding OtherCollection} SelectedItem={Binding SomeCommand}>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
This approach seems Okay to me, but this is my first ever MVVM project, and I wonder if this is how you do stuff.
I use FreshMvvm as the backing framework, and it uses conventions for bindings, so the view is automatically bound to its namesake partner.
Also, if you'd like to look at my nested lists, see below:
Thanks for your time,
By default this behaviour isn't supported because a control only has a DataContext o BindableContex and this class is where the compiler look for your binding properties and commands.
In MVVM, you should only use a ViewModel for each View so your approach isn't frequent.
However I think that you could use the code that you proposed if you use the Path binding property and you have other control which its BindableContex is OtherViewModel
I'm curious how this works, because I have a MainViewModel, which has Property say called SubViewModel which has a Property of ObservableCollection (we'll call it Property1.)
I've implemented INotifyChangedProperty on everything.
My Main Window
<Window ..
DataContext="{Binding MainViewModel}" />
...
<StackPanel DataContext="{Binding SubViewModel}">
<local:SomeControl DataContext="{Binding}" />
</StackPanel>
</Window>
And my UserControl
<UserControl Name="SomeControl">
<DataGrid Name="MyDataGrid" ItemSource="{Binding Property1, Mode=TwoWay}" CurrentCellChanged="TestMethod" />
...
</UserControl>
In my test method, just as a test to figure out why the changes are not propegating up to the main view model I do something like this
private void TestMethod()
{
var vm = this.DataContext as SubViewModel;
var itemSourceObservableCollection = MyDataGrid.ItemsSource as ObservableCollection<MyType>;
//I thought vm.Property1 would be equal to itemSourceObservableCollection
//but they are not, itemSourceObservableCollection shows the changes I've made
//vm.Property1 has not reflected any changes made, even though I though they were the same item
}
So I figured out that ItemSource must create a copy of the item you bind it to? I'm stuck here, how do manually notify the viewModel that this property has changed and it needs to update? I thought that was INotifyPropertyChanged's job?
I think part of my problem is I lack the understanding of how this kinda works internally. If anyone can point to a good blog post, or documentation to help me understand why my code isn't working the way I expected, that would be great.
1) No copy is made.
2) ObservableCollection will propogate changes made to the collection, not the items within the collection. So you'll see additions, deletions etc. but NOT property changes to items within the collection.
3) If you want to see changes made to individual items in the ObservableCollection, you need to implement INotifyPropertyChanged on those items.
There's actually TWO different issues here. What happens internally when you bind to a collection? AND why changes on the user surface are not propagated back to your View Model. Based upon what you wrote, the two issues are not connected, but let's take them one at a time...
For the first issue... When you bind a collection, the WPF binding engine creates a "CollectionView" class that mediates between your object store and the logical tree. You can, if needed, get a copy of the the "CollectionView" using a static method on CollectionViewSource...
var cvs = CollectionViewSource.GetDefaultView(MyCollectionOfThings);
There are several interesting properties in the result, and some of them contain write accessors which allow you to directory modify the CollectionView.
For the second issue... The business classes in your SubViewModel need to inherit from INotifyPropertyChanged such that changes are 'announced' via the WPF binding engine. Your VM should be a publisher, but can also be a subscriber. A property that participates in the INotifyPropertyChanged plumbing gets declared like this...
private string _name;
[Description("Name of the driver")]
public string Name
{
[DebuggerStepThrough]
get { return _name; }
[DebuggerStepThrough]
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
This code publishes changes, but can also subscribe to changes made on the user surface by setting the appropriate attributes in your Xaml.
Background reading: What is a CollectionView?
Also, Similar question
I have little problem with databinding in my current project.
I have an ObservableCollection I want to bind to an ListBox.
public ObservableCollection<GeoDataContainer> geoList = new ObservableCollection<GeoDataContainer>();
...later...
geoListBox.ItemsSource = geoList;
This code works fine. The Listbox has a datatemplate and everything looks perfect.
But I don't want to use C# code for binding. I want to make the binding in the XAML Code.
I am searching for days but I don't get it. These are two lines C# code but to archive this in XAML it seems impossible without creating my own class for my collection or adding a DataProvider or resources or whatever.
Is there no easy way to do it?
All you have to do is expose the collection and bind to it. For example, if you expose it as:
public ICollection<GeoDataContainer> GeoList
{
get { return geoList; }
}
You will be able to bind to it as:
<ListBox ItemsSource="{Binding GeoList}"/>
The "trick" is to make sure that the DataContext of the ListBox is the class that exposes the GeoList property.
Another good way would be instantiating geoList as a resource
<WindowResources>
<l:GeoCollection x:Key="geoList"/>
</WindowResources>
Then you have
GeoCollection geoList = FindResource("geoList") as GeoCollection;
Of course, this is for cases when the data is related to the view only. If this is related to model or modelview, you use DataContext and bind to its properties.
Kent suggestion is the way to go...
On a further note, if you do not wish to set your DataContext to the list, you can also retrieve the property with an another form of binding:
Make sure your root control has a name, i.e. "Root"
{Binding ElementName=Root, Path=GeoList}