I'm having an issue with C# using WPF.
Just being brief here.
The following code below collects names via Entity Framework into a list.
This is in my MainWindow.xaml.cs file.
public ObservableCollection<string> FruitInfo
{
get
{
using (var context = new Fruit())
{
ObservableCollection<string> fruits= new ObservableCollection<string>();
foreach (var item in context.Fruits.OrderBy(s => s.FruitName))
{
fruits.Add(item.FruitName);
}
return fruits;
}
}
}
In my MainWindow.xaml file, I have the following:
<GroupBox Grid.Row="0" Grid.Column="0" Margin="5" Header="Fruit Info" >
<ComboBox Margin="5" SelectedItem="{Binding FruitInfo}"/>
</GroupBox>
When running my project, I see that the Combo Box does not populate the fruits.
Any ideas why I'm not seeing this?
All thoughts appreciated
You should bind the ItemsSource of the ComboBox to your collection, and the SelectedItem to another string that will represent the user's selection.
First:
<GroupBox Grid.Row="0" Grid.Column="0" Margin="5" Header="Fruit Info" >
<ComboBox Margin="5" ItemsSource="{Binding FruitInfo}" SelectedItem="{Binding SelectedFruit}"/>
</GroupBox>
Second: Make a SelectedFruit in your ViewModel
public string SelectedFruit { get; set; }
Ok, I understand what your trying to do, even though I'm still not sure why you're trying to do it.
The idea of using using is that it creates the variable for you, and the disposes of it when you finish the block of code you're running.
Now, you're creating a variable in that block, and return it ... and then, the system tries to dispose of it. So your return collection must be implicitly convertible to System.IDisposable, which I doubt yours is.
Putting that aside, you should follow emedbo advice. You will bind the source to the collection, and have another property for the selected index (since you're using mvvm).
I wouldn't get the data like that inside a using inside a getter, since it feels like that data you're getting might be deleted, and if it's not, then the whole use of your using is a bit wrong.
Not to mention it's not very readable, and you should aim for readability in most cases.
I don't use the Entity Framework, but I think the pattern for the FruitInfo property is missing of an important piece.
The problem is that the binding mechanism does not realize about the new ObservableCollection, because it expect some "notification" way to be alerted. That is, you have several ways to solve your problem:
use a DependencyPropety instead of an ordinary property: every time you set the property the bound controls are also notified.
I'd recommend this solution: reliable and versatile.
implement the INotifyPropertyChanged interface in the class exposing the FruitInfo property (e.g. MainWindow), then fire a PropertyChanged event on any actual FruitInfo's value changing.
This way is also valuable, but it looks useless adding a thing already exposed in any DependencyObject-derived class. The INotifyPropertyChanged fits perfectly for the POCO classes (Plain-Old CLR-Objects).
give a name to the combobox, then set the ItemsSource property explicitly.
It works fine, but you'd lose the benefits of the data-context inheritance, especially within templates.
the pattern you used creates the collection in a "lazy" fashion: consider avoiding the lazy-way, and set the FruitInfo value before the combobox is created/bound.
Doable, but typically may be applied in a few cases. Also requires that you know for sure the sequence of the objects creation. Keep as latest way.
== UPDATE
Try to modify your code as follows:
private ObservableCollection<string> _fruits = new ObservableCollection<string>();
public ObservableCollection<string> FruitInfo
{
get
{
using (var context = new Fruit())
{
this._fruits.Clear();
foreach (var item in context.Fruits.OrderBy(s => s.FruitName))
{
this._fruits.Add(item.FruitName);
}
return this._fruits;
}
}
}
Related
Im trying to learn MVVM, but I'm having some trouble. Im new to xaml and c#.
What I have so far:
A person class, that define a person object: name, age and other info
A model class people, owns a private linkedlist (list of person), which also contains methods such as get, remove, add and do some calculations
a viewmodel class, doing casting/parsing/transforming between xaml behind code and the model.
A xaml behind code file mainWindow.xaml.cs, that listen to button click and such, and invoke methods from the viewModel class, and do some simple binding such as total.Content = objModelView.getTotal().
I didnt use INotifyPropertyChanged ObservableCollection, still trying to wrap my head around it. While my program does what I want I'm not sure how to structure it better.
Basically I have 2 main questions:
I see examples online where people store/initiate the list of items in viewmodel, shouldn't I keep the list in model instead, that should be where all the data be stored right?
Let's say I'm suppose to display all the items (in the list of the model class) onto a dataGrid. Right now in my program: mainWindow.xaml.cs will detect the button click, then it ask viewModel to store it in model, if no error then xaml behind code will do
people_dataGrid.Items.Add(new person { name = newName, age = newAge, address = newAdd }); Is this bad practice? Dont get how to use ObservableCollection here, can it somehow detect a change in the list of my model class, and then remove and add the rows to the datagrid?
I've been reading for the whole day but I'm struck here, hope I can get some direction
The model stores data, the view display it, the viewmodel is the bridge between the two.
That doesn't mean that the view have a direct access to model data because you don't always need to display all data in model layer. So we have a viewmodel layer that makes only useful information accessible.
The viewmodel is very useful when you want to display the same data multiple times but displayed differently: you don't have to replicate data, you only need to define twice what information you need from those data and how to display them.
What you're doing in your second question is using model in view : This is not MVVM. What you want to do is bind the ItemsSource DP of Datagrid to a list of PersonVM which will fetch information from Person.
You code could be structured like that:
public class Person {
public String Name {get; set;}
public int Age {get; set;}
}
public class PersonVM {
public PersonVM(Person model) {
_model = model;
}
private readonly Person _model;
internal Person Model {get {return _model;}}
public String Name {
get { return _model.Name; }
set { _model.Name = value; }
}
public int Age {
get {return _model.Age;}
set { _model.Name = value; }
}
}
//PersonV.xaml
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
public class People : ObservableCollection<Person> {
}
public class PeopleVM : ObservableCollection<PersonVM> {
public PeopleVM(People model) {
_model = model;
foreach(Person p in _model) {
Add(new PersonVM(p));
}
_model.CollectionChanged += CollectionChangedHandler;
}
private void CollectionChangedHandler(Object sender, NotifyCollectionChangedEventArgs args) {
switch (notifyCollectionChangedEventArgs.Action) {
case NotifyCollectionChangedAction.Add:
foreach(People p in args.NewItems) {
if(!this.Any(pvm => pvm.Model == p)) {
this.Add(new PersonVM(p));
}
}
break;
case NotifyCollectionChangedAction.Remove:
foreach(People p in args.OldItems) {
PersonVM pvm = this.FirstOrDefault(pe => pe.Model == p);
if(pvm != null) this.Remove(pvm);
}
break;
case NotifyCollectionChangedAction.Reset:
Clear();
break;
default:
break;
}
}
private readonly People _model;
}
//PeopleV.xaml
<ItemsControl ItemsSource={Binding}>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type PersonVM}">
<PersonV/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
public class AppVM {
public AppVM() {
People p = ServiceLayer.LoadPeople(); //load people
People = new PeopleVM(p);
}
public PeopleVM People {get; set;};
}
//MainWindow.xaml
<Window ...
>
<Window.DataContext>
<local:AppVM/>
</Window.DataContext>
<PeopleV/>
</Window>
Answer to your post can be as long as one wishes to explain, perhaps a whole lengthy blog itself. I will try to just answer 2 of your specific questions here. I am not going to show the code for each sub-answer, you have to take it as home work. :)
I didnt use INotifyPropertyChanged ObservableCollection, still trying
to wrap my head around it. While my program does what I want I'm not
sure how to structure it better.
Why? If you don't use these magic sticks, it's better you write a WinForms app and not a WPF one. Forget everything and dive into these two. You must (no escape) understand and use them in MVVM/WPF. You can even defer reading my further answer for that matter.
I see examples online where people store/initiate the list of items in
viewmodel, shouldn't I keep the list in model instead, that should be
where all the data be stored right?
They are not wrong. Person class in model layer represents a real world entity and is must, however, I would not bother about having People class in model. It's just a collection that could easily be accommodated by ViewModel. I personally would prefer that way always.
Let's say I'm suppose to display all the items (in the list of the
model class) onto a dataGrid. Right now in my program:
mainWindow.xaml.cs will detect the button click, then it ask viewModel
to store it in model, if no error then xaml behind code will do
people_dataGrid.Items.Add(new person { name = newName, age = newAge,
address = newAdd }); Is this bad practice? Dont get how to use
ObservableCollection here, can it somehow detect a change in the list
of my model class, and then remove and add the rows to the datagrid?
That's not MVVM, trust me. At the maximum what you should be required to write in view code behind, is initializing view model and setting it as view's data context.
To handle view events (Button.Click for ex) you should use ICommand implementation that will be bound to Button.Command property in XAML. This way you decouple control's event handler from the code behind.
You need to have a ObservableCollection<Person> in your viewmodel which will be bound the DataGrid in view. So when click a button to add person, button's command object will update this collection and view will be refreshed automatically without having you to add it manually to data grid.
You aren't using MVVM at all. It sounds like you are using MVP, which is a completely different pattern.
Before you continue, you need to understand what MVVM was designed for, because its a highly complicated (seemlying over engineered pattern) with a huge number of abstractions just to write the ubiquitous To-Do list.
But you must do all of it, otherwise its not MVVM.
The Zen of MVVM
MVVM grew out of the realisation that writing good, bug-free, safe UI code is hard. Testing UI code is harder, and involves hiring human testers, that are slow and can get it wrong.
So the solution that they came up with was simple.
DON'T WRITE ANY CODE IN YOUR UI
Done.
Except, not. Now, your UI doesn't do anything, it just looks pretty. So they added an extra layer between the UI and the Program/Business Logic/Model, and they called it the ViewModel.
The job of the ViewModel was to tell the UI what to do. But the trick was to get it to tell the UI what to do, without the ViewModel knowing about the UI at all.
(MVP has a similar concept, but the Presenter DOES know about the UI)
By having the ViewModel not know about the UI at all, we can write clean code that can easily be debugged and tested using our usual bag of tricks. Such as unit testing, refactoring, static code analysis, dependency injection etc etc...
Times are good!
Except the View Model still doesn't know about the UI. So we know what the UI should look like, but the UI doesn't know, because no one is telling it...
So they added the Binding class. The binding class's job is to watch the ViewModel, and then update the UI whenever something changes in the ViewModel.
In the world of MVVM there have been two different approaches to how the Binding class works.
You have the WPF way of doing things, which is to implement an event that tells the Binding class that the ViewModel has been updated. This is fast and flexible, but really annoying to write.
And you have the AngularJS way of doing things, which is to poll the ViewModel for updates. This is ridiculously slow and buggy.
If you have been following me thus far, you will note that MVVM defines a flow of data from your Model to your View. A break in any part of this chain will make it "not work".
It all so COMPLICATED, why bother?
The only reason I've found that justifies MVVM's excessive complexity is that you can write a GUI which you can have 90% test coverage, as the view only covers a tiny part of your program.
If you think automated testing is overrated, then you shouldn't use MVVM.
I am also pretty new to WPF, C# and MVVM. I have read quite a fair bit for these two to three months, so maybe I'll share what I understood.
You seem to have the same question that I had a week or two ago. Data should not be stored in the model. Models are just data structures. Databases (or simulated alternatives like Lists) are the actual storages that store these data. Some people would simply keep them in ViewModel, while some would move them to something outside of MVVM (e.g. a "Repository" class).
You are doing it all wrong. In MVVM, Views do not interact with ViewModels in this manner - they interact via Commands and Bindings. The fact that your View is directly manipulating the list means that it's definitely wrong.
Example:
View (Window):
<Window.Resources>
<vm:MyViewModel x:Key="MyVM" />
</Window.Resources>
<Window.DataContext>
<StaticResourceExtension ResourceKey="MyVM" />
</Window.DataContext>
<DataGrid ItemsSource="{Binding PeopleList}" ..... >
<Button Command="{Binding StoreCommand}" .... >
ViewModel:
public static readonly DependencyProperty PeopleListProperty =
DependencyProperty.Register("PeopleList",
typeof(ObservableCollection<Person>),
typeof(ViewModel));
public ObservableCollection<Person> PeopleList
{
get
{
return GetValue(PeopleListProperty) as ObservableCollection<EmissionEntry>;
}
set
{
SetValue(PeopleListProperty, value);
}
}
private ICommand _storeCommand;
public ICommand StoreCommand
{
get
{
if (_storeCommand == null)
_storeCommand = new MyCommandImplementation();
return _storeCommand;
}
}
Person is your model class with name/age etc. The list is kept in ViewModel, unless you want to have a repository somewhere.
You probably haven't really read anything about ICommand, so I suggest reading it up first. It is too long to give a tutorial here, but you can ask questions after you have read up some.
Hello good people of stackoverflow.
EDIT: Sourcecode is avaliable on:
https://www.dropbox.com/sh/yq4qbznl4b6gm4h/AADdjd_hb-OQXV5KL8OU5cbqa?dl=0
More specefic on I4PRJ4 --> Backend --> Backend.sln.
I'm currently making a productmanagementsystem that has a GUI. We've decided to use MVVM, and we're still learning it.
Though I have a problem. On the main screen, a list of categories is shown, and the products in the selected category is also shown.
As for now, we've binded the data to an observeable collection. But the problem arises when we need to add another product, using a differen view and viewmodel. In that case we need to have Categories with data. We open the add-product-view through a command in the mainwindow, so to get the data to the viewmodel we have to pass the object from MainWindowViewModel to AddProductView and then to AddProductViewModel - and that's not the coupling I want.
So I tried using the singletonpattern, and binding to the observable collections as:
xmlns:models="clr-namespace:Backend.Models"
..
..
<ListBox Margin="0, 30, 0, 0" ItemsSource="{Binding Source={x:Static models:GlobalCategories.CategoryList}, Path=Name}" DisplayMemberPath="Name"/>
Where GlobalCategories is as follows:
[Models: GlobalCategories]
public class GlobalCategories
{
private static BackendProductCategoryList _list;
public static BackendProductCategoryList CategoryList
{
get
{
if (_list == null)
{
MessageBox.Show("Made new list");
return new BackendProductCategoryList();
}
MessageBox.Show("Returned old list");
return _list;
}
set { _list = value; }
}
}
As you can see in the code, a messagesbox appears, telling me what was returned. But with the above XAML-code, it actually creates that object, which was my understanding it wouldn't do that and you therefore would have to initialize it yourself. It actually creates the object, and the msgbox will say that a new list has been made.
Though if I then do the following in MainWindowViewModel
public class MainWindowViewModel
{
public MainWindowViewModel()
{
MessageBox.Show("" + CategoryList.Count);
}
Then it creates ANOTHER list, but if I then do another operation, I get a "old list" message. What is going on - why is this happening?
What am I doing wrong? Oh - and the binding doesn't work, nothing is shown when I do this. And it's driving me nuts. I'm comming from a background in C and C++, and been working with c# and xaml for a couple of months - I NEED CONTROL. AND POINTERS :-D
I really hope you guys can help me out here, giving me an understanding what is going on, and how I solve it.
Or even better - is there a better way of sharing data between viewmodels? Because to be honest, then I'm not the biggest fan of singleton, and would really appericiate another soloution to share data between viewmodels.
Thank you so much for your help!
Best regards,
Benjamin
I think there may be some confusion on your end as to how property accessors work. Set and get simply allow you to declare members that look like regular properties to everything referencing them but are implemented with code. When something needs to access the list it calls your getter and is expecting that function to return a value (or null). Your current implementation is creating a new list and returning it:
return new BackendProductCategoryList();
But you're not setting the value in _list, so the next time the getter is called the value of _list is still null and you create and return the list again. And again, and so on. All you need to do is store it so that the list only gets created once:
public static BackendProductCategoryList CategoryList
{
get
{
if (_list == null)
{
MessageBox.Show("Made new list");
_list = new BackendProductCategoryList();
}
else
MessageBox.Show("Returned old list");
return _list;
}
set { _list = value; }
}
One additional tip: don't call MessageBox.Show in accessors, you should be doing as little work in accessors as possible. Using statics like this really isn't a good idea either for a host of reasons, but I'll leave that for another question (look up "dependency injection").
I'd like to be able to display an index value from within a DataTemplate, but I don't want the data to be persisted or backed by the model or viewmodel. In other words, if the order of the items in the OC changes, I don't want to have to recalculate the indexes. The value should be intrinsically tied to the underlying index in the OC. It is okay if the index is 0-based (in fact, I'd expect it).
One method that others have used is the AlternationIndex AP, but this has its own pitfalls for certain situations.
One last thought: I can't help but think that a converter is going to be helpful in a final solution.
I would use a converter to do this.
The trick is giving it the source collection, either on the ConverterParameter or a Dependency Property. At that point, conversion is as simple as using IndexOf.
Here's a sample converter that does this:
public class ItemToIndexConverter : IValueConverter
{
public object Convert(...)
{
CollectionViewSource itemSource = parameter as CollectionViewSource;
IEnumerable<object> items = itemSource.Source as IEnumerable<object>;
return items.IndexOf(value as object);
}
public object ConvertBack(...)
{
return Binding.DoNothing;
}
}
You can make the implementation strongly typed, return a formatted string as a number, etc. The basic pattern will be as above though.
This implementation uses the parameter approach, as making a DP is more messy in my view. Because you can't bind ConverterParameter, I have it set to a static resource that is bound to the collection:
<CollectionViewSource x:Key="collectionSource" Source="{Binding Path=MyCollection}" />
...
<TextBlock Text="{Binding Converter={StaticResource ResourceKey=ItemToIndexConverter},
ConverterParameter={StaticResource ResourceKey=collectionSource}}"/>
I have a Dictionary, which contains data like so:
key Value
UK01 Building 1
UK02 Building 2
I have a textbox in which data is populated like this:
foreach (var building in dictionary)
{
listbox1.Items.Add(building.Value);
}
This then displays Building 1 Building 2, ...
Problem:
What I want to do is so that when the SelectionChanged is triggered, I can access the Key/Value for the option that has been selected and store these as variables which I can use later on. Currently I can only get the item selected, i.e "Building 1" or I can get the SelectedIndedbut this only gives me: 1, 2, .. And I understand why.
Is it therefore possible (elegantly) so that the key/value can be accessed without displaying the key? I have tried to use a class:
public class Test
{
public ID { get; set; }
public value { get; set; }
}
But this did not work. Does anyone have any suggestions?
Thank you :)
You need to use a DataTemplate in your ListBox to ensure the listbox knows how to render it's content. Assuming you follow your class based approach as above:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding value}">
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This creates a DataTemplate for each item you have added to the ListBox which contains a text block which is bound to a property on the items added
Read up on binding - it is one of the most powerful things in WPF/WP7/Silverlight and probably one of the most extensive implementations of binding out there
http://www.mono-software.com/blog/post/Mono/166/Data-binding-in-Windows-Phone-7-application/
Edit:
Though I'm pretty sure the default template for ListBox is just to call 'ToString()' on each item... so it should be working anyway!
Oh and consider making the members auto-properties instead of fields:
public string ID { get; set; }
Edit:
Ok I'm not great with Linq but since the query should return an IEnumerable<KeyValuePair<string, string>> as far as I can see, you can just enumerate it and build your objects off it e.g.
var myList = new ObservableCollection<MyClass>();
foreach(var kvp in dict)
{
myList.Add(new MyClass(kvp.Key, kvp.Value));
// Or myList.Add(new MyClass() { ID = kvp.Key, Value = kvp.Value }); depending on your constructor
}
Having said this, there no reason why you can't just point to the key/value of the dictionary items (KeyValuePair<string, string> is just a reference type like anything else)
This:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Key}">
<TextBlock Text="{Binding Value}">
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Should give you
{Key}
{Value}
{Key}
{Value}
{Key}
{Value}
In your listbox
Assuming you don't need any additional functionality from the result items KeyValuePair should suffice. Creating a class just to show some values may be overkill since you are already 99% of the way there.
You might want to look at some MVVM patterns and maybe get into looking at an MVVM framework (I'm biased because I use it all the time but Caliburn.Micro is really easy to get started with and supports WP7).
Your code wouldn't be any more complex, but certain things would be wired up for you and it would give you more feedback as to where you are going wrong with bindings etc (some bindings are hard to figure out especially when popups/contextmenus are involved)
If you are serious about developing for XAML based technologies, they go hand in hand with the MVVM pattern, and a framework just makes things a cinch
If you are interested there's an easy to follow tut here:
https://caliburnmicro.codeplex.com/wikipage?title=Basic%20Configuration%2c%20Actions%20and%20Conventions&referringTitle=Documentation
Another variation on the same tut with a bit more info is here:
http://buksbaum.us/2010/08/01/caliburn-micro-hello-world/
I think you only need to override the ToString function in your Test class and it should work as you want it to
like this:
public override string ToString()
{
return Value;
}
I know WPF and somewhat it should be similar.
There you can bind a control to a collection with something called data binding.
http://msdn.microsoft.com/en-us/magazine/cc163299.aspx
http://blogs.msdn.com/b/wriju/archive/2011/07/25/windows-phone-7-binding-data-to-listbox-through-code.aspx
Our application is in use of several TreeViewItem and DataGrid controls in XAML whose ItemsSource properties are bound to Entity Framework ObjectSet<Entity> collections. The problem is that the UI controls are behaving as if the ObjectSets are empty.
The EF ObjectContext is enclosed within a singleton class in a static class:
public static class BusinessData
{
public static readonly BizDataSource Source = BizDataSource.Instance;
}
public class BizDataSource : INotifyPropertyChanged
{
private BusinessEntitiesObjectContext _context = ...;
and the ObjectSets are being returned from readonly properties within the singleton:
public IEnumerable<Employee> RetiredEmployees
{
get {
return (from it in _context.Employees where it.Status == "Retired" select it);
}
}
and finally, the ItemsSource is data bound to the collection, with INotifyPropertyChanged serving to update the UI when its known that the data source is updated:
<TreeViewItem x:Name="PART_TVI" Header="Retired Employees"
ItemsSource="{Binding
Source={x:Static local:BusinessData.Source},
Path=RetiredEmployees}"
/>
Debug stepping has revealed that the binding is correctly providing an IEnumerable which resolves to the business objects, but it seems like the control isn't iterating upon it. For example, if I add this code to the window:
PART_TVI.ItemsSource = BusinessData.Source.RetiredEmployees;
The same behavior occcurs as with XAML Binding: nothing. However:
PART_TVI.ItemsSource = BusinessData.Source.RetiredEmployees.ToArray();
Ah hah! Now, we have generated content in our TreeViewItem. But, why was this necessary in the first place?
My guess is that: in the case of IEnumerable, you pass the expression to the ItemsSource, NOT a SOURCE. When you called: ToArray() which means that, it will execute the expression and return the actual data for the ItemsSource.
In summary, the first case: You tell the WPF how to get the ItemsSource. The second case: You pass the actual data source.