Displaying collection of objects with ItemsControl - wpf

I am trying to display collection of objects using ItemsControl. This is the simple example I created;
<Window x:Class="PhotoWieverWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PhotoWieverWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ScrollViewer>
<ItemsControl x:Name="Images" ItemsSource="{Binding Path=Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=FullName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Window>
using System.Collections.ObjectModel;
using System.Windows;
namespace PhotoWieverWPF
{
/// <summary>
/// MainWindow.xaml etkileşim mantığı
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<ListItem> Items { get; } = new ObservableCollection<ListItem>();
public MainWindow()
{
InitializeComponent();
Items.Add(new ListItem("Item 1"));
Items.Add(new ListItem("Item 2"));
Items.Add(new ListItem("Item 3"));
Items.Add(new ListItem("Item 4"));
Items.Add(new ListItem("Item 5"));
Items.Add(new ListItem("Item 6"));
}
public class ListItem
{
public string FullName { get; }
public ListItem(string fullname)
{
FullName = fullname;
}
}
}
}
However, I am getting a blank window with no errors. I suppose I made a mistake with binding, but I can't figure out what it is.

Like #Clemens said you forget to set DataContext for your View. You can easily set it to your code-behind's constructor like this:
public MainWindow()
{
InitializeComponent();
DataContext = this; // you need this
Items.Add(new ListItem("Item 1"));
Items.Add(new ListItem("Item 2"));
Items.Add(new ListItem("Item 3"));
Items.Add(new ListItem("Item 4"));
Items.Add(new ListItem("Item 5"));
Items.Add(new ListItem("Item 6"));
}

Related

WPF How to bind object from window to custom user control

I need to create a custom user control and pass it from the main window an object. I need to display the object's attribute inside the user control, how can i do it? Thanks in advance.
EDIT: This is my code, what i'm doing wrong?
My custom control:
public partial class DetailsComponent : UserControl
{
public static readonly DependencyProperty ModelProperty = DependencyProperty.Register("Model", typeof(bool), typeof(DetailsComponent), new PropertyMetadata(null));
public ModelClass Model
{
get { return (ModelClass)GetValue(ModelProperty); }
set
{
SetValue(ModelProperty, value);
}
}
public DetailsComponent()
{
InitializeComponent();
DataContext = this;
}
}
usercontrol.xaml.cs:
<UserControl x:Class="WpfApp3.DetailsComponent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel>
<TextBlock Text="{Binding Name}"></TextBlock></TextBlock>-->
</StackPanel>
</Grid>
MainWindows.xaml:
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp3" xmlns:custom="clr-namespace:LoadingSpinnerControl;assembly=LoadingSpinnerControl" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:DetailsComponent Model="{Binding Model}"></local:DetailsComponent>
</Grid>
MainWindows.xaml.cs:
public partial class MainWindow : Window, INotifyPropertyChanged
{
ModelClass Model = null;
public MainWindow()
{
InitializeComponent();
Model = new ModelClass("hi", "hi", "hi");
DataContext = this;
}
}
ModelClass.cs:
public class ModelClass
{
public ModelClass(string name, string description, string city)
{
Name = name;
Description = description;
City = city;
}
public string Name { get; set; }
public string Description { get; set; }
public string City { get; set; }
}
Model must be defined as a public property for you to be able to bind to it:
public partial class MainWindow : Window
{
public ModelClass Model { get; }
public MainWindow()
{
InitializeComponent();
Model = new ModelClass("hi", "hi", "hi");
DataContext = this;
}
}
The DetailsModel should not set its own DataContext property because then you cannot bind to the Model property of its inherited DataContext:
public partial class DetailsComponent : UserControl
{
public static readonly DependencyProperty ModelProperty =
DependencyProperty.Register("Model", typeof(ModelClass), typeof(DetailsComponent), new PropertyMetadata(null));
public ModelClass Model
{
get { return (ModelClass)GetValue(ModelProperty); }
set { SetValue(ModelProperty, value); }
}
public DetailsComponent()
{
InitializeComponent();
}
}
You could bind to the Name property of the Model dependency property using a RelativeSource binding:
<UserControl x:Class="WpfApp3.DetailsComponent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel>
<TextBlock Text="{Binding Model.Name,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
</StackPanel>
</Grid>
</UserControl

WPF: Update ListBox from bound property when button is pressed, not automatically

Given the following classes (where ViewModelBase contains the INotifyPropertyChanged members)
namespace WpfApplication1
{
class ViewModel : ViewModelBase
{
private Person selectedPerson;
public ObservableCollection<Person> Persons { get; set; }
public Person SelectedPerson
{
get
{
return selectedPerson;
}
set
{
selectedPerson = value;
OnPropertyChanged("SelectedPerson");
}
}
public ViewModel()
{
Persons = new ObservableCollection<Person>();
Persons.Add(new Person { FirstName = "John", LastName = "Smith" });
Persons.Add(new Person { FirstName = "Jane", LastName = "Jones" });
SelectedPerson = new Person();
}
}
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
And the following XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:ViewModel />
</Window.DataContext>
<Grid>
<StackPanel>
<ListBox ItemsSource="{Binding Persons}"
DisplayMemberPath="FirstName"
SelectedItem="{Binding SelectedPerson}"/>
<StackPanel DataContext="{Binding SelectedPerson}">
<TextBox Text="{Binding FirstName}" />
<TextBox Text="{Binding LastName}" />
</StackPanel>
</StackPanel>
</Grid>
</Window>
If the value in the TextBox bound to FirstName is changed, the change is automatically reflected in the ListBox. Assuming that I add a Button control and associated Command, is there a way instead to have the changed value propagated to the ListBox only if that Button has been pressed?

WPF ComboBox not Firing on selection

I have a ComboBox, shown below. Why isn't the code-behind always firing?
XAML:
<ComboBox Height="23"
Name="cbAppendCreate"
VerticalAlignment="Top"
Width="120"
Margin="{StaticResource ConsistentMargins}"
ItemsSource="{Binding Path=CbCreateAppendItems}"
SelectedValue="{Binding Path=CbAppendCreate,UpdateSourceTrigger=PropertyChanged}" />
CodeBehind:
private string cbAppendCreate;
public string CbAppendCreate {
get {
//....
return cbAppendCreate
}
set { //This doesn't fire when selecting the first of 2 Items,
//but always fires when selecting the 2nd of two items
//....
cbAppendCreate = value;
}
}
I'll post my working code here, it's very simple. I've just created a default WPF app using VS2012 template. Here's MainWindow.xaml content:
<Window x:Class="
WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ComboBox Height="23"
Name="cbAppendCreate"
VerticalAlignment="Top"
Width="120"
ItemsSource="{Binding Path=CbCreateAppendItems}"
SelectedValue="{Binding Path=CbAppendCreate,UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding CbAppendCreate}"></TextBlock>
</StackPanel>
Here's code-behind:
namespace WpfApplication1
{
public class DataSource
{
public List<string> CbCreateAppendItems { get; set; }
public string CbAppendCreate { get; set; }
public DataSource()
{
CbCreateAppendItems = new List<string>() { "create", "append" };
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new DataSource();
}
}
}
When I select different values in combo-box, the TextBlock updates to the same value, hence the VM's property is also updated.

How to databind public property in xaml

All I am trying to do is bind a public property to a textBlock. What am I doing wrong here?
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public string test { get; set; }
public MainWindow()
{
test = "this is a test";
InitializeComponent();
}
}
}
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="test"></ObjectDataProvider>
</Window.Resources>
<Grid>
<TextBlock Height="23" HorizontalAlignment="Left" Margin="108,58,0,0" Name="textBlock1" VerticalAlignment="Top" Text="{Binding Source={StaticResource test}}" />
</Grid>
You can simply add a datacontext and access your property
public partial class MainWindow : Window,INotifyPropertyChanged
{
private string _test;
public string test
{
get
{
return _test;
}
set
{
_test = value;
OnPropertyChanged("test");
}
}
public MainWindow()
{
test = "this is a test";
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
<TextBlock Height="23" HorizontalAlignment="Left" Margin="108,58,0,0" Name="textBlock1" VerticalAlignment="Top" Text="{Binding test}"/>
Also check this post for details of when to use an ObjectDataProvider
http://bea.stollnitz.com/blog/?p=22
At first you need you class to implement INotifyPropertyChanged or a property to be DependencyProperty for changing property value on textbox text change,
namespace WpfApplication1
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _test
public string test
{
get
{
return _test;
}
set
{
_test = value;
OnPropertyChanged("test");
}
}
public MainWindow()
{
test = "this is a test";
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
Than you can bind to that property by giving name to that window, and using ElementName property like this.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Name="myWindow">
<Window.Resources>
<ObjectDataProvider x:Key="test"></ObjectDataProvider>
</Window.Resources>
<Grid>
<TextBlock Height="23" HorizontalAlignment="Left" Margin="108,58,0,0" Name="textBlock1" VerticalAlignment="Top" Text="{Binding ElementName=myWindow, Path=test}" />
</Grid>
You actually don't need to implement INotifyPropertyChanged. However, this will be a one time data binding.
For example in XAML:
<TextBlock Name="SomeTextBlock" Text="{Binding Path=SomeProp}" />
In Code:
public string SomeProp { get; set; }
public MainWindow()
{
InitializeComponent();
SomeProp = "Test Test Test";
SomeTextBlock.DataContext = this;
}

Grouping items in a ComboBox

I have a ListView that contains two types of objects, single and multiple.
The single is a ordinary TextBlock while the multiple is a ComboBox with items.
I'm trying to group the items in the ComboBox without success. Is it possible? Or should I go for a different approach?
What I'm trying to achieve:
[ComboBox v]
[Header ]
[ Item]
[ Item]
[Header ]
[ Item]
It is possible. Use a ListCollectionView with a GroupDescription as the ItemsSource and just provide a GroupStyle to your ComboBox. See sample below:
XAML:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StackOverflow"
xmlns:uc="clr-namespace:StackOverflow.UserControls"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ComboBox x:Name="comboBox">
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Window>
Code-behind:
namespace StackOverflow
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//this.comboBox.DataContext = this;
List<Item> items = new List<Item>();
items.Add(new Item() { Name = "Item1", Category = "A" });
items.Add(new Item() { Name = "Item2", Category = "A" });
items.Add(new Item() { Name = "Item3", Category = "A" });
items.Add(new Item() { Name = "Item4", Category = "B" });
items.Add(new Item() { Name = "Item5", Category = "B" });
ListCollectionView lcv = new ListCollectionView(items);
lcv.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
this.comboBox.ItemsSource = lcv;
}
}
public class Item
{
public string Name { get; set; }
public string Category { get; set; }
}
}

Resources