WPF ListView Databind not binding! - wpf

A simple WPF ListView:
<ListView Name="recordContainer" ItemsSource="{Binding Path=MyCollection}">
<GridView>
<GridViewColumn Width="260" Header="Name" DisplayMemberBinding="{Binding Path=Name}"/>
<GridViewColumn Width="100" Header="Value" DisplayMemberBinding="{Binding Path=Value}"/>
</GridView>
</ListView>
MyCollection is a property on my Page:
public ObservableCollection<MyData> MyCollection
{
get
{
return myCollection;
}
}
and this is my data object:
public class MyData
{
public string Name { get; set; }
public string Value { get; set; }
}
I fill up myCollection in the contructor of the page (before InitializeComponent) but the list is coming up empty!
I have the exact same configuration on other pages and it works fine - what am I missing?
Any help appreciated!

Set the DataContext of the page to the page itself :
this.DataContext = this;

What Thomas said... or...
What you're missing is that the binding is actually examining the DataContext. You can set the source differently a number of ways, however.
Think of it this way... Where is the binding to look for MyCollection? Binding is just a class; it isn't all knowing. You have to tell it where to look. By default, this is the DataContext. The DataContext is shared among the logical tree elements of your UI, with items lower in the tree able to see DataContexts higher up in the tree, or even override this value for items lower than themselves.
In your case, you want a value located on your Page which is not the DataContext. You must tell Binding how to find this. You can do this via the RelativeSource property.
{Binding MyCollection RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Page}}}
This is a combination of three markup helpers, Binding, RelativeSource, and Type.
RelativeSource finds an object in the tree relative to the current position. In this case, we are Finding an Ancestor (we're looking up the tree). RelativeSource walks up the tree looking for the first object of type Page. Once it finds this, it returns it to Binding. Binding then examines this object for a property MyCollection.

Here: http://simplesample.site90.com/wpf_binded_listview.php
is a complete example showing a ListView binded to a ObservableCollections and manipulating them using Commands.
Hope this helps.

I believe the problem is that a listview takes as a container something like a gridview. This is how a listview allows for non-linear arrangements of items unlike a listbox.
Think about it as
ListView = Layout Coordinator
GridView = Layout Style
Elements = Items to be displayed
Unless you intend to display your items in anyway other than a list, use a ListBox.
(p.s. disregard highlighting)

Related

Binding ObservableCollection of strings to templated ListBox

I have this collection
ObservableCollection<string> Urls { get; set; }
inside my data context class. I have a binding to it in my list box:
<ListBox ItemsSource="{Binding Urls}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding .}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The data is diplayed in the list box, the two corresponding-not-shown-here buttons with commands Add and Delete work as well, however, the changing the TextBox does not affect the contents of the collection.
I have tried Mode=TwoWay in binding, but I figured that it is turned on already. I have tried some other options like Validate=OnPropertyChange, however, there is still nothing get updated.
How to make the TextBox inside that templated items in ListBox actually update the Urls property of a datacontext class?
You cannot modify strings; use a wrapper class with one string property, then bind the TextBox to said property. That way the strings in the property can be replaced with the edited ones.

Binding TwoWay to SelectedItem: "Wrong way" synchronization on initialization

I am trying to bind a property of my DataContext to the SelectedItem on a ComboBox like this:
<ComboBox x:Name="ElementSelector"
ItemsSource="{Binding Source={StaticResource Elements}}"
DisplayMemberPath="ElementName"
SelectedItem="{Binding ValueElement, Mode=TwoWay}">
where the Elements resource is a CollectionViewSource (don't know, whether this matters).
When everything is initialized, the property ValueElement of the DataContext is set to the first item in the CollectionViewSource. What I want, is to initialize it the other way around: I would like to set SelectedItem of the ComboBox to the value of the property or null if no matching item is contained.
How can this be done?
EDIT - Additional information:
The ComboBox is part of a DataTemplate:
<DataTemplate x:Key="ReferenceTemplate"
DataType="viewModels:ElementMetaReferenceViewModel">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<ResourceDictionary>
<views:ElementsForReferenceViewSource x:Key="Elements"
Source="{Binding DataContext.CurrentProject.Elements, ElementName=Root}"
ReferenceToFilterFor="{Binding}"/>
</ResourceDictionary>
</StackPanel.Resources>
<TextBlock Text="{Binding PropertyName}"/>
<ComboBox x:Name="ElementSelector"
ItemsSource="{Binding Source={StaticResource Elements}}"
DisplayMemberPath="ElementName"
SelectedItem=""{Binding ValueElement, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
The ElementsForReferenceViewSource simply derives from CollectionViewSource and implements an additional DependencyProperty which is used for filtering.
The DataContext of the items in the CollectionViewSource look like this:
public class ElementMetaReferenceViewModel : ViewModelBase<ElementMetaReference, ElementMetaReferenceContext>
{
...
private ElementMetaViewModel _valueElement;
public ElementMetaViewModel ValueElement
{
get { return _valueElement; }
set
{
if (value == null) return;
_valueElement = value;
Model.TargetElement = value.Model;
}
}
...
}
For people encountering the same issue
The above code works as expected. The solution was getting the stuff behind the scenes right. Make sure, that the instance of the ViewModel which is the value of the property you want to bind to is definitely contained in the CollectionViewSource.
In my case the issue was deserializing an object tree incorrectly, so objects were instantiated twice. Then for each object a distinct ViewModel was initialized and then obviously the value of the property was not contained in the list.
Remark
To check whether this is an issue in your case, you can try the following:
Override the ToString() methods of the ViewModels displayed in the ComboBox like this:
public override string ToString()
{
return "VM"+ Model.GetHashCode().ToString();
}
Then you can easily compare the items in the source collection with the value on your property. Not the most professional way, but it did the job for me.

How to make a WPF DataGrid display a ViewModel collection with binding and a DataTemplate

First off, an apology as I am sure this is question's answer is quite simple. Nonetheless I still cannot seem to find an answer.
This is my first week using WPF. I simply wish to display the contents of some sort of list inside of a DataGrid. I am currently trying to use an ObservableCollection<> and a DataGrid, but that can change. How do I DataTemplate the list and display it?
The list is in a ViewModel, which has been set in Apps.xaml.cs as the DataSource for the MainWindow.xaml:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var window = new MainWindow();
// Create the ViewModel to which
// the main window binds.
var viewModel = new MainWindowViewModel();
// Allow all controls in the window to
// bind to the ViewModel by setting the
// DataContext, which propagates down
// the element tree.
window.DataContext = viewModel;
window.Show();
}
Here is the current list:
public ObservableCollection<PersonData> SearchResults { get; set; }
As for my xaml, though, I am rather lost -- I've tried fiddling around with binding and ItemsSource, and really have no idea how to use them here. Also, I suppose using a DataTemplate will be necessary, as I need to let it know somehow that the columns need to display certain properties of the PersonData, such as first and last name, location, and title. Any help is appreciated.
EDIT
More generally -- how does one simply display a ViewModel list, collection, what have you, period?
Your basic DataGrid syntax should look something like this:
<DataGrid ItemsSource="{Binding SearchResults}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
<DataGridTextColumn Header="Last Name" Binding="{Binding LastName}" />
</DataGrid.Columns>
</DataGrid>
If you don't want to display your data in a DataGrid, you can use an ItemsControl.
The default ItemsControl will add all your items to a StackPanel, and draw each of them using a TextBlock bound to the .ToString() of the item. You can change how the ItemsControl displays the items by specifying your own ItemsPanelTemplate and ItemTemplate for it to use. For some examples, check out my blog post on WPF's ItemsControl.
Ach. Failure to pre-study is fatal -- I did not implement INotifyPropertyChanged. Found out how to here: http://msdn.microsoft.com/en-us/library/ms743695

how to build dynamic grid and binding to xaml using mvvm

I'm planning a WPF application which will build dynamic grid with textblocks in the viewmodel and then refresh interface (xaml) with the new grid.
I've done the firts step, but i have problems to refresh the view with the new grid.
Is there any example code of how to bind the grid to the xaml that I can have a look at?? I really can't figure this out!
Thanks
You may be approaching this slightly wrongly, hard to say from the question-
Generally to show a dynamic set of UI elements in MVVM you bind the ItemsSource property of an ItemsControl to an ObservableCollection. The ItemsControl ItemsTemplate property converts the YourViewModel object into a UIElement which can be a TextBlock or whatever style you want.
So as an example:
// model
class Person
{
public string Name {get; private set;}
}
// view model
class MainViewModel
{
public ObservableCollection<Person> People {get; private set;}
}
//view
<UserControl DataContext="{Binding MyMainViewModelObject}">
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemsTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>/
</ItemsControl.ItemsTemplate>
</ItemsControl>
</UserControl>
I havent tested that code, it is just to illustrate. There are other ways of dissecting the problem into MVVM, it all depends on the situation. You would have to give more details for us to help you out with that. Rarely in WPF is there a need to use code to create or add UI elements to other UIElements etc.
A point to note more along the exact lines of the question however is that an ItemsControl can either bind to a bunch of regular objects and use it's template to create UIElements from them, OR it can bind to a list of UIElements, in which case the template is not applied (sounds like this is the situation you have).

Render List<Canvas> as list items (or itemscontrol)

I've effectively got a List or a List, XAML that represents the canvas elements... as a return from some stuff I have no control of.
I've been moderately successful rendering via stackpanel.children.add etc, however want to start having the list of canvas within a virtualizing panel or list.
I've set itemssource and datacontext on the <ItemsControl> and set the <DataTemplate> as such
<DataTemplate>
<ContentControl content="{Binding Path=CanvasBody}"/>
</DataTemplate>
This effectively turns entire silverlight body white/blank. I dont really care how I ultimately get to the desired result which is a list of the rendered canvas's... preferably virtualized for speed.
Its a retarded problem, and not ideal as far as how silverlight apps are built, I know...
I'd really appreciate any pointers. THANKS!
Generally to display a list of elements you bind the items control itemssource property to the list and then set a datatemplate for it which displays the desired properties of the list item type. You dont need to set content control. Beyond that I cant see what exactly you are asking.
<DataTemplate>
<Grid>
<TextBlock Text="{Binding ExampleProperty1}"/>
</Grid>
</DataTemplate>
codebehind file:
public class ExampleClass
{
public String ExampleProperty1
{
get
{
return "TEST";
}
}
}
public List<ExampleClass> List {get;} // note that this must be a public PROPERTY,
// not a field!

Resources