WPF DataGrid not loading data - wpf

I am a beginner in C# and WPF .
I have created a user control LogTable.atxml which contains a DataGrid and added it to the MainWindow.xaml .
The Table is displayed but the contents are not being fetched.
I think the issue is im not able to sent the Itemsource in the right way.
[Result]Please help.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TableTest.UserControls
{
class Tabledata
{
string A{ get; set; }
string B { get; set; }
string C { get; set; }
public Tabledata(string a, string b, string c)
{
A = a;
B = b;
C =c;
}
}
}
namespace TableTest.UserControls
{
/// <summary>
/// Interaction logic for LogTable.xaml
/// </summary>
public partial class LogTable : UserControl
{
ObservableCollection<Tabledata> list;
public LogTable()
{
InitializeComponent();
list = getTableDetails();
this.logGrid.ItemsSource = list;
}
private ObservableCollection<Tabledata> getTableDetails()
{
ObservableCollection<Tabledata> list= new ObservableCollection<Tabledata>();
Tabledata data = new Tabledata("aaa", "aaa", "aaa");
Tabledata data1 = new Tabledata("bbb", "aaa", "aaa");
Tabledata data2 = new Tabledata("ccc", "aaa", "aaa");
list.Add(data);
list.Add(data1);
list.Add(data2);
return list;
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UserControls="clr-namespace:TableTest.UserControls" x:Class="TableTest.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<UserControls:LogTable x:Name="logtable" HorizontalAlignment="Left" Margin="0,209,0,0" VerticalAlignment="Top" Width="287" Height="111"/>
</Grid>
</Window>
<UserControl x:Class="TableTest.UserControls.LogTable"
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"
mc:Ignorable="d"
>
<DataGrid x:Name="logGrid" AutoGenerateColumns="False"
Height="290"
HorizontalAlignment="Left"
VerticalAlignment="Top" Width="290"
ItemsSource="{Binding list}"
>
<DataGrid.Columns >
<DataGridTextColumn Binding="{Binding Path=A}" MinWidth="50" Header="Column 1"/>
<DataGridTextColumn Binding="{Binding Path=B}" MinWidth="50" Header="Column 2"/>
<DataGridTextColumn Binding="{Binding Path=C}" MinWidth="50" Header="Column 3"/>
</DataGrid.Columns>
</DataGrid>
</UserControl>

I think you need to do a few things
1. Your ItemSource needs to bind to a property. So your code should look something like
public partial class LogTable : UserControl
{
public ObservableCollection<Tabledata> list {get;set;}
public LogTable()
{
InitializeComponent();
DataContext=this;
list = new ObservableCollection<TableData>();
list = getTableDetails();
this.logGrid.ItemsSource = list;
}
You need to set your data context of your user control. If you are just using the codebehind you can get away with setting the DataContext in your usercontrols constructor like in the code above. But probably in the future you are going to want to use the mvvm pattern and set your datacontext to your viewmodel.
Note: You will need to set your datacontext of the mainwindow if you want to access any information from that window's codebehind (or whatever you want to bind data from).
Here is a good resource to read up on mvvm.
Update: Just saw your xaml. Since you named the Datagrid you can actually get away with not setting the DataContext as your as setting the ItemSource directly in your code. However, since you don't have your datacontext set you can remove the ItemSource={Binding list} from your xaml. That will only work if you have the list property available on your DataContext.
Update 2: You also need to make your properties public on your TableData class. then it will work
class Tabledata
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public Tabledata(string a, string b, string c)
{
A = a;
B = b;
C = c;
}
}

Related

How to bind a ComboBox to generic dictionary with xaml code

I'm using the following code :
private Dictionary<string, string> GetNumber { get; set; }
public ReportsLetterTra()
{
GetNumber = new Dictionary<string, string>
{
{"1", "First"},
{"2", "Second"}
};
InitializeComponent();
}
xaml code :
<ComboBox
DisplayMemberPath="value"
SelectedValuePath="key"
ItemsSource="{Binding ElementName=reportslettra,Path=GetNumber}"
SelectedIndex="0" Name="cmbFromNumber" />
Why is not bind GetNumber to cmbFromNumber?!
Update :
my complete code in behind file :
using System.Collections.Generic;
using System.Windows;
namespace WpfApplication20
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Dictionary<string, string> GetNumber { get; set; }
public Window1()
{
GetNumber = new Dictionary<string, string>
{
{"1", "First"},
{"2", "Second"}
};
InitializeComponent();
}
}
}
my complete xaml code:
<Window x:Class="WpfApplication20.Window1" Name="eportslettra"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid Height="26">
<ComboBox DisplayMemberPath="Value" SelectedValuePath="Key"
ItemsSource="{Binding ElementName=reportslettra,Path=GetNumber}"
SelectedIndex="0" Name="cmbFromNumber" />
</Grid>
</Window>
Where is my wrong?
Why is not bind GetNumber to cmbFromNumber?!
Your property is marked as private, make it public. Also, correct the casing on your ComboBox to Value and Key. Thirdly, your binding expression looks invalid, double check this. Finally, it looks like your properties are part of the view's code behind file. You might consider the MVVM design pattern.
Update
Your Window has the Name of 'eportslettra', but your binding expression uses the ElementName 'reportslettra'. Correct one of them.

TabControl: SelectedContent and SelectedItem return the same

I'm displaying a list of custom objects (here: Customer) dynamicly, each in its own tab using TabControl, ItemsSource and DataTemplate:
MainWindows.xaml.cs:
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace testTabControl
{
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int NumberOfContracts { get; set; }
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//create all
var customers = new List<Customer>
{
new Customer {FirstName = "Jim", LastName = "Smith", NumberOfContracts = 23},
new Customer {FirstName = "Jane", LastName = "Smiths", NumberOfContracts = 42},
new Customer {FirstName = "John", LastName = "Tester", NumberOfContracts = 32}
};
//show
myTabControl.ItemsSource = customers;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
Trace.WriteLine("myTabControl.SelectedContent is " + myTabControl.SelectedContent.GetType());
Trace.WriteLine("myTabControl.SelectedItem is " + myTabControl.SelectedItem.GetType());
// do something with content of the selected tab:
///myTabControl.SelectedContent.Foreground = new SolidColorBrush(Color.FromRgb(255, 0, 0));
}
}
}
MainWindow.xaml:
<Window x:Class="testTabControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:testTabControl="clr-namespace:testTabControl"
Title="MainWindow" Height="350" Width="525"
SizeChanged="OnSizeChanged"
>
<Window.Resources>
<DataTemplate x:Key="myContent" DataType="{x:Type testTabControl:Customer}">
<TextBlock x:Name="myContentRoot">
This is the content for
<TextBlock Text="{Binding FirstName}"/> <TextBlock Text="{Binding LastName}"/>
</TextBlock>
</DataTemplate>
<DataTemplate x:Key="myHeader" DataType="{x:Type testTabControl:Customer}">
<TextBlock Text="{Binding LastName}"/>
</DataTemplate>
</Window.Resources>
<TabControl
x:Name="myTabControl"
SelectedIndex="0"
ItemTemplate="{StaticResource ResourceKey=myHeader}"
ContentTemplate="{StaticResource ResourceKey=myContent}"
/>
</Window>
In the method OnSizeChanged I expected myTabControl.SelectedContent to return a TextBlock generated from DataTemplate. But an instance of Customer is returned! Same as by myTabControl.SelectedItem.
The only way I found to get the generated content is described here: http://msdn.microsoft.com/en-us/library/bb613579.aspx
Does anybody know any other solution without visual tree walk?
Your TabControl contains objects of type Customer. Templates can be used to tell WPF how to draw each Customer object, however it doesn't change the fact that the items in the TabControl are still Customer objects.
If you want to access a templated UI object for the item, you can either walk the VisualTree or use the ItemContainerGenerator to get the container holding the SelectedItem
For example,
var selectedItem = myTabControl.SelectedItem;
var selectedItemContainer =
myTabControl.ItemContainerGenerator.ContainerFromItem(selectedItem);
The reason your SelectedContent is returning a Customer object is this:
<DataTemplate x:Key="myContent" DataType="{x:Type testTabControl:Customer}">
It is returning exactly what you ask for. All the DataTemplate does is describe how to display the Customer object which is the content, nothing else. When you ask for content, it returns the object that the DataTemplate is displaying.
One way that you could do this house a property on your Customer object which replicates the output you want.
public string OutputString
{
get
{
return string.Format("This is the content for {0} {1}", this.FirstName, this.LastName);
}
}
And then do something like
Trace.WriteLine("myTabControl.SelectedContent is " + myTabControl.SelectedContent.OutputString);
Or you might could create a collection of strings that followed the format above, and make those the content. Then your SelectedContent would be a simple string by default.

Binding data to ComboBox WPF

I am newbie to WPF, and needs help to bind data into the ComboBox. The xaml file contains the tag below.
<UserControl x:Class="SKAT.Postfordeler.Client.UI.View.ChooseInboxView"
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"
mc:Ignorable="d"
d:DesignHeight="42" d:DesignWidth="598">
<Grid>
<StackPanel Orientation="Horizontal">
<ComboBox Name="_currentInbox" Width="180" Margin="5" Height="22" DataContext="{Binding}" />
<Label Content="Et job kører allerede i denne indbakke (1500 ud af 1700 poster behandlet)" Name="_currentProcess" Margin="5" Height="25" />
</StackPanel>
</Grid>
//Inbox class , this class was implemented in seperate project
namespace SKAT.Postfordeler.Shared.DataTypes
{
[DataContract]
public class Inbox
{
[DataMember]
public String Id { get; set; }
[DataMember]
public String Folder { get; set; }
[DataMember]
public Rule Rules { get; set; }
}
}
//This code is located in the controller, the Activate method will fire when the MainWindow was executed
public void Activate()
{
var configuration = _configurationManager.GetConfiguration();// this method gets the xaml file settings
_chooseInboxView.FillInboxes(configuration.Inboxes); // Inboxes data binds to combobox
}
and in the View code behind, I created a method to bind the data which contains a type of list
public void FillInboxes(List<Inbox> inboxes)
{
DataContext = inboxes;
}
But it won't works,Any help please?
I assume your Inbox class consists of two properties (for simplicity), but there may be any number of them:
public class Inbox
{
public int ID { get; set; }
public string Text { get; set; }
}
You write a DataTemplate, for example:
<Grid.Resources>
<DataTemplate x:Key="InboxTemplate">
<WrapPanel>
<TextBlock Text="{Binding Path=ID}"/>
<TextBlock>:</TextBlock>
<TextBlock Text="{Binding Path=Text}"/>
</WrapPanel>
</DataTemplate>
</Grid.Resources>
Then correct your ComboBox declaration like:
<ComboBox Name="_currentInbox" Width="180" Margin="5" Height="22" ItemsSource="{Binding}" ItemTemplate="{StaticResource InboxTemplate}" />
Finally you set DataContext of your ComboBox to your List<Inbox>:
public void FillInboxes(List<Inbox> inboxes)
{
_currentInbox.DataContext = inboxes;
}
EDIT: As you've asked for a simpler solution, you can just override ToString() method of your Inbox class:
protected override string ToString()
{
return ID.ToString() + ":" + Text;
}
Instead of DataContext={Binding} you should have ItemsSource={Binding}.
The data context for any frameworkelement in the visual tree is by default {Binding}.
<ComboBox Name="_currentInbox"
SelectedItem="Hoved"
Width="180"
Margin="5"
Height="22"
DisplayMemberPath="Name"
ItemSource="{Binding}" />
Also for the combobox to display text of the items correctly I suppose you need DisplayMemberPath too. I assumed the property from Inbox class that you need to display is Name. Please replace with your relevant property name.
If your Inbox class is like,
public class Inbox
{
public int ID { get; set; }
public string Text { get; set; }
}
And if you do not want to change your xmal, the code behind method should be like this,
public void FillInboxes(List<Inbox> inboxes)
{
_currentInbox.DisplayMemberPath = "Text"; // To display the 'Text' property in the combobox dropdown
//_currentInbox.DisplayMemberPath = "ID"; // To display the 'ID' property in the combobox dropdown
_currentInbox.DataContext = inboxes;
}

Binding to a property of of the SelectedItem Property

I am currently experimenting with WPF.
One thing, I wanted to do was a master to detail selection over multiple comboboxes.
I have a ViewModel with GroupItems that i use as ItemSource for the first combobox. These GroupItems have a Property called Childs, which includes a List of items that belong to this group.
I can't find a way to bind the comboBox1.SelectedItem.Childs as Itemsource for the second comboBox.
Right now I only got to
ItemsSource="{Binding ElementName=comboBox1, Path=SelectedItem}"
But I don't get the Property of the SelectedItem. How can this be done? Or is this not the WPF way to this?
Is there any good website to learn how to select different elements? Eplaining Path, XPath, Source and everything?
Thanks for any help.
Your binding above isn't attempting to bind to Childs, only SelectedItem.
Try something like this:
Window1.xaml
<Window x:Class="WpfApplication5.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 x:Name="_groups" ItemsSource="{Binding Groups}" DisplayMemberPath="Name"/>
<ComboBox ItemsSource="{Binding SelectedItem.Items, ElementName=_groups}"/>
</StackPanel>
</Window>
Window1.xaml.cs
using System.Windows;
namespace WpfApplication5 {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var model = new ViewModel();
var g1 = new Group { Name = "Group1" };
g1._items.Add("G1C1");
g1._items.Add("G1C2");
g1._items.Add("G1C3");
model._groups.Add(g1);
var g2 = new Group { Name = "Group2" };
g2._items.Add("G2C1");
g2._items.Add("G2C2");
g2._items.Add("G2C3");
model._groups.Add(g2);
var g3 = new Group { Name = "Group3" };
g3._items.Add("G3C1");
g3._items.Add("G3C2");
g3._items.Add("G3C3");
model._groups.Add(g3);
DataContext = model;
}
}
}
ViewModel.cs
using System;
using System.Collections.Generic;
namespace WpfApplication5
{
public class Group {
internal List<String> _items = new List<string>();
public IEnumerable<String> Items {
get { return _items; }
}
public String Name { get; set; }
}
public class ViewModel
{
internal List<Group> _groups = new List<Group>();
public IEnumerable<Group> Groups
{
get { return _groups; }
}
}
}

How to force ListBox to reload properties of ListBoxItems

Is there any way, how to force ObservableCollection to fire CollectionChanged?
I have a ObservableCollection of objects ListBox item source, so every time I add/remove item to collection, ListBox changes accordingly, but when I change properties of some objects in collection, ListBox still renders the old values.
Even if I do modify some properties and then add/remove object to the collection, nothing happens, I still see old values.
Is there any other way around to do this? I found interface INotifyPropertyChanged, but I don't know how to use it.
I agree with Matt's comments above. Here's a small piece of code to show how to implement the INotifyPropertyChanged.
===========
Code-behind
===========
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Documents;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
Nicknames names;
public Window1()
{
InitializeComponent();
this.addButton.Click += addButton_Click;
this.names = new Nicknames();
dockPanel.DataContext = this.names;
}
void addButton_Click(object sender, RoutedEventArgs e)
{
this.names.Add(new Nickname(myName.Text, myNick.Text));
}
}
public class Nicknames : System.Collections.ObjectModel.ObservableCollection<Nickname> { }
public class Nickname : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
void Notify(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propName));
}
}
string name;
public string Name
{
get { return name; }
set
{
name = value;
Notify("Name");
}
}
string nick;
public string Nick
{
get { return nick; }
set
{
nick = value;
Notify("Nick");
}
}
public Nickname() : this("name", "nick") { }
public Nickname(string name, string nick)
{
this.name = name;
this.nick = nick;
}
public override string ToString()
{
return Name.ToString() + " " + Nick.ToString();
}
}
}
XAML
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<DockPanel x:Name="dockPanel">
<TextBlock DockPanel.Dock="Top">
<TextBlock VerticalAlignment="Center">Name: </TextBlock>
<TextBox Text="{Binding Path=Name}" Name="myName" />
<TextBlock VerticalAlignment="Center">Nick: </TextBlock>
<TextBox Text="{Binding Path=Nick}" Name="myNick" />
</TextBlock>
<Button DockPanel.Dock="Bottom" x:Name="addButton">Add</Button>
<ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />
</DockPanel>
</Grid>
Modifying properties on the items in your collection won't fire NotifyCollectionChanged on the collection itself - it hasn't changed the collection, after all.
You're on the right track with INotifyPropertyChanged. You'll need to implement that interface on the class that your list contains. So if your collection is ObservableCollection<Foo>, make sure your Foo class implements INotifyPropertyChanged.

Resources