I have a ComboBox bound to an ObservableCollection of objects (with several properties). The Combo Box accurately displays the desired property of all objects and I can select any item from the Combo as expected.
<ComboBox Height="23" Name="comboBox1" Width="120" Margin="5" ItemsSource="{Binding Issues}" DisplayMemberPath="Issue" SelectedValuePath="Issue" SelectedValue="{Binding Path=Issues}" IsEditable="False" SelectionChanged="comboBox1_SelectionChanged" LostFocus="comboBox1_LostFocus" KeyUp="comboBox1_KeyUp" Loaded="comboBox1_Loaded" DropDownClosed="comboBox1_DropDownClosed" IsSynchronizedWithCurrentItem="True" />
I have a series of text boxes which are supposed to display other properties of the selected object. This works fine too.
<TextBox Height="23" Name="textBox5" Width="59" IsReadOnly="True" Text="{Binding Issues/LastSale, StringFormat={}{0:N4}}" />
<TextBox Height="23" Name="textBox9" Width="90" IsReadOnly="True" Text="{Binding Path=Issues/LastUpdate, Converter={StaticResource TimeConverter}}" />
BUT... The properties of ObservableCollection are updated in the Code-Behind on a regular basis and I make a change to the OC by either adding or removing a dummy object in it every time the properties are updated. (I found this simpler than other solutions).
BUT...the data in the TextBoxes DO NOT change! :-( If I select a different object from the ComboBox I get updated info, but it does not change when the OC is changed.
The OC is composed of a bunch of these Objects:
public class IssuesItems
{
public String Issue { get; set; }
public Double LastSale { get; set; }
public DateTime LastUpdate { get; set; }
...
}
The OC is defined as:
public ObservableCollection<IssuesItems> Issues { get; set; }
and instantiated:
this.Issues = new ObservableCollection<IssuesItems>();
What am I doing wrong? Everything I read says that when the LastSale and LastUpdate properties are changed in the OC (and I do something to force an update of the OC) the data in the text boxes ought to change.
ObservableCollection implements INotifyCollectionChanged which allows GUI to refresh when any item is added or deleted from collection (you need not to worry about doing it manually).
But like i mentioned this is restricted to only addition/deletion of items from collection but if you want GUI to refresh when any underlying property gets changed, your underlying source class must implement INotifyPropertyChanged to give notification to GUI that property has changed so refresh yourself.
IssuesItems should implement INPC interface in your case.
Refer to this - How to implement INotifyPropertyChanged on class.
public class IssuesItems : INotifyPropertyChanged
{
private string issue;
public string Issue
{
get { return issue; }
set
{
if(issue != value)
{
issue= value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Issue");
}
}
}
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Implement other properties just like Issue as mentioned above.
Related
My ComboBox does not get populated with data.
Class Employee set to public, has variables such as:
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
Code on UserControl:
public IEnumerable<csEmployee> employeeList;
public ObservableCollection<csEmployee> _employeeSorted { get; set; }
public ucAddClient()
{
InitializeComponent();
//Establish connection
var GetMyData = new DataAccess();
//Get data by procedure
employeeList = GetMyDataPV.ExecuteStoredProc<csEmployee>("procedure", new {KeyDate = Key_to_extract});
employeeList = employeeList.Where(record => record.EmployeeLevelID > 300);
_employeeSorted = new ObservableCollection<csEmployee>(employeeList.Where(record => record != null));
}
And WPF:
<ComboBox x:Name="cbAddManager"
Foreground="#FF4D648B"
FontSize="12"
IsEditable="True"
ItemsSource="{Binding _employeeSorted}"
DisplayMemberPath="FirstName"
PreviewKeyDown="cbAddManager_PreviewKeyDown"
Width="200">
<!--<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width ="50" Text="{Binding LastName}"/>
<TextBlock Text=", "/>
<TextBlock Width ="50" Text="{Binding FirstName}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>-->
</ComboBox>
Do you have any idea, why ComboBoxis not populated? When I do this in code (I add it in user control class) it gets data needed.
Im not sure if Im binding it correctly?
That is because you assign a new instance of a collection to your _employeeSorted property after InitializeComponent. At that time, the binding is already set up and does not get notified that you have updated the property from null, because you do not implement INotifyPropertyChanged.
There are multiple ways to solve the issue:
Initialize the collection before InitializeComponent and work on this same collection if you intend to change it, using Clear and Add instead of creating a new instance on changes.
Implement the INotifyPropertyChanged interface and use it to notify changes to your property so that the bindings are updated the the changes are applied in the user interface, e.g.:
public partial class MyUserControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<csEmployee> _employeeSortedField;
public ObservableCollection<csEmployee> _employeeSorted
{
get => _employeeSortedField;
set
{
if (_employeeSortedField == value)
return;
_employeeSortedField = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Expose a depenedency property for the collection instead and bind it to a collection in your view model that is passed as data context of the UserControl, thus moving the data access out it and separating the view from the business logic and data (recommended, see below MVVM).
Another issue might be that you do not set your data context to the UserControl itself in XAML (which is not recommened by the way, although it might solve your issue). In this case, the binding is unable to resolve the property at runtime (a binding error will be shown in the output window).
<UserControl x:Class="YourProject.YourControl"
...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
As a note, it seems that you mix your business logic with your UserControl (view). Leverage the MVVM design pattern to create view models and seprate both concerns instead. Furthermore, if you set the data context of your UserControl to itself, you break data context inheritance.
I am very new to the concept of data binding and I don't think I understood it completely. I have a class named Project with a LinkedList of type ToDo as one of its properties. When I navigate to one instance of Project, I will display the LinkedList of type ToDo in a ListView. I have created functions that allow me to change the sequences of the nodes in the LinkedList (move up, move down) and to remove the selected node (delete). I want the ListView to refresh whenever there is a change in the LinkedList, (move up, move down or delete). However, I cannot achieve that. Here is my code: (not all parts are included)
XAML of the page:
<ListView x:Name="myListView" ItemsSource="{Binding Source={StaticResource ToDos}, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox x:Name="myCheckBox"
Content="{Binding ToDoTitle, Mode=TwoWay}"
IsChecked="{Binding IsCompleted, Mode=TwoWay}">
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C# for DataModel:
public class ToDo : INotifyPropertyChanged
{
private string toDoTitle;
private bool isCompleted;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public string ToDoTitle { get { return this.toDoTitle; } set { this.toDoTitle = value; this.OnPropertyChanged(); } }
public bool IsCompleted { get { return this.isCompleted; } set { this.isCompleted = value; this.OnPropertyChanged(); } }
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// Raise the PropertyChanged event, passing the name of the property whose value has changed.
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Projects : INotifyPropertyChanged
{
private LinkedList<ToDo> toDos;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public LinkedList<ToDo> ToDos { get { return this.toDos; } set { this.toDos = value; this.OnCollectionChanged(); } }
public Projects()
{
ToDos = new LinkedList<ToDo>();
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// Raise the PropertyChanged event, passing the name of the property whose value has changed.
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Thank you.
First I would advise you to read about MVVM, and try to follow some basic tutorials like this one.
You can use MVVM Light to avoid managing the INotifyPropertyChanged by yourself at first (but it's really good to know how MVVM light work under the hood).
To come back to your problem, your current code notifies only if you set the full ToDos list. If you want to be aware of any change in a list (seing when an item is add/remove/update), you are probably looking for an ObservableCollection, not a LinkedList.
Hope it helps.
I have a slight issue. I am making a calendar application with some listbox elements. Each calendar view retrieves it's "calendar events" from a dictionary where TKey = DateTime and TValue = ObservableCollection <CalendarEvent>. Now this works fine for any calendar day where there are predefined events already. I can data bind the listbox to a property that contains a reference to the dictionary entry of that particular calendar day. However another feature of my application should be the ability to add events during runtime. What I have done right now is, if there is no dictionary key present for that particular calendar day it just sets the Events property to null and then I change it during runtime if an event was added for that day, unfortunately it doesn't seem to like that, it doesn't "bind" properly or so to say.
Here is the code
public CalendarDayView(DateTime date)
{
DataContext = this;
Date = date;
Events = CalendarRepository.Instance.Entries.ContainsKey(date) ? CalendarRepository.Instance.Entries[date] : null;
}
public DateTime Date { get; set; }
public ObservableCollection<CalendarEvent> Events { get; set; }
/// <summary>
/// This method will set the listbox item source to the ObservableCollection if it hasn't been set already
/// </summary>
public void UpdateItemSource()
{
if (Events == null)
// This is the part that doesn't do anything unfortunately
Events = CalendarRepository.Instance.Entries[Date];
}
XAML markup
<ControlTemplate TargetType="{x:Type local:CalendarDayView}">
<Border BorderBrush="Gray" BorderThickness="0.2" Width="100" Height="100">
<Grid Name="contentGrid">
<ListBox
Name="entriesListBox" Background="LightYellow" FontSize="10"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Events}">
</ListBox>
<!-- Date display below -->
<TextBlock
Name="dateTextBlock" Text="{Binding Date, StringFormat={}{0:dd-MMM}, UpdateSourceTrigger=PropertyChanged}"
FontFamily="Segoe UI Light" FontSize="18" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="5"/>
</Grid>
</Border>
</ControlTemplate>
I don't see you raising the PropertyChanged event anywhere to notify the view of binding changes. You should implement INotifyPropertyChanged on the CalendarDayView model and raise the implemented PropertyChanged event in your property setters that are used as binding source (Events in this case).
The following code shows a simple example, but it might be better to add the PropertyChanged functionality to a base model class.
public class CalendarDayView : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<CalendarEvent> _events;
public ObservableCollection<CalendarEvent> Events
{
get { return _events; }
set
{
_events = value;
RaisePropertyChanged("Events");
}
}
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
In C#, how can a method be called when a property changes (both method and property belong to the same class)?
e.g.,
class BrowserViewModel
{
#region Properties
public List<TreeViewModel> Status { get; private set; }
public string Conditions { get; private set; }
#endregion // Properties
// i'd like to call this method when Status gets updated
void updateConditions
{
/* Conditions = something depending on the TreeViewItem select status */
}
}
Binding
<TreeView Grid.Row="1"
x:Name="StatusTree"
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
ItemsSource="{Binding Path=Status, Mode=OneTime}"
ItemTemplate="{StaticResource CheckBoxItemTemplate}"
/>
Use-Case (if you are curious)
The property Status is bound to a TreeView control in the xaml. When it is updated, I'd like to call a method that updates the property Conditions. This property is bound to a TextBox in the xaml.
I'm new to Eventing in C#, so am a little lost.
Edit
class TreeViewModel implements INotifyPropertyChanged.
Conditions is updated by getting the IsChecked Value from the TreeView.
The size of the Status List never changes. When a TreeViewItem is selected/unselected the TreeViewModel changes.
TreeViewModel source (FooViewModel on this page)
Binding code above.
Didn't have to change Binding Mode for IsChecked.
<HierarchicalDataTemplate
x:Key="CheckBoxItemTemplate"
ItemsSource="{Binding Children, Mode=OneTime}"
>
<StackPanel Orientation="Horizontal">
<!-- These elements are bound to a TreeViewModel object. -->
<CheckBox
Focusable="False"
IsChecked="{Binding IsChecked}"
VerticalAlignment="Center"
/>
<ContentPresenter
Content="{Binding Name, Mode=OneTime}"
Margin="2,0"
/>
</StackPanel>
</HierarchicalDataTemplate>
I assume you want updateConditions to fire whenever an item is added/removed/changed in your list, not if the list reference itself changes.
Since you're implementing INotifyPropertyChanged within your TreeViewModel, I think you'll want to use ObservableCollection<T> instead of a plain List<T>. Check it here: http://msdn.microsoft.com/en-us/library/ms668604.aspx
Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
class BrowserViewModel
{
#region Properties
public ObservableCollection<TreeViewModel> Status { get; private set; }
public string Conditions { get; private set; }
#endregion // Properties
// i'd like to call this method when Status gets updated
void updateConditions
{
/* Conditions = something */
}
public BrowserViewModel()
{
Status = new ObservableCollection<TreeViewModel>();
Status.CollectionChanged += (e, v) => updateConditions();
}
}
CollectionChanged will fire whenever an item is added/removed/changed. As far as I know, it will consider it "changed" when its reference changes or any of its properties are changed (which is notified through INotifyPropertyChanged)
Just checked it here: http://msdn.microsoft.com/en-us/library/ms653375.aspx
ObservableCollection.CollectionChanged Event
Occurs when an item is added, removed, changed, moved, or the entire list is refreshed.
ObservableCollection<T> resides in the System.Collections.ObjectModel namespace, in System.dll assembly.
Setup: I have a tabcontrol with 0...n tabs, and each tab is bound to a class:
class MyTabItem
{
string Text {get; set;}
int ID {get; set;}
ISet<MyTabContent> Contents {get; set;}
}
Class MyTabContent
{
int ID {get; set;}
string Subtext {get; set;}
}
Each tabitem class has many tabcontent classes in it's set. (The whole thing gets fetched via NHibernate).
I've tried a lot of things to bind the content of MyTabItem to the header of each tabcontrol item, and the content of MyTabContent to a datagrid in the content of each tabcontrol item.
I can display all the content in the tabs, but whenever I change properties in the bound classes, the UI does not update. I've tried to call InvalidateVisual, tried to Dispatch a Render event, tried to UpdateTarget and UpdateSource on the bindings (those last 2 threw exceptions). I've implemented INotifyPropertyChanged in my viewmodel, and even tried manually using OnPropertyChanged("MyTabItem") to no avail.
I really don't understand why my tabcontrol contents won't update when I change properties in the bound classes. I've tried 2 different binding strategies, either works in displaying the content, but neither updates when the content changes.
My XAML setup is:
<TabControl>
<TabControl.ItemTemplate>
<DataTemplate DataType="models:MyTabItem">
<TextBlock Text="{Binding Text}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="models:MyTabItem">
<DataGrid ItemsSource="{Binding Contents}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Subtext}" />
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
With that XAML setup, I simply added Items to the tabcontrol with tabcontrol.Items.Add(new MyTabItem).
Then I tried another XAML setup where I bound the tabcontrol.Itemsource to an ObservableCollection in the code-behind. Again, the tab content did not update if the bound properties change :(
I also tried making a CurrentItem property in the ViewModel, and used that as a Window.Resource, and then bound the Tabcontrol contents to
{Binding Source={StaticResource CurrentItem}, Path=Text}
And whenever I changed tabs I would update the CurrentItem in the viewmodel, but with that it also didn't update changes.
I'm pretty much out of ideas :(
You need to implement INotifyPropertyChanged
Keep in mind there's a new attribute in .NET 4.5 that simplifies the task, take a look here
Here's a sample, apply that to both your classes, the list will need to become ObservableCollection:
private ObservableCollection<MyTabContent> _contents = new ObservableCollection<MyTabContent>();
public ObservableCollection<MyTabContent> Contents { get { return _contents; } }
-
public class MyTabContent : INotifyPropertyChanged
{
private int _id;
int ID {
get{ return _id; }
set{ _id = value; OnPropertyChanged(); }
}
private string _subText;
public string Subtext {
get{ return _subText; }
set{ _subText= value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged!= null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}