I copied the basic method of having checkbox in a treeview from the official Silverlight toolkit Checkboxes in a TreeView example.
When a user clicks on a parent TreeViewItem I want all of the child items to be checked, as in the above example. This works fine when the parent is collapsed, clicking the checkbox puts a tick in the parent and when you expand the node all children have a tick in the checkbox.
However it doesn't work if the parent is expanded. None of the children are updated to have a tick in the checkbox, although the underlying data list is updated.
My XAML is as follows:
<sdk:HierarchicalDataTemplate x:Key="NodeTemplate" ItemsSource="{Binding Path=Contracts}">
<StackPanel Orientation="Horizontal" ToolTipService.ToolTip="{Binding Path=Name}">
<CheckBox IsTabStop="False" IsThreeState="{Binding Path=HasContracts}" IsChecked="{Binding Path=Selected, Mode=TwoWay}" Click="CheckBox_Click" />
<TextBlock Text="{Binding Path=Name}" Tag="{Binding Path=ID}"/>
</StackPanel>
</sdk:HierarchicalDataTemplate>
<sdk:TreeView x:Name="tvClientContract" ItemsSource="{Binding Path=ClientContracts, Mode=TwoWay}" ItemTemplate="{StaticResource NodeTemplate}"/>
This is bound to a List<ClientContract> and uses the same code behind as in the linked example.
The ClientContract object is:
public int ID { get; set; }
public string Name { get; set; }
public List<ClientContract> Contracts { get; set; }
public bool? Selected { get; set; }
How can I force the child to repaint itself as the underlying List<ClientContract> object is updated?
If you want to use INotifyPropertyChange(what I did instead of using ObservableCollection) here is how you do it per example on the ID element:
public class myclass : INotifyPropertyChanged
{
private int id_Value;
public int ID
{
get { return id_Value; }
set
{
id_Value = value;
NotifyPropertyChanged("ID");
}
}
public string Name { get; set; }
public List<ClientContract> Contracts { get; set; }
public bool? Selected { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
I hope this helps if it was what you were trying to do.
Try using ObservableCollection<ClientContract> instead of a List<>. Usually you want to databind to this collection type instead when the data is dynamic so it can notify the UI of collection changes.
Related
I am facing a problem while binding the double click event on Listbox item.
I am using MVVM and Prism. I didn't understand what is wrong here.
View
<ListBox HorizontalAlignment="Center" ItemsSource="{Binding Cities , Source={StaticResource vmC}}" SelectedItem="{Binding SelectedCity , Source={StaticResource vmC}}" Width="100" Height="200">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.ItemSelectedCommand}"/>
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ViewModel
public class CityViewModel : ViewModelBase
{
public DelegateCommand ItemSelectedCommand { get; private set; }
public string SelectedCity { get; set; }
public List<string> Cities { get; private set; }
public CityViewModel()
{
ItemSelectedCommand = new DelegateCommand(OnItemSelected);
Cities = new List<string>() { "Bangalore", "New York", "Sydney", "London", "Washington" };
}
private void OnItemSelected()
{
var city = SelectedCity;
}
}
MouseBinding is not part of the visual tree. This means, you cannot use Binding.RelativeSource as there is no tree to traverse.
Instead you must bind to the current DataContext, which is the item's data model. To accomplish this, you would have tzo introduce a model class e.g. City that exposes a Name and ItemSelectedCommand property.
If moving the command to the item model doesn't make sense in your scenario, you should use a RoutedCommand and handle it e.g., in the parent Window. InputBinding is primarily intended to be used in the view only and therefore encourages the use of RoutedCommand.
In your case, it seems you are only interested in notifying the CityViewModel that the selection has changed. In this case simply call OnItemSelected from the SelectedCity property set():
public class CityViewModel : ViewModelBase
{
private string selectedCity;
public string SelectedCity
{
get => selectedCity;
set
{
selectedCity = value;
OnItemSelected();
}
}
public List<string> Cities { get; private set; }
public CityViewModel()
{
...
}
private void OnItemSelected()
{
var city = SelectedCity;
}
}
Hey I have an ObservableCollection which consists of a class with two attributes (strings = User and Response) bound to a listbox.
I would like to have the users in the listbox first, which I add with this:
for (int i = 0; i < ArrStrUser.Length; i++)
{
Users.Add(new User() { input = ArrStrUser[i].Trim() });
}
I want to add the responses to the respective user later.
If I do this, they will be added to the ObservableCollection but not update in the listbox.
Users[i].response = strOutput.Trim().Replace(Environment.NewLine, " ");
The ObservableCollecton
private ObservableCollection<Input> Users = new ObservableCollection<Input>();
The Class:
public class Input
{
public string user{ get; set; }
public string response { get; set; }
}
XAML:
<ListBox x:Name="LBresponse" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" ItemTemplate="{StaticResource UserTemplate}" />
<DataTemplate x:Key="UserTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path= user}" Width="50"/>
<TextBlock Text="{Binding Path= response}" />
<Button Content="Delete" Click="DeleteUser_Clicked" HorizontalAlignment="Left"/>
</StackPanel>
</DataTemplate>
Simple solution
Your Input class needs to implement the INotifyPropertyChanged interface and invoke the PropertyChanged event upon changing property's value in order to update the ListBox. The ObservableCollection only "cares" about adding or removing items, it doesn't handle item's property changing.
Try editing your input class like this:
public class Input : INotifyPropertyChanged
{
public string user{ get; set; }
private string _response;
public string Response{
get => _response;
set {
_response = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now changing the Response property should update the UI.
Better solution
I'd also advise you to separate the INotifyPropertyChanged implementation into its own class if you want to use it somewhere else, too. Or better yet, use a library that already has it, like the mvvm-helpers nuget package by James Montemagno.
Here's a link to the INotifyPropertyChanged implementation from that library
This is how you use it:
public class Input : ObservableObject
{
public string user{ get; set; }
private string _response;
public string Response{
get => _response;
set => SetProperty(ref _response, value);
}
}
It also supports passing in an OnChanged Action and a validation function.
I am trying to change the selected item of a combo box based on a change in another combo box. The situation is complicated by the fact that both combo boxes appear in a list of item templates.
The XAML is as follows:
<ListBox ItemsSource="{Binding AncillaryExtendedPropertyViewModels}" ItemTemplateSelector="{StaticResource templateSelector}"/>
<DataTemplate x:Key="EnumDataTemplate"> <Grid Margin="4"
MinHeight="25"> <ComboBox SelectedItem="{Binding ExtendedPropertyEnum,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding ExtendedPropertyEnumList}"
DisplayMemberPath="Value"/> </Grid> </DataTemplate>
The data context of the view containing the XAML is set to AncillaryBaseViewModel. The following is a cut down version of AncillaryBaseViewModel.
public class AncillaryBaseViewModel : ComplexOrderItemViewModel, IDataErrorInfo
{
private ObservableCollection<ExtendedPropertyViewModel> _ancillaryExtendedPropertyViewModels;
public ObservableCollection<ExtendedPropertyViewModel> AncillaryExtendedPropertyViewModels
{
get { return _ancillaryExtendedPropertyViewModels; }
set
{
_ancillaryExtendedPropertyViewModels = value;
OnPropertyChanged("AncillaryExtendedPropertyViewModels");
}
}
and the ExtendedPropertyViewModel class....
public class ExtendedPropertyViewModel : DataTemplateSelector
{
private ExtendedProperty _extendedProperty;
public DataTemplate DefaultnDataTemplate { get; set; }
public DataTemplate BooleanDataTemplate { get; set; }
public DataTemplate EnumDataTemplate { get; set; }
public ExtendedPropertyEnum ExtendedPropertyEnum
{
get
{ return ExtendedProperty.ExtendedPropertyEnum; }
set
{
if (ExtendedProperty.ExtendedPropertyEnum != value)
{
_extendedProperty.ExtendedPropertyEnum = value;
AncillaryBaseViewModel parent = RequestParent();
if (parent != null)
{
parent.AncillaryExtendedPropertyViewModels[7].ExtendedPropertyEnum =
ExtendedProperty.ExtendedPropertyEnum.DependentExtendedPropertyEnums[0];
}
parent.OrderItem.SetStockCode();
PropertyChanged += parent.OnExtendedPropertyChanged;
OnPropertyChanged();
}
}
}
and the ExtendedProperty class....
public class ExtendedProperty : ViewModelBase
{
private ExtendedPropertyEnum _extendedPropertyEnum;
public int ExtendedPropertyID { get; set; }
public int OrderItemTypeID { get; set; }
public string DisplayName {get; set;}
public ObservableCollection<ExtendedPropertyEnum> ExtendedPropertyEnumList { get; set; }
public string Value { get; set; }
public ExtendedPropertyEnum ExtendedPropertyEnum
{
get
{
return _extendedPropertyEnum;
}
set
{
_extendedPropertyEnum = value;
OnPropertyChanged("ExtendedPropertyEnum");
}
}
}
To summarise, when I change the value of combo box A through the UI, this calls the ExtendedPropertyEnum setter within ExtendedPropertyViewModel, which changes the ExtendedPropertyEnum bound to another combo box B, which is in the same list. I would expect this to update the value displayed in combo box B accordingly, which it does not.
As an aside, changing the value of combo box A does update a label that is not within a data template. The XAML for this label is....
<Label Content="{Binding StockCode}" MinWidth="100"/>
This is updated by the following code within AncillaryBaseViewModel....
public void OnExtendedPropertyChanged(object sender, EventArgs args)
{
OnPropertyChanged("StockCode");
}
I thought I could change this to the following to force the combo boxes in the list to update.
public void OnExtendedPropertyChanged(object sender, EventArgs args)
{
OnPropertyChanged("StockCode");
OnPropertyChanged("AncillaryExtendedPropertyViewModels");
}
However, this did not work either.
Any help greatly appreciated.
Roger.
if I understand the question correctly then you are expecting a changed value within an observablecollection to be reflected within the UI. This will not happen. observablecollections raise notifyproperty events when the collection itself changes, not when values within them change. You'll either need to raise a notifyproperty event on the value changing or remove the item from the list and add it back with a new value.
I'm trying to make use of EntityFramework and WPF data binding for the first time.
I have some ListBox. I have set ItemsSource to SomeDbContext.SomeEntity.ToList(); programatically and I have set my binding like this:
<ListBox Name="listbox" Margin="4" SelectedValuePath="Address" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Id}"></Label>
<Label Content="{Binding Address}"></Label>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Click="ButtonTest_Click">Open</Button>
I'm adding new item to my DbSet and I expected that my list will refresh after SomeDbContext.SaveChanges(); method call, but it didn't.
My Window code behind:
DatabaseContext _dbContext = new DatabaseContext();
public MainWindow()
{
InitializeComponent();
lb.ItemsSource = _dbContext.Addresses.ToList();
// I have tried to set source to _dbContext
}
private void ButtonTest_Click(object sender, RoutedEventArgs e)
{
_dbContext.Addresses.Add(new Adresses() { Address = "192.168.1.2:502" });
_dbContext.SaveChanges();
}
Here is my Entity:
public class Adresses
{
public int Id { get; set; }
public string Address { get; set; }
}
My DbContext:
public class DatabaseContext : DbContext
{
public DbSet<Adresses> Addresses { get; set; }
}
What I am doing wrong?
I guess that my approach is wrong, because I'm creating new object when I'm setting ItemsSource to _dbContext.Addresses.ToList();, but I have no idea how to bind directly to my DbSet (or is it possible).
I had a listbox of user controls and each usercontrol displays properties of the bounded data and bind a collection to custom control when the custom control changes the data the UI not updated my code is as follows :
ObservableCollection<Subscription> subscriptions = new ObservableCollection<Subscription> SubscriptionRepository.GetSubscriptions());
SubListBox.ItemsSource = subscriptions;
xaml :
<DataTemplate x:Key="MyDataTemplate">
<UserControls:SubscriptionUC />
</DataTemplate>
<ListBox x:Name="SubListBox" ItemTemplate="{StaticResource MyDataTemplate}">
</ListBox>
user control :
<TextBlock Text="{Binding Path=DaysAttended}" />
<cc:CustomControl SubscriptionSource="{Binding Path=SubscriptionDays,Mode=TwoWay}" />
Subscription class :
public class Subscription : INotifyPropertyChanged
{
public int SubscriptionTypeId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public ObservableCollection<SubscriptionDay> SubscriptionDays { get; set; }
public int DaysAttended { get { return SubscriptionDays.Count(d => d.Attended == true); } }
public void DayChanged()
{
RaisePropertyChanged("SubscriptionDays");
RaisePropertyChanged("DaysAttended");
}
}
DayChanged() is called from SubscriptionDay class when SubscriptionDay property changed and it is called but DaysAttended not updated.
You also need to have setter for DaysAttended property,For Two Way binding it is must that your property should have getter and setter .I hope this will help.