How do I bind to a List<T> using DataContext? - c#

I'm self-learning C#, OOP, and WPF so the potential for stuff ups is staggering.
So given that, can someone please explain why after clicking the button in my tiny test example the Name property appears in the TextBox but the ListBox shows nothing?
XAML:
<Window x:Class="BindingTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BindingTest" Height="250" Width="300">
<Grid Name="mainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Name="MakeIntListButton"
Click="MakeIntListButton_Click">Make and Display Integer List</Button>
<TextBox Grid.Row="1" Text ="{Binding Path=Name}"
/>
<ListBox
Grid.Row="2"
ItemsSource="{Binding Path=MyIntegers}"
/>
</Grid>
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BindingTest
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void MakeIntListButton_Click(object sender, RoutedEventArgs e)
{
AClass InstanceOfAClass = new AClass();
InstanceOfAClass.MyIntegers.Add(6);
InstanceOfAClass.MyIntegers.Add(7);
InstanceOfAClass.MyIntegers.Add(42);
InstanceOfAClass.Name = "Fred";
mainGrid.DataContext =InstanceOfAClass ;
}
}
public class AClass
{
public string Name {get;set;}
public List<int> MyIntegers = new List<int>();
}
}

Part of me wonders whether it's something to do with the fact that "MyIntegers" is a public field rather than a property. Can you refactor you class to look like this and try it?
public class AClass
{
private List<int> _ints = new List<int>();
public string Name { get; set; }
public List<int> MyIntegers
{
get { return _ints; }
}
}

I ran your sample and when I clicked on the button the TextBox was populated with the Name as expected.
The only problem I encountered was that the ListView was not getting populated with the list of integers. That's to do with the fact that XAML is not very comfortable with generics if you modify it to bind to an array instead it works. WPF supports consumption of XAML fine, it's using generics within XAML that's not supported. As Matt Hamilton points out in his answer MyIntegers just needs to be made a propety by adding a get acessor.
Add C# Property:
public int[] MyInts { get { return MyIntegers.ToArray(); } }
XAML:
<ListBox Grid.Row="2" ItemsSource="{Binding Path=MyInts}" />

Look into using the System.Collections.ObjectModel.ObservableCollection for list binding instead of a plain List.

Related

The name "X" does not exist in the namespace "clr-namespace:Y"

I am trying to create a simple GUI application which changes the value of a string when it is changed in the GUI interface. The cs code is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TryBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
consle myconsole = new consle();
}
public class consle
{
public string mainstr {get; set;}
private int change = 23;
public void consle()
{
}
public void wrStr()
{
change = 44;
}
}
}
and the XAML code is as follows:
<Window x:Class="TryBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TryBinding"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:consle x:Name="Data" mainstr="maintry"/>
</Window.DataContext>
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="244,37,0,0" VerticalAlignment="Top" Width="75"/>
<TextBlock HorizontalAlignment="Left" Margin="131,43,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding Path=mainstr}" />
<CheckBox x:Name="ok" Content="CheckBox" HorizontalAlignment="Left" Margin="131,76,0,0" VerticalAlignment="Top"/>
</Grid>
but I am getting the error:
Error 1 The name "consle" does not exist in the namespace "clr-namespace:TryBinding".
Error 2 'consle': member names cannot be the same as their enclosing type
I am not able to understand why its happening since the class consle exists in the namespace, and the consle() is a constructor.
your constructor shoule look like
public consle()
{
}
Constructor doesn't have the datatype.

Using an object in code, which is instantiated in xaml

I am trying to experiment making simple GUI application.
In it I have made a simple class called consle. I am using a button click to trigger an event Button_Click_1. This should in turn call the function wrStr which changes the value of variable change internally. This changed value should reflect in the GUI because the variable change is also bound in the XAML code.
The problem is, I do not know how to access the consle type object instantiated in XAML code. If I get the name of the object instantiated in XAML I could just say <NameOfObject>.wrStr() inside the Button_Click_1 function.
The XAML code is given below:
<Window x:Class="TryBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TryBinding"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Set44" HorizontalAlignment="Left" Margin="244,37,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/>
<TextBlock HorizontalAlignment="Left" Margin="339,76,0,0" TextWrapping="Wrap" Text="{Binding Source=consle, Path=change, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top"/>
</Grid>
The cs code is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TryBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
}
}
public class consle
{
public string mainstr {get; set;}
public int change { get; set; }
public consle()
{
}
public void wrStr()
{
change = 44;
}
}
}

wpf using DataTemplate into HierachicalDataTemplate

in Wpf application i've to show the same collection of items into a ListBox and TreeView, The List show only the first level, the TreeView the whole Hierarchical. My Question is: why in the TreeView is not applied the DataTemplate i declared for Item object? how do i can share the same DataTemplate across the 2 controls (the color of text into TV must be red)?
<Window x:Class="TestWpf.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"
xmlns:viewmodel="clr-namespace:TestWpf"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.Resources>
<DataTemplate DataType="{x:Type viewmodel:Item}">
<TextBlock Foreground="Red" Text="{Binding Caption}"></TextBlock>
</DataTemplate>
</Grid.Resources>
<ListBox Grid.Row="0" Name="lb1" ></ListBox>
<TreeView Name="tv1" Grid.Row="1" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type viewmodel:Item}" ItemsSource="{Binding Items}" >
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
MainWindow.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TestWpf
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ObservableCollection<Item> Items = new ObservableCollection<Item>();
var item1 = new Item("1");
Items.Add(item1);
item1.Items.Add(new Item("1.1"));
item1.Items.Add(new Item("1.2"));
item1.Items.Add(new Item("1.3"));
var item2 = new Item("2");
Items.Add(item2);
item2.Items.Add(new Item("2.1"));
item2.Items.Add(new Item("2.2"));
item2.Items.Add(new Item("2.3"));
var item3 = new Item("3");
Items.Add(item3);
item3.Items.Add(new Item("3.1"));
item3.Items.Add(new Item("3.2"));
item3.Items.Add(new Item("3.3"));
this.lb1.ItemsSource = Items;
this.tv1.ItemsSource = Items;
}
}
}
Item.cs
namespace TestWpf
{
public class Item
{
public List<Item> Items { get; set; }
public string Caption { get; set; }
public Item(string caption)
{
this.Caption = caption;
this.Items = new List<Item>();
}
}
}
I just copy and pasted your code in VS, pressed F5 and it worked.
The only thing i had to change was the namespace since i created it with WPFApplication1 :)
Try taking your code out to a fresh project and try to build it and see if it works.

List<string> is not updated back to ListBox's ItemsSource?

I'm new to WPF. I have a List<string> as a source for my ListBox's ItemsSource. Initially, the ListBox shows all the Items in my List<string> OK. However, after trying adding some string to my List<string>, the ListBox doesn't update the changes. I'm using Binding to bind the data (behind) with the ListBox (view), here is my code:
//Code behind
public MainWindow: Window {
public MainWindow(){
InitializeComponent();
Items = new List<string>(){"1","2","3"};//after loaded, all these values are displayed OK in my ListBox.
DataContext = this;
//Try clicking on a button to add new value
button1.Click += (s,e) => {
Items.Add("4");//But my ListBox stays the same without any update/changes.
};
}
public List<string> Items {get;set;}
}
//XAML
<ListBox ItemsSource={Binding Items}/>
Could you please point out what I'm doing wrong here and give me a solution? Thank you very much in advance.
If you had read the documentation of ItemsSource you would already know what is wrong.
[...]
This example shows how to create and bind to a collection that derives from the ObservableCollection<T> class, which is a collection class that provides notifications when items get added or removed.
you should try ObservableCollection instead because it's
Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
<Window x:Class="WpfApplication3.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">
<Grid>
<Button Click="Button_Click" Content="Button" HorizontalAlignment="Left" Margin="441,289,0,0" VerticalAlignment="Top" Width="75"/>
<ListBox HorizontalAlignment="Left" ItemsSource="{Binding MyList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Name="lstbox" Height="296" Margin="21,23,0,0" VerticalAlignment="Top" Width="209"/>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<string> _myList = new ObservableCollection<string>(new List<string>(){"1","2","3"});
int i = 3;
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MyList.Add(i++.ToString());
}
public ObservableCollection<string> MyList
{
get { return _myList; }
set { _myList = value; }
}
}
}

How are ListView's ItemCollections related?

If I create multiple ListViews with the same ItemsSource they become strangely linked. In the following example, the two ListViews display a common list of strings. The assertions show that the two ItemCollections and SortDescriptionCollections are distinct, but if I attempt to sort the ListViews differently, the second sort order is applied to both.
The two ItemCollections must be related in order for the Selector.IsSynchronizedWithCurrentItem property to have any effect, but I would like to be able to break this association so that I can do things like I've tried in this example. Does anyone know how these collections are related, and how I can sever this relationship?
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:llv="clr-namespace:LinkedListViews"
x:Class="LinkedListViews.Window1"
x:Name="Window"
Title="Window1"
Width="640" Height="480">
<Grid x:Name="LayoutRoot">
<ListView
x:Name="ListView1"
ItemsSource="{Binding ElementName=Window, Path=Data}"
Margin="75,8,0,8" Width="237" HorizontalAlignment="Left"/>
<ListView
x:Name="ListView2"
ItemsSource="{Binding ElementName=Window, Path=Data}"
HorizontalAlignment="Right" Margin="0,8,73,8" Width="243"/>
</Grid>
</Window>
Code behind:
using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.ComponentModel;
using System.Collections.Generic;
namespace LinkedListViews
{
public partial class Window1
{
private List<string> _Data = new List<string>
{
"Alpha", "Beta", "Gamma"
};
public List<string> Data
{
get { return _Data; }
}
public Window1()
{
this.InitializeComponent();
// Insert code required on object creation below this point.
System.Diagnostics.Debug.Assert(ListView1.Items != ListView2.Items);
System.Diagnostics.Debug.Assert(ListView1.Items.SortDescriptions != ListView2.Items.SortDescriptions);
this.ListView1.Items.SortDescriptions.Add(new SortDescription(null, ListSortDirection.Ascending));
this.ListView2.Items.SortDescriptions.Clear();
this.ListView2.Items.SortDescriptions.Add(new SortDescription(null, ListSortDirection.Descending));
}
}
}
See Stopping ItemsControls from sharing filters

Categories

Resources