I have the following XAML:
...
<ListBox Name ="RoomsListBox" Height="100"
HorizontalAlignment="Left" Margin="12,41,0,0"
VerticalAlignment="Top" Width="120"></ListBox>
...
And the following C#-code:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
RoomsListBox.ItemsSource = new[] { new { Name = "First1" },
new { Name = "First2" } };
RoomsListBox.DisplayMemberPath = "Name";
}
The problem is that my ListBox have items but they are empty. Why I don't see "First1" and "First2" instead?
The issue here isn't with the bindings nor the ItemTemplate nor the change notification. It's the Anonymous Type you're using that's causing it. try using a class or struct for your items
public class Item
{
public string Name { get; set; }
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
RoomsListBox.ItemsSource = new[] {
new Item { Name = "First1" },
new Item { Name = "First2" }};
RoomsListBox.DisplayMemberPath = "Name";
}
your xaml stays the same, or you can define a DataTemplate for the ListBox items if you want. Note that you can't set both the ItemTemplate and DisplayMemberPath at the same time (one has to be null). Also, make sure that the class representing your items has to be public.
Hope this helps :)
Just a thought..
Have you tried settings the DisplayMemberPath property in XAML? There might be an issue with the order of calls.
You have to set DisplayMemberPath property on your ListBox to Name.
Moving forward you might want to consider creating a DataTemplate for your items to have more control:
<ListBox x:Name ="RoomsListBox" Height="100"
HorizontalAlignment="Left" Margin="12,41,0,0"
VerticalAlignment="Top" Width="120">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
See this tutorial for more info: http://weblogs.asp.net/scottgu/pages/silverlight-tutorial-part-5-using-the-listbox-and-databinding-to-display-list-data.aspx
I would prefer that you define your binding in your xaml and for example in your Code-Behind you define a property for the items of your listbox.
Example: (xaml)
<ListBox Name ="RoomsListBox"
ItemsSource="{Binding MyItems}"
Height="100"
HorizontalAlignment="Left"
Margin="12,41,0,0"
VerticalAlignment="Top"
Width="120" />
Example: (C# in your code-behind)
//...
private ObservableCollection<string> _myItems;
public ObservableCollection<String> MyItems
{
get
{
return _myItems ?? (_myItems = new ObservableCollection<string> { "FirstItem", "SecondItem"});
}
set
{
_myItems = value;
}
}
Like ChrisF said you could use the INotifiyPropertyChanged Interface, there you would raise the PropertyChanged event in the setter of your property.
See --> http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx
Related
I'm new to wpf, I created a listbox it will create a dynamic listitems,Here I used datetemplate which contains two controls that is two textblocks, one textblocks contains binding a values form combobox(which is string datatype),The other one is, bind a value from code bind.
XAML
<ListBox ItemsSource="{Binding obj}" HorizontalContentAlignment="Left" x:Name="lstbxindex" SelectionMode="Extended" Foreground="White" FontSize="20px" Height="201" BorderBrush="#555555" Margin="80,40,0,0" VerticalAlignment="Top" Width="282" Background="#555555" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5" >
<TextBlock Height="40px" Width="80px" Text="{Binding roundedhourvalue, FontSize="24" Background="#555555" Foreground="White"></TextBlock>
<TextBlock x:Name="items" Text="{Binding}" Margin="35,0,0,0"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C# (Roundedhour.cs)
public class Roundedhour
{
public string hourvalue { get; set; }
public override string ToString()
{
return string.Format("{0}", hourvalue);
}
}
In this class create a property for hourvalue. For this class I created a object in codebehind file which I mentioned below.create a object and assign a value for hourvalue variable.
C# (Code Behind)
{
if (dispatcherTimer1.Interval == TimeSpan.FromSeconds(15))
{
//lstbxindex.Items.Add(lstbxindex.SelectedItem.ToString());
string hrvalue = Convert.ToString(hrvalueinitially);
obj = new Roundedhour();
obj.hourvalue = Convert.ToString(hrvalueinitially);
string roundedhourvalue =obj.hourvalue;
this.DataContext = this;
//lblprojectAhr.Content = string.Join(",", hrvalueinitially + "" + "hr");
}
}
Here, I created a object for Rounderhour class.Assign values to that property hour value. But I cannot be bind a value from codebind to XAML page.
Your ItemsSource should be of an CollectionType.
ListBox ItemsSource="{Binding obj}"
You should also start to give your variables and properties meaningful names. That makes it easier to read your code later on.
The second Problem is in your Binding itself.
You are binding like this: Text="{Binding roundedhourvalue}
So WPF is expecting a property roundedhourvalue on obj.
But as shown in your CodeBehind there is only obj.hourvalue.
So change your Binding to Text="{Binding hourvalue}
Check your Output-Window for details.
NOTE:
string roundedhourvalue = obj.hourvalue;
has no getter and is not accsessible since its private.
NOTE: You either use a Binding OR your set the ItemsSource in CodeBehind.
Try it like this:
Just remove all the formatting and stuff:
<ListBox ItemsSource="{Binding RoundedHours}" x:Name="ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5" >
<TextBlock Text="{Binding hourvalue}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And change your code-behind to this:
private void UpdateDataContext(object hrvalueinitially)
{
List<Roundedhour> hours = new List<Roundedhour>();
hours.Add(new Roundedhour()
{
hourvalue = hrvalueinitially.ToString()
});
//Set the ItemsSource in code: => remove your ItemsSource Binding from XAML
listBox.ItemsSource = hours;
}
OR your can use an 'MVVM' approach:
public class MyViewModel : INotifyPropertyChanged
{
//IMPLEMENT INotifyPropertyChanged HERE PLS
public ObservableCollection<RoundedHour> Collection { get; set; } = new ObservableCollection<RoundedHour>();
private void AddToCollection(object hrvalueinitially)
{
Collection.Add(new RoundedHour()
{
hourvalue = hrvalueinitially.ToString()
});
OnPropertyChanged("Collection");
}
//Make sure to set your Windows DataContext to an Instance of this Class
}
Assign XAML object's "ItemsSource" property with your binding variable.
Also it's totally wrong binding object's itself into object's property like
this.DataTemplate = this;
Use:
List<yourobject> bindingObjectList = new List<yourobject>();
// insert your objects into the list
this.ItemsSource = bindingObjectList;
Here you can find an example:
Grid & Pivot Binding Example for multiple DataTemplates
I try to understand (without success) why binding behaves differentially when source object is string[] and List<string>. I have two lists, their only difference is ItemsSource - in one case array in second List:
XAML code:
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Modify items" Click="Button_Click"/>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<DataTemplate x:Key="ItemTemplate">
<TextBlock Text="{Binding}" FontSize="16"/>
</DataTemplate>
</StackPanel.Resources>
<ListView ItemsSource="{Binding ArrayElements}" ItemTemplate="{StaticResource ItemTemplate}" Width="100" Margin="20"/>
<ListView ItemsSource="{Binding ListElements}" ItemTemplate="{StaticResource ItemTemplate}" Width="100" Margin="20"/>
</StackPanel>
</StackPanel>
Code behind:
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
public string[] ArrayElements { get; } = new string[] { "Standard", "Standard", "Standard" };
public List<string> ListElements { get; } = new List<string> { "Standard", "Standard", "Standard" };
public MainPage() { this.InitializeComponent(); this.DataContext = this; }
private void Button_Click(object sender, RoutedEventArgs e)
{
ArrayElements[1] = "Modified one";
ListElements[1] = "Modified one";
RaiseProperty(nameof(ArrayElements));
RaiseProperty(nameof(ListElements));
}
}
Once I click button, the first list (build upon array) gets refilled with new elements (seems like new ItemsSource), so I can see a little flicker and the change in second element.
The same time in the second list nothing changes - the reference to binding source doesn't change so we don't see the difference on screen (PropertyChange has no effect as no property has been changed) - that's clear.
So why the list where the ItemsSource is set to array behaves different?
Sample to reproduce the issue (though almost whole code is above)
It sounds weird to me but i never faced this Problem because i use ElementViewModel objects in an observable collection. Instead of changing the list-item / Array element i Change a property of the ElementViewModel and use the INotifyProperty Change there. It's not the actuall answer to you question but you can work around with this.
I'm using WPF and have a TreeView on my form binding to model. Objects have attributes and I want to bind a selected item(in tree view) attributes to a listbox but I can't figure out how to this. My code is:
Bar class:
public class Bar
{
string barName;
List<bar> children;
List<Foo> attrs;
public string BarName
{
get { return barName; }
set { barName = value; }
}
public List<Folder> Children
{
get { return children; }
set { children = value; }
}
public List<Foo> Attributes
{
get { return attrs; }
set { attrs = value; }
}
public Bar(string name)
{
barName = name;
children = new List<Bar>();
attrs = new List<Foo>();
attrs.Add(new Foo { Name = "Attr1: " + name });
attrs.Add(new Foo { Name = "Attr2: " + name });
attrs.Add(new Foo { Name = "Attr3: " + name });
}
}
Foo class:
public class Foo
{
public string Name { get; set; }
}
Filling model:
Bar bar = new Folder("bar1");
bar.Children.Add(new Bar("bar1.1"));
bar.Children[0].Children.Add(new Bar("bar1.1.1"));
bar.Children.Add(new Bar("bar2"));
this.DataContext = bar;
And also XAML:
<Window.Resources>
<ResourceDictionary>
<HierarchicalDataTemplate DataType="{x:Type local:Bar}"
ItemsSource="{Binding Children}">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding BarName}"
Foreground="Black"
TextTrimming="CharacterEllipsis"
TextWrapping="Wrap"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Grid.Column="1"/>
</Grid>
</HierarchicalDataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<TreeView Height="162" HorizontalAlignment="Left" Margin="203,0,0,0" Name="treeView1" VerticalAlignment="Top" Width="288" ItemsSource="{Binding Children}"/>
<ListBox Height="100" HorizontalAlignment="Left" Margin="203,168,0,0" Name="listBox2" VerticalAlignment="Top" Width="288" ItemsSource="{Binding Name}"/>
</Grid>
Now TreeView binding works fine and I there is a Bar.Name displayed, but ListBox is empty. Please, explain me, what should I do?
You're binding your listbox to Name, so it's trying to find a property called "Name" in your Bar class. What I believe you're actually trying to do is show the attributes for the item that's currently selected in the TreeView. So bind to the TreeView's SelectedItem and set DisplayMemberPath to "Name":
<ListBox Height="100" HorizontalAlignment="Left" Margin="203,168,0,0" Name="listBox2" VerticalAlignment="Top" Width="288" ItemsSource="{Binding SelectedItem.Attributes, ElementName=treeView1}" DisplayMemberPath="Name"/>
This will work, but a better idea would be to create a member in you Bar class for the currently selected item (e.g. "CurrentTreeItem") and bind both the treelist's SelectedItem and the ListBox's source items to that property, that way at least you can put breakpoints in the setter etc and make sure your front end controls are firing properly. The problem with doing this though is that you don't seem to be supporting IPropertyChange notification (if you don't know what that is then drop what you're doing and hit Google before going any further).
You can bind your listbox directly to TreeView selected item by changing xaml to
<TreeView Height="162" HorizontalAlignment="Left" Margin="203,0,0,0" Name="treeView1" VerticalAlignment="Top" Width="288" ItemsSource="{Binding Source}" />
<ListBox DisplayMemberPath="Name" Height="100" HorizontalAlignment="Left" Margin="203,168,0,0" Name="listBox2" VerticalAlignment="Top" Width="288" ItemsSource="{Binding Path=SelectedItem.Attributes, ElementName=treeView1}"/>
Sadly, tree view's selected item is read only property, so you can't easily create a property in you view and bind both listbox and treeview to it.
Also you should change your population to
public ObservableCollection<Bar> Source { get; set; }
public MainWindow()
{
InitializeComponent();
Bar bar = new Bar("bar1");
bar.Children.Add(new Bar("bar1.1"));
bar.Children[0].Children.Add(new Bar("bar1.1.1"));
bar.Children.Add(new Bar("bar2"));
Source = new ObservableCollection<Bar>() { bar };
this.DataContext = this;
}
Be aware thought, that you have to implement INotifyPropertyChanged interface to allow bindings to update automatically
I´m trying to bind a ListBox to a ObservableCollection. I wan´t to bind the Text Properties of the ListBox entrys and the Background of the ListBox entrys.
The ListBox is defined in an loaded loose xaml file:
<TextBox Margin="0,5,5,5" Text="{Binding Path=TB9P}" Background="LightBlue" Name="DetailsviewTB9" Height="20">
<TextBox.ToolTip>
<StackPanel>
<Label FontWeight="Bold" Background="Blue" Foreground="White">Daten</Label>
<ListBox ItemsSource="{Binding Source={StaticResource res_LB1P}}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StringP}" Background="{Binding Path=SelectedItemP, Converter={StaticResource c_SelectedItemToBackgroundConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</TextBox.ToolTip>
</TextBox>
The DataContext is set on class DetailsViewText
public class LBEntry
{
bool DetailsViewLBSelectedItem = true;
string DetailsViewLB = "test";
public LBEntry(bool selcected, string str)
{
DetailsViewLB = str;
DetailsViewLBSelectedItem = selcected;
}
public bool SelectedItemP
{
get { return DetailsViewLBSelectedItem; }
set { DetailsViewLBSelectedItem = value; }
}
public string StringP
{
get { return DetailsViewLB; }
set { DetailsViewLB = value; }
}
}
public class LBEntrysCollection : System.Collections.ObjectModel.ObservableCollection<LBEntry>
{
//
}
public class DetailsViewText
{
string[] DetailsViewTB1_Text = new string[20];
bool[] fDetailsViewCB = new bool[20];
LBEntrysCollection[] LBEntrys = new LBEntrysCollection[]{
new LBEntrysCollection{ new LBEntry(false, "test"), new LBEntry(true, "test") },
new LBEntrysCollection{ new LBEntry(true, "test") },
new LBEntrysCollection{ new LBEntry(false, "test") },
new LBEntrysCollection{ new LBEntry(false, "test") },
new LBEntrysCollection{ new LBEntry(false, "test") }
};
public LBEntrysCollection LB1P
{
get { return LBEntrys[0]; }
set { LBEntrys[0] = value; }
}
public string TB9P
{
get { return DetailsViewTB1_Text[8]; }
set { DetailsViewTB1_Text[8] = value; }
}
...
}
The resource res_LB1P is set in the mainWindow constructor:
// Resources
this.Resources.Add("res_LB1P", detailsViewFrameHandling.DetailsViewTextP.LB1P);
Basicly I just want to bind the ListBox to a LBEntrysCollection with SelectedItemP as switch for the background Color and StringP as the Text Property. But I need the DataContext on DetailsViewText for other Propertys.
I´m getting an Exception when the xaml File is loading the StaticResource res_LB1P.
How do I have to set my Binding on ListBox and TextBlock to get it right?
EDIT:
With this
<ListBox ItemsSource="{Binding Path=LB1P}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=LB1P.StringP}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Items are added, but there is no Text shown in the TextBox
Now I´m really confused. It does work like this:
<ListBox ItemsSource="{Binding Path=LB1P}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StringP}" Background="{Binding Path=SelectedItemBrushP}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Simple enough, but I thought i had tried this before and it didn´t work...
Is it possible, that if one Binding does fail (the Background Binding) the other Binding (Text Property) does also not work?
I have always considered the ViewModel (the object the DataContext points to) to be just that: a Model of the View.
So to solve this, you need either one object that will be the ViewModel because there is only one DataContext property or you will need to add an extra DataContext-like property.
The first option (one ViewModel) can be realized by creating a new class that contains both the ObservableCollection and the DetailsViewText:
class ComposedViewModel: INotifyPropertyChanged
{
public LBEntrysCollection LBEntries
{
get { ... }
set { ... }
}
public DetailsViewText Details
{
get { ... }
set { ... }
}
}
The second option (extra DataContext-like property) can be realized by sub-classing the ListBox and adding another property.
Why not do this ?
<ListBox ItemsSource="{Binding ElementName=<TextBox's Name>, Path=DataContext">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=StringP}" Background="{Binding Path=SelectedItemP, Converter={StaticResource c_SelectedItemToBackgroundConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Correct me if I'm wrong with understanding your question. You want to bind the listbox's itemssource to the textbox's datacontext?
I have MVVM master /details like this:
<Window.Resources>
<DataTemplate DataType="{x:Type model:EveryDay}">
<views:EveryDayView/>
</DataTemplate>
<DataTemplate DataType="{x:Type model:EveryMonth}">
<views:EveryMonthView/>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Margin="12,24,0,35" Name="schedules"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Elements}"
SelectedItem="{Binding Path=CurrentElement}"
DisplayMemberPath="Name" HorizontalAlignment="Left" Width="120"/>
<ContentControl Margin="168,86,32,35" Name="contentControl1"
Content="{Binding Path=CurrentElement.Schedule}" />
<ComboBox Height="23" Margin="188,24,51,0" Name="comboBox1"
VerticalAlignment="Top"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Schedules}"
SelectedItem="{Binding Path=CurrentElement.Schedule}"
DisplayMemberPath="Name"
SelectedValuePath="ID"
SelectedValue="{Binding Path=CurrentElement.Schedule.ID}"/>
</Grid>
This Window has DataContext class:
public class MainViewModel : INotifyPropertyChanged {
public MainViewModel() {
elements.Add(new Element("first", new EveryDay("First EveryDay object")));
elements.Add(new Element("second", new EveryMonth("Every Month object")));
elements.Add(new Element("third", new EveryDay("Second EveryDay object")));
schedules.Add(new EveryDay());
schedules.Add(new EveryMonth());
}
private ObservableCollection<ScheduleBase> _schedules = new
ObservableCollection<ScheduleBase>();
public ObservableCollection<ScheduleBase> Schedules {
get {
return _schedules;
}
set {
schedules = value;
this.OnPropertyChanged("Schedules");
}
}
private Element _currentElement = null;
public Element CurrentElement {
get {
return this._currentElement;
}
set {
this._currentElement = value;
this.OnPropertyChanged("CurrentElement");
}
}
private ObservableCollection<Element> _elements = new
ObservableCollection<Element>();
public ObservableCollection<Element> Elements {
get {
return _elements;
}
set {
elements = value;
this.OnPropertyChanged("Elements");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
One of Views:
<UserControl x:Class="Views.EveryDayView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid >
<GroupBox Header="Every Day Data" Name="groupBox1" VerticalAlignment="Top">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBox Name="textBox2" Text="{Binding Path=AnyDayData}" />
</Grid>
</GroupBox>
</Grid>
My SelectedItem in ComboBox doesn't works correctly. Are there any visible errors in my code?
What I usually do is bind the items of an ItemsControl to an ICollectionView (usually ListCollectionView) instead of directly to a collection; I think that's what the ItemsControl does by default anyway (creates a default ICollectionView), but I might be wrong.
Anyway, this allows you to work with the CurrentItem property of the ICollectionView, which is automatically synchronized with the selected item in an ItemsControl (if the IsSynchronizedWithCurrentItem property of the control is true or null/default). Then, when you need the current item in the ViewModel, you can use that instead. You can also set the selected item by using the MoveCurrentTo... methods on the ICollectionView.
But as I re-read the question I realize you may have another problem altogether; you have a collection of 'default' items and need a way to match them to specific instances. It would however be a bad idea to override the equality operators of the objects to consider them always equal if they are of the same type, since that has the potential to make other code very confusing. I would consider extracting the type information into an enum, and put a read-only property on each object returning one of the enum values. Then you can bind the items to a collection of the enum values, and the selected item to the enum property of each object.
Let me know if you need an example, I may have made a mess of the explanation :)