Binding a ListView to a property in the same Class - wpf

I have a Listview and i want to bind it to a list declared on the same class(codebehind)
public ObservableCollection<Slot> ListViewList { get; set; }
<ListView x:Name="ListViewSlots" Margin="0,230,0,0" ItemsPanel="{DynamicResource ItemsPanelTemplate1}" ItemsSource="{Binding Path=UserControl.ListViewList}" >
But is not working, i tried setting the datacontext of the usercontrol to self and desnt works.

Have you tried setting the DataContext of the UserControl to the list, and then setting the ItemsSource of the ListView to that?
ie.
<ListView ItemsSource="{Binding}" >

Add to your Window
<Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
and then your ListView becomes
<Listview ItemsSource="{Binding ListViewList}">...

first you have to introduce your list to the resources of the class:
public List<string> ListViewList
{
get{ return (List<string> Resources["ListViewList"];}
set{ Resources["ListViewList"] = value;}
}
or use ObservableCollection:
private ObservableCollection<string> _listViewList = new ObservableCollection<string>();
public ObservableCollection<string> ListViewList { get { return _listViewList; } }
then in XAML, you can bind something to it:
<ListView>
<ItemsPanel
ItemsPanel="{DynamicResource ItemsPanelTemplate1}"
ItemsSource="{Binding ListViewList}"
/>
</ListView>
and as Joel said you need to set the DataContext of the entire window
(or just the block you're dealing with) to self:
<Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>

Related

In WPF datatemplate, What I missed? data doesn't show correctly

I use a class 'SecondCondition' as basic data unit. It have 3 public property. ConditionColor, ConditionName, ConditionID
ObservableCollection 'SearchConditionList' is used as data list.
I made a datatemplate Binding like below.
< Model:SearchCondition x:Key="SearchCondition" />
< DataTemplate x:Key="ConditionSelector">
< StackPanel >
< xctk:ColorPicker x:Name="ConditionColorPicker"
SelectedColor="{Binding Path=ConditionColor,
Mode=TwoWay}">
< /xctk:ColorPicker>
< CheckBox x:Name="ConditionCheckbox"
Content="{Binding Path=ConditionName,
Mode=TwoWay}" />
< /StackPanel>
And I used the datatemplate at my Listbox.
< ListBox ItemsSource="{Binding Path=SearchConditionList}"
ItemTemplate="{StaticResource ConditionSelector}">
< /ListBox>
As result, I get number of blank template as much as List of items. But it doesn't show properties like color and name.
What I used as reference article use almost same and the code works, but mine is not. How can I solve this?
below is my reference.
https://learn.microsoft.com/ko-kr/dotnet/framework/wpf/data/data-templating-overview
Thank you.
P.S When I change codes like below, constant strings are shown very well but Bound value are not.
<StackPanel DataContext="{Binding Path=SearchConditionList}">
<ListBox ItemsSource="{Binding}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock Text="Why this doens't show bound value?"/>
<TextBlock Text=" : " />
<TextBlock Text="{Binding Path=ConditionName}"/>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
result is like below.
Here is my implementation based on your source code.
Model
public class SearchCondition
{
public Color ConditionColor { get; set; }
public string ConditionName { get; set; }
public string ConditionID { get; set; }
public SearchCondition(Color color, string name, string id)
{
ConditionColor = color;
ConditionName = name;
ConditionID = id;
}
}
ViewModel
public class ViewModel
{
public ObservableCollection<SearchCondition> SearchConditionList { get; set; }
public ViewModel()
{
SearchConditionList = new ObservableCollection<SearchCondition>();
SearchConditionList.Add(new SearchCondition(Colors.Red, "Red", "001"));
SearchConditionList.Add(new SearchCondition(Colors.Green, "Green", "002"));
}
}
The ViewModel is bound to the view in the code-behind.
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
Okay.. now this is the XAML.
<Window x:Class="DataBindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ConditionSelector">
<StackPanel Orientation="Horizontal">
<xctk:ColorPicker x:Name="ConditionColorPicker" SelectedColor="{Binding Path=ConditionColor, Mode=TwoWay}" />
<CheckBox x:Name="ConditionCheckbox" Content="{Binding Path=ConditionName, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</Grid.Resources>
<ListBox ItemsSource="{Binding Path=SearchConditionList}" ItemTemplate="{StaticResource ConditionSelector}" />
</Grid>
Screenshot
Honestly, I can't figure out where the problem is unless I see your full source code.
Visual Studio doesn't spit out exceptions explicitly. But you should be able to see the binding error in the output window. So you can find the clue.
Please compare your implementation with mine.

How to bind same window as a Data Context?

I have maintains the collection for the Combo-Box in Window code behind, if I bound that collection via xaml means it’s unable to fetch the collection until externally set the DataContext for the window in code behind. can anyone help me to resolve this.
public ObservableCollection<string> Orders
{
get; set;
}
public MainWindow()
{
InitializeComponent();
Orders = new ObservableCollection<string>();
Orders.Add("1000");
Orders.Add("1001");
Orders.Add("1002");
Orders.Add("1003");
//this.DataContext = this;
}
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}}">
<ComboBox Height="25" Width="100" ItemsSource="{Binding Orders}"></ComboBox>
</Grid>
You can set the DataContext for the Grid itself to use the same in various UI elements
You can bind datacontext of the window to itself.
That still will not work unless you move setting up the data though:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
<Grid>
<ComboBox ItemsSource="{Binding Orders}"/>
</Grid>
</Window>
and
public ObservableCollection<string> Orders
{
get; set;
}
public MainWindow()
{
Orders = new ObservableCollection<string>();
Orders.Add("1000");
Orders.Add("1001");
Orders.Add("1002");
Orders.Add("1003");
InitializeComponent();
}

WPF Binding with DataContext on Custom Content Control

I have a custom wizard control WizardControl deriving from UserControl which has a dependency property called Pages with a data type of my custom class collection called WizardPageCollection.
The WizardControl is hosted in a Window with a view model called MainViewModel and the pages of the wizard instantiated using XAML.
I am trying to bind the pages to sub-view models Page1VM and Page2VM declared as properties on the MainViewModel.
The first page binding of DataContext to Page1VM works fine, however the binding of the second page fails with the following error message:
System.Windows.Data Error: 3 : Cannot find element that provides DataContext. BindingExpression:Path=Page2VM; DataItem=null; target element is 'MyPage' (Name=''); target property is 'DataContext' (type 'Object')
Q. Why does the binding work on the first page but fail on the second and is there a way I can get this to work whilst still keeping the MainViewModel declared within the DataContext XAML tags of MainWindow? I would prefer not to use the ViewModel as a dictionary resources as this has some implications for us which I won't go into detail about.
As suggested by a commentor, if I change the binding to use RelativeSource as follows:
<common:MyPage DataContext="{Binding DataContext.Page1VM, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
<common:MyPage DataContext="{Binding DataContext.Page2VM, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
The first binding works ok, but the second one still fails, but with a different error message (as expected):
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=DataContext.Page2VM; DataItem=null; target element is 'MyPage' (Name=''); target property is 'DataContext' (type 'Object')
Thanks for your time!
My code listing is shown below:
MainWindow XAML:
<Window.DataContext>
<common:MainViewModel />
</Window.DataContext>
<Grid>
<common:WizardControl>
<common:WizardControl.Pages>
<common:WizardPageCollection>
<common:MyPage DataContext="{Binding Page1VM}" />
<common:MyPage DataContext="{Binding Page2VM}" />
</common:WizardPageCollection>
</common:WizardControl.Pages>
</common:WizardControl>
</Grid>
MainViewModel and PageViewModel:
public class MainViewModel
{
public PageViewModel Page1VM
{
get;
set;
}
public PageViewModel Page2VM
{
get;
set;
}
public MainViewModel()
{
this.Page1VM = new PageViewModel("Page 1");
this.Page2VM = new PageViewModel("Page 2");
}
}
public class PageViewModel
{
public string Title { get; set; }
public PageViewModel(string title) { this.Title = title; }
}
WizardControl XAML:
<Grid>
<ContentPresenter Grid.Row="0" x:Name="contentPage"/>
</Grid>
WizardControl code-behind:
public partial class WizardControl : UserControl
{
public WizardControl()
{
InitializeComponent();
}
public WizardPageCollection Pages
{
get { return (WizardPageCollection)GetValue(PagesProperty); }
set { SetValue(PagesProperty, value); }
}
public static readonly DependencyProperty PagesProperty =
DependencyProperty.Register("Pages", typeof(WizardPageCollection), typeof(WizardControl), new PropertyMetadata(new WizardPageCollection(), new PropertyChangedCallback(Pages_Changed)));
static void Pages_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
WizardPageCollection col = e.NewValue as WizardPageCollection;
WizardControl ctrl = obj as WizardControl;
ctrl.contentPage.Content = col.First();
}
}
public class WizardPageCollection : ObservableCollection<WizardPageBase> { }
public class WizardPageBase : ContentControl { }
MyPage XAML:
<Grid>
<Label Content="{Binding Title}" />
</Grid>
Your approach depends on the value inheritance of the Window's DataContext property, which doesn't work with your WizardPageCollection because it doesn't form a WPF element tree.
You should instead create your MainViewModel as a resource, and then reference it by StaticResource:
<Window ...>
<Window.Resources>
<common:MainViewModel x:Key="MainViewModel"/>
</Window.Resources>
<Window.DataContext>
<Binding Source="{StaticResource MainViewModel}"/>
</Window.DataContext>
<Grid>
<common:WizardControl>
<common:WizardControl.Pages>
<common:WizardPageCollection>
<common:MyPage DataContext="{Binding Page1VM,
Source={StaticResource MainViewModel}}"/>
<common:MyPage DataContext="{Binding Page2VM,
Source={StaticResource MainViewModel}}"/>
</common:WizardPageCollection>
</common:WizardControl.Pages>
</common:WizardControl>
</Grid>
</Window>
#Clemens answer workarounds the problem but the problem is something else, imho.
When item is added to WizardPageCollection, it should be added to LogicalTree as well. Look at sources of ItemsControl for inspiration. It is definitelly possible to make your binding works as they were.
I would use viewmodel first approach here. Define pages as collection of page viewmodels and genereate the views. At the end the xaml would look like this:
<common:WizardControl PagesSource="{Binding Pages}">
<common:WizardControl.PageTemplate>
<DataTemplate>
<common:MyPage DataContext="{Binding }" />
</DataTemplate>
</common:WizardControl.PageTemplate>
</common:WizardControl>
alternativelly, consider your WizardControl derive from Selector class instead usercontrol. (Selector is base class from listbox. It has itemssource and selected item).
<common:WizardControl ItemsSource="{Binding Pages}"
SelectedItem="{Binding SelectedPage}">
<common:WizardControl.ItemTemplate>
<DataTemplate>
<common:MyPage DataContext="{Binding }" />
</DataTemplate>
</common:WizardControl.ItemTemplate>
</common:WizardControl>

Wpf binding when property of the viewmodel is changed

My wpf project follows the MVVM pattern. In my view-model I have an IList of obejcts which I take from database. Every object from this IList has a property which is List.
When I open the view for that view-model, I have an ItemsControl with this property:
ItemsSource="{Binding TheIListOfObjects}"
and the items in that ItemsControl are actually showing information from List.
So, while the user is on the view, an itemscontrol is shown. What I want to do is this: while on the same view, if the user clicks a button, the List is changed. How can I make the ItemsControl to refresh and show the new info?
All you need to have is ObservableCollection type for the property type.
Xaml
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<Button Content="Click" Click="Button_Click" />
<ListView ItemsSource="{Binding People}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Window>
Xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private ObservableCollection<Person> _people = new ObservableCollection<Person>();
public ObservableCollection<Person> People
{
get { return _people; }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
People.Add(new Person { Name = "A" });
}
}
public class Person
{
public string Name { get; set; }
}
To make sure that changes in a collection are notified to its bound controls, you must use ObservableCollection<> instead of IList<>
In WPF, once we have data bound a collection to the ItemsSource property of a collection control, we don't refresh the ItemsSource property, or interact with it in any other way. Instead, we work with the data bound property value, so for your example...:
ItemsSource="{Binding TheIListOfObjects}"
... you should manipulate the TheIListOfObjects collection:
TheIListOfObjects = GetNewCollectionItems();
If you have correctly implemented the INotifyPropertyChanged interface in your view model, then your view should update as expected.

How to set Itemsource to a Comobox Inside a DataTemplate Dynamically?

I have one Listbox and applied one DataTemplate like this
<ListBox>
<ListBox.ItemTemplate>
<Grid>
<TextBlock Text="{Binding Path=Name}" Grid.Row=0/>
<ComoboBox Name="test"
DisplayMemberPath="Country"
SelectedValuePath="Country_ID">
</Grid>
How will I load ItemSource to this ComboBox dynamically based on each item selected in the ListBox? Iam new to WPF... pls help with your valuable suggestions.
<ListBox>
<ListBox.ItemTemplate>
<Grid>
<TextBlock Text="{Binding Path=Name}" Grid.Row=0/>
<ComoboBox Name="test"
DataContent="{Binding RelativeSource={RelativeSource AncestorType=ListBox}}"
ItemsSource="{Binding}"
DisplayMemberPath="Country"
SelectedValuePath="Country_ID">
</Grid>
Now your combocbox is always have the same itemssource as the parent listbox.
One way to do this is to bind the ItemsSource of your ComboBox to the SelectedValue property of the ListBox. For this to work the ListBox needs to be bound to a collection of items that contains a list of items that the ComboBox will bind to.
<ListBox
x:Name="CategoryList"
ItemsSource="{Binding Path=MasterList,
RelativeSource={RelativeSource AncestorType=Window}}"
DisplayMemberPath="MasterProperty"
SelectedValuePath="Details"
/>
<ComboBox
ItemsSource="{Binding Path=SelectedValue, ElementName=CategoryList}"
DisplayMemberPath="DetailProperty"
Grid.Row="1"
/>
In this example I have created a public property in the code behind of the window that exposes a list of objects containing the Details collection.
public List<Master> MasterList { get; set; }
public class Master
{
public string MasterProperty { get; set; }
public List<Detail> Details { get; set; }
}
public class Detail
{
public string DetailProperty { get; set; }
}

Resources