Binding ObservableCollection to ListView - c#

I've been following this tutorial to try to work an understanding of XML, WPF, and C# (coming out from Fortran). I've gotten the functionality working (thanks to the tutorial), but I'm having troubles modifying it to work with WPF instead of WinForms.
Essentially, when I click the "Get RSS" button, the following is happening:
RssManager reader = new RSSManager();
ObservableCollection<Rss.Items> _list = new ObservableCollection<Rss.Items>();
reader.Url = txtFeed.Text;
reader.GetFeed();
_list = (ObservableCollection<Rss.Items>)reader.RssItems;
The listview just sits blank. It's code is like the following. Also, trying this with a listbox results in the name of the class being populated for each item in the list instead of my data:
<ListView ItemsSource="_rssItems">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Title}"/>
</GridView>
</ListView.View>
</ListView>
Would it be possible to do something like (again, forgive me for my ignorance, here):
_list.Add( new Rss.Items());
The list (_list) contains all of the information that I need, I just want to figure out how to properly bind it (or add it) to the ListView.

It looks like you are a bit lost.
Ultimately you want to bind your view(WPF form) to a View-Model and your View-Model to a model (the RSSManager).
For now lets bind the view directly to the model.
In your constructor you make a new instance of the model and you assign it to the data context.
This model is going to live as long as the form -
public MainWindow()
{
InitializeComponent();
_model = new RssManager();
DataContext = _model;
}
Then in your XAML you bind the item source to your collection property :
<ListView ItemsSource="{Binding Path=RssItems}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Title}"/>
</GridView>
</ListView.View>
</ListView>
Note that in "Path=RssItems" is relative to whatever you assigned to the DataContext.
Then in your refresh button logic you call:
_model.Url = txtFeed.Text;
_model.GetFeed();
What you ultimately want to do is put another layer in the middle. This is the view model. The View model is as you may have guessed a model of the view.
The view model's job is to collect information about the state of the view and to expose the data from the model that is to be presented by the view. It also can hold current ui state information - f.e. which row in the table is selected, since some command may act on that later. In essence it allows to abstract all the logic of the view from your code. Your commands operate on things like which row is selected - regardless of which type of control did the selection.

As Lee suggests in his comment, the binding for the ItemsSource property of the ListView does not appear to be correct. There are multiple ways to approach this, depending on how your project is designed/structured.
In order to DataBind, the ListView will need some kind of DataContext which (as the name implies) is kind of the Context for the Binding. If you are using MVVM, then most likely, the DataContext of your entire Window/Control would be a ViewModel. In that case, you bind to a property of the ViewModel like this:
<ListView ItemsSource="{Binding RssItems}">...</ListView>
This assumes you have a ViewModel with a public RssItems property (which is some kind of List/Enumerable) and the ViewModel is DataContext.
If you are not using MVVM, there are a lot of ways to assign the ItemsSource both with DataBinding and without. The easiest way I can suggest, if you're not fully comfortable with DataBinding, would be to manually assign the ItemsSource, like this:
Xaml:
<ListView x:Name="MyRssList">...</ListView>
Code Behind (somewhere after the UI has Loaded and after you've created/populated _list):
MyRssList.ItemsSource = _list;
This doesn't use DataBinding, but it will get the job done. If you want to start out with DataBinding, you could do the following:
XAML:
<ListView x:Name="MyRssList" ItemsSource="{Binding}>...</ListView>
Code Behind:
MyRssList.DataContext = _list;
This will assign the List as the DataContext of the ListView, then DataBind the ItemsSource property to the DataContext.
Overall, You're on the right track. I'd recommend some reading on DataBinding and MVVM. MVVM is a very good way to leverage the powerful DataBinding capabilities of WPF, and a strong understanding of DataBinding is extremely valuable in designing and building great WPF apps.
Hope this helps. Good luck!

Related

WPF and MVVM: bind UserControl to XAML dynamically

seems like a trivial task: i am building a wpf application, using MVVM pattern. what i want is dynamically change part of a view, using different UserControls, dependent on user input.
let's say, i have got 2 UserControls, one with a button, and another with a label.
in main view i have a container for that. following XAML "works":
<GroupBox Header="container" >
<local:UserControlButton />
</GroupBox>
and a UserControl element with buttons pops up. if i change it to another one, it works too.
question is how to feed that groupbox dynamically. if i put something like that in my model view:
private UserControl _myControl;
public UserControl MyControl
{
get
{
return _myControl;
}
set
{
_myControl= value;
InvokePropertyChanged("MyControl");
}
}
and change my view XAML to something like:
<GroupBox Header="container" >
<ItemsControl ItemsSource="{Binding MyControl}" />
</GroupBox>
and feed it from command with usercontrol for button or for label: nothing happens, although "MyControl" variable is set and is "invoke property changed"..
Obviously there are many ways to skin this particular cat - but to answer the question of why it doesn't work you need to look into the ItemsSource property of ItemsControl on MSDN.
The items control is designed to show multiple items, provided through an IEnumerable passed to the ItemsSource property. You are passing a UserControl, so the binding will fail.
For your example, I would change the ItemsControl to a ContentControl and bind the content to your MyControl property. This should then work.
<GroupBox Header="container" >
<ContentControl Content="{Binding MyControl}" />
</GroupBox>
However, I would strongly recommend looking into other ways of doing this - having a control in your VM breaks MVVM to my mind. Depending on what you are doing look at data templates - #Sheridan's link in the comments provides an great description of a way to do it.
Couldn't post this as a comment so adding as answer..
Have a look at this:
Implementing an own "Factory" for reusing Views in WPF
It uses DataTemplates but doesn't require the DataTemplate section for each view. If you potentially have a lot of user controls/views you wish to display or you are reusing through multiple views or you are intending to actually dynamically generate a view (versus just loading an existing user control) then this might suite your needs.

MVVM and dynamic generation of controls

i've written a tool that generates sql queries using GUI, i want to rewrite the tool using MVVM and WPF, every sql column type has a different control as you can see in the following image
i add a column filter control based on the sql column type, and i generate the controls using code, just like i used to do in windows forms.
in MVVM i've read that the view is writtien enteirly using XAML,
does MVVM suite such application where i have to add different user
controls dynamically to a stack panel?
The controls won't exist in the view unless some column is double clicked, that means the control won't be available in the xaml and won't be hidden or collapsed.
is there any way that i can avoid the bindings in the code behind?
should i create a user control for each column type?
in general what is the best approach to devlop such application with complex and dynamic ui using mvvm?
Guess I know how to achieve that, but it is very complex stuff. First you should comprehend MVVM basic concepts.
Main ViewModel should be a class with ObservableCollection of ViewModels, each of them represents a column with its data and properties.
interface IViewModel : INotifyPropertyChanged,IDisposable
{
}
interface IColumnViewModel : IViewModel
{
}
class ViewModelBase : IViewModel
{
// ... MVVM basics, PropertyChanged etc. ...
}
class MainViewModel : ViewModelBase
{
ObservableCollection<IColumnViewModel> Columns {get; set}
}
In View I suppose something like ItemsControl with ItemTemplate, that should embed ContentControl with DataTemplate, that shall be automatically selected by WPF according to binded DataContext of list item. StackPanel itself is not suitable for that, but it can be invoked as ItemsPanelTemplate
<Window
xmlns:v="clr-namespace:WpfApplication.Views"
xmlns:vm="clr-namespace:WpfApplication.ViewModels">
<Window.Resources>
<DataTemplate DataType="{x:Type TypeName=vm:TextColumnViewModel}">
<v:TextColumnView/>
</DataTemplate>
</Window.Resources>
<ItemsControl
ItemsSource="{Binding Columns}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
So, you should build View/ViewModel pair for every column type.
Hope, my example will help. Good luck with your girlfriend and MVVM :)
If I've understood your scenario correctly :
You can use Data Templates & Items Templates
For example I've written an application which loads Data into Collection and then shows each item of that collection in a Wrap Panel [ Or stack panel ] based on defined data template.
And Wrap penel items are in sync by the collection itself within two way binding
You should consider using Observable Collections to achieve this goal
Then you can fill the collection and see the results on a view
I hope this helps
To write something like this in MVVM, you would have one view that is say, your content area. That view would have a view model, one of the properties of that view model would be a view, or several properties of that view model would be a view. It takes a bit to wrap your head around at times, but if you use Inversion of Control and Dependency Injection properly a view of views is very manageable in an MVVM pattern.
Well, your view isn't written entirely in XAML - you generate controls in C#.
I don't think you'll gain something from rewriting this and fitting it into an MVVM mold. Just keep the code as it is now and enjoy.

Replace current binding source object with another object

I'm totally new to C# and WPF and I'm trying to do my best with the data binding. I have a MyClass which implements INotifyPropertyChanged; so everytime I change a property value, this is updated in my UI. Then I have bound the DataContext of a stackpanel to an object of MyClass. Like this:
<StackPanel Name="stackPanel1" DataContext="{Binding}">
<TextBlock Name="textBlock1" Text="{Binding Path=Title, Mode=OneWay}" />
</StackPanel>
In code behind I do this:
item = new MyClass();
stackPanel1.DataContext = item;
and the binding is working fine. If I replace my current binding source object with another one, I have to manually set this by typing again the datacontext binding:
item = new MyClass();
stackPanel1.DataContext = item;
item1 = new MyClass();
.
. //manipulate item1
.
if (item1 is ok)
item=item1;
stackPanel1.DataContext = item;
Is there a better way to replace my source object and have all the associated bindings updated?
When you say stackPanel1.DataContext = item;, you are setting the property, not binding it.
When you set the property, you are setting it equal to an instance of the object. When you bind it, you are telling it it will be getting its value from some other location, so look in that location anytime it needs to get the value.
Providing your class that contains the bound properties implements INotifyPropertyChanged, then the UI will be alerted anytime a bound property changes, which causes the binding to get reevaluated.
For example, if you had set the DataContext initially with
MyWindow.DataContext = this;
where this was your Window, and your Window had a propety of type MyClass called Item, then you could bind the DataContext using the following
<StackPanel DataContext="{Binding Item}" ...>
and anytime you updated the property Item, your StackPanel's DataContext would also update (providing you implement INotifyPropertyChanged).
If you're interested, I like to blog about beginner concepts in WPF, and you may be interested in checking out my article What is this "DataContext" you speak of?, which is a very simple explanation of what the DataContext is and how it's used.
To summarize, WPF has two layers: the UI layer and the Data Layer. The DataContext is the data layer, and when you write {Binding SomeProperty}, you are actually binding to the data layer. Typically you set the data layer (DataContext) once in your code behind, and then use Bindings in your XAML to make your UI layer display information from the data layer.
(You may also be interested in checking out my Simple MVVM Example, which contains a very simple working code sample, and illustrates some examples of how INotifyPropertyChanged is implemented and how the UI layers and Data layers can be completely separate)
You may add a CurrentItem property in your MainWindow (or UserControl or whatever it is) and also implement INotifyPropertyChange for that property. Then set
DataContext = this;
in the MainWindow's constructor and bind like this:
Text="{Binding Path=CurrentItem.Title}"
Now whenever you set
var item = new MyClass();
...
CurrentItem = item;
the binding will be updated.
DataContext="{Binding}"
and
stackPanel1.DataContext = item;
Both do basically the same thing. The difference being that one is done in XAML and the other is in code. While the first example would allow binding to be updated given a binding parent the second one must be updated every time you want to change what the stackpanel is attached to. IMHO you should create a common binding parent to bind against. This would allow you to change the child bindings without having to set the context everytime.
<StackPanel Name="parentPanel">
<StackPanel Name="stackPanel1" DataContext="{Binding Path=Child}">
<TextBlock Name="textBlock1" Text="{Binding Path=Title, Mode=OneWay}" />
</StackPanel>
</StackPanel>
parent = new ParentClass();
parent.Child= new MyClass();
parentPanel.DataContext = parent ;
Now if notify property changed was created on ParentClass correctly you can changing the binding for the child stack panel
parent.Child= new NewClass();

Loading XAML at runtime using the MVVM pattern in WPF

This is a question that extends from the originally posted here:
Link to loading-xaml through runtime
I'm working on a WPF MVVM application that loads XAML content dynamically from an external source, very similar as the answer in the post above.
Here is what I got so far:
My View declares an instance of the ViewModel as a resource and creates an instance of that ViewModel
In my ViewModel constructor I'm loading a XamlString property coming from an external source (file or db..)
In my view I have a button that user clicks after ViewModel finishes loading and in the click-event code-behind I'm deserializing the dynamically loaded XAML and add it to my grid.
My question is, how can I eliminate code-behind and automate the logic so the View can render the new xaml section dynamically right after the ViewModel is done getting the XAML content and initializing the string property?
Should I use some kind of Messaging Bus so the ViewModel notifies once the property has been set so the View can add the new content?
What troubles me is the fact that ViewModels do have a reference to Views and should not be in charge of generating UI elements.
Thanks in advance!
Edit:
Just to clarify: in my particular case I am not trying to bind a Business Object or Collection (Model) to a UI element (e.g. Grid) which obviously could be accomplished through templates and binding. My ViewModel is retrieving a whole XAML Form from an external source and setting it as a string property available to the View. My question is: Who should be in charge of deserializing this XAML string property into a UI element and add it programmatically to the my grid once my Xaml string property in the VM is set?
This sounds to me more of like a View responsibility, not ViewModel. But the pattern as i understand it enforces to replace any code-behind logic with V-VM bindings.
I have a working solution now and I'd like to share it. Unfortunately I did not get rid of code-behind completely but it works as I expect it to. Here is how it works(simplified):
I have my simplified ViewModel:
public class MyViewModel : ViewModelBase
{
//This property implements INPC and triggers notification on Set
public string XamlViewData {get;set;}
public ViewModel()
{
GetXamlFormData();
}
//Gets the XAML Form from an external source (e.g. Database, File System)
public void GetXamlFormData()
{
//Set the Xaml String property
XamlViewData = //Logic to get XAML string from external source
}
}
Now my View:
<UserControl.Resources>
<ViewModel:MyViewModel x:Key="Model"></ViewModel:MyViewModel>
</UserControl.Resources>
<Grid DataContext="{StaticResource Model}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<!-- This is the Grid used as a Place Holder to populate the dynamic content!-->
<Grid x:Name="content" Grid.Row="1" Margin="2"/>
<!-- Then create a Hidden TextBlock bound to my XamlString property. Right after binding happens I will trigger an event handled in the code-behind -->
<TextBlock Name="tb_XamlString" Text="{Binding Path=XamlViewData, Mode=TwoWay, UpdateSourceTrigger=LostFocus, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Visibility="Hidden" Loaded="tb_XamlString_Loaded" />
</StackPanel>
</Grid>
Basically I created a hidden TextBlock bound to my XAML String property in the ViewModel and I hooked its Loaded event to an event handler in the code behind of the View:
private void tb_XamlString_Loaded(object sender, RoutedEventArgs routedEventArgs)
{
//First get the ViewModel from DataContext
MyViewModel vm = content.DataContext as MyViewModel;
FrameworkElement rootObject = XamlReader.Parse(vm.XamlViewData) as FrameworkElement;
//Add the XAML portion to the Grid content to render the XAML form dynamically!
content.Children.Add(rootObject);
}
This may not be the most elegant but gets the job done. Like some people say, in MVVM there are some cases like this where little code-behind code is needed. It doesn't hurt and also part of this solution still uses the V-VM Binding principles when using the VM to retrieve and populate the XamlString property and exposing it to the View. If we would like to Unit Test the XAML parsing and loading functionality we could delegate it to a separate class.
I hope someone finds this useful!
I'm having trouble understanding what you're saying, so my answer will be based on my interpretation. You should consider posting a sample (simplified) of what you're trying to do.
1) I think you're misunderstanding what MVVM does. MVVM is mostly a binding-based pattern. Your view model should be exposing properties containing business objects and your view should just be binding to those properties. If I am misunderstanding you, and that's what you are doing, then your problem is that your view needs to be aware of when the properties get updated (after you deserialize your xaml, etc). There are two ways to do this: INotifyPropertyChanged interface on your viewmodel, or make your view model inherit from DependencyObject, and make the properties dependency properties. I won't go into details here, because this is a large subject that you should research on Google before making a decision.
2) Generally speaking, you shouldn't use click events inside your view if you're using MVVM. Instead, create properties on the view model of type ICommand (and create ICommand implementations to match, or use an implementation of DelegateCommand (google it) which will allow you to use delegates to implement the interface. The idea is, your view binds to the property and executes the handler directly inside the viewmodel.
3) If you want to push information from the viewmodel to the view, then you should create an event on the viewmodel and subscribe to it in the view, but this is a last resort, only to be used in cases like displaying a new window, etc. Generally, you should be using binding.
4) To be more specific about what you're doing, you should be binding your Grid's ItemsSource property to some property on the view model. Note, the property on the view model should be of type ObservableCollection<T> if you want to be able to add items and get instant updates.
Hope this helps.

How do I get my WPF ListView to properly bind with my List so the view updates when the data changes?

To date every ListView I've had I just set ItemSource={Binding} in my Xaml and then in the .CS file I say listview.datacontext = myobject and the view loads just fine. But now I need to have a list that updates as the data updates as well. So after some research I discovered ObservableCollections and rewrote my code to use that. But I can't get my data to display when setting the listview to my dataobject.
My Xaml:
<ListView ItemsSource="{Binding Tests}" Name="DataCompareTests" Margin="0,0,5,0" Grid.Column="0">
<ListView.View>
<GridView>
<GridViewColumn Header="TestCase" Width="200" DisplayMemberBinding="{Binding name}" />
</GridView>
</ListView.View>
</ListView>
My Xaml.cs:
readonly DataCompare dataCompare = new DataCompare();
public void Execute_Click(object sender, RoutedEventArgs e)
{
var Tests = new ObservableCollection<TestCases>();
Tests = dataCompare.LoadTestCases(); //located in another class file
//DataCompareTests.DataContext = Tests;
}
If I remove the "Tests" part of the binding in my Xaml and remove the comments from the .DataContext line above, the view displays the correct information. However it's my assumption that if I want my view to update as the data does I need to specify my object in the binding. How do I properly set that? I can't seem to find the correct answer.
Thanks,
Jason
I think you need to familiarize yourself a little better with bindings and object oriented programming in general.
If you set your datacontext to your model object, ".Tests" should be a public property of that model object. Also, don't do this:
var someVariable = new SomeClassThatTakesWorkToConstruct();
someVarialbe = someOtherVariable.SomeMethod();
What you meant to do was this:
var someVariable = someOtherVariable.SomeMethod();
This is for 2 good reasons 1) You are not wasting the construction of an ObservableCollection. 2) Your code will be easier to refactor (the type returned by SomeMethod can change without you having to alter your declaration of someVariable).
Edit, additional resources:
Databinding Overview
You've got a path specified but no source for the binding specified.
MVVM Article
Great article on using the common MVVM WPF pattern, helps you keep your code object oriented, clean, etc. even with complex UI interaction.
It would appear my concerns were pointless and I WAS doing this the proper way in the first place.
According to MSDN:
"However, if you are binding to an object that has already been created, you need to set > the DataContext in code, as in the following example.
...
myListBox.DataContext = myDataSet;"
My object was already created, and I did set the DataContext in the code. All I had to do was leave the ListView ItemSource as {Binding} and each time I added to the Tests object, the list updated..
I can't believe I spent an entire day doubting I was doing this correctly without moving forward to check. :-)

Categories