I'm working in MVVM, WPF and I have a popup; inside this popup is a listbox and inside the listbox I have a checkbox. The problem is: if I uncheck an item from the list box and click outside, popup disappears; if a I click again the checkbox is reseted at its initial value (all the items become checked).
So, how can I maintain the state of the popup set and stop its resetting while the app is running ? Can I do this through XAML ?
here is the code:
public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isChecked = false;
private T item;
public CheckedListItem()
{ }
public CheckedListItem(T item, bool isChecked)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
the viewModel:
private void OnApplyFiltersCommandRaised(object obj)
{
if (FilterElement.Contains("ClassView"))
{
switch (FilterElement)
{
case "buttonClassViewClassFilter":
FilteredClassViewItems.Clear();
FilteredFieldViewItems.Clear();
foreach (var filterItem in FilterItems)
{
if (filterItem.IsChecked == true)
{
FilteredClassViewItems.Add(classViewItems.First(c => c.ClassName == filterItem.Item));
FilteredFieldViewItems.Add(fieldViewItems.First(c => c.ClassName == filterItem.Item));
}
}
break;
...
public ObservableCollection<CheckedListItem<string>> FilterItems
{
get
{
return filterItems;
}
set
{
filterItems = value;
SetPropertyChanged("FilterItems");
}
}
the XAML part:
<ListBox x:Name="listBoxPopupContent"
Height="250"
ItemsSource="{Binding FilterItems}"
BorderThickness="0"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="FontSize" Value="8" />
<Setter Property="IsSelected" Value="{Binding IsChecked, Mode=TwoWay}" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Item}"
Command="{Binding DataContext.ApplyFiltersCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBox}}}"
CommandParameter="{Binding IsChecked,
RelativeSource={RelativeSource Self},
Mode=OneWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks in advance !
If you want to keep the state, you can just create a new view that will contain your listbox. Then your popup will be
<Popup>
<views:MyListBoxview>
</Popup>
where views is the path where wpf can find MyListBoxview.
This is an example of how you can do MyListBoxView. First of all, add a new usercontrol to your project. Then you create:
<ListBox ItemSource = {Binding MyCollectionOfItem}>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked = {Binding IsItemChecked} Content = {Binding Name}/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You will need to assign to this view a viewmodel that will of course implement INotifyPropertyChanged and that will have these this class defined inside it (also this class will implement INotifyPropertyChanged)
public class MyItem : INotifyPropertyChanged
{
public void SetPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private bool isItemChecked = false;
public bool IsItemChecked
{
get { return isItemChecked; }
set
{
isItemChecked = value;
SetPropertyChanged("IsItemChecked");
}
}
private string name ;
public string Name
{
get { return Name; }
set
{
name = value;
SetPropertyChanged("Name");
}
}
}
finally, the viewmodel that will represent the state of the popup will have inside this property
private ObservableCollection<MyItem> myCollectionOfItem = new ObservableCollection<MyItem>();
public ObservableCollection<MyItem> MyCollectionOfItem
{
get { return myCollectionOfItem; }
set
{
myCollectionOfItem = value;
SetPropertyChanged("MyCollectionOfItem");
}
}
I usually handle this kind of problem modelling properly the object that i need to bind to my controls in WPF
Related
I have a problem with IsSelected property. It doesn't send values from view to view-model. I posted my code below
ViewModel:
public class Viewmodel : INotifyPropertyChanged
{
private ObservableCollection<int> seznam;
public ObservableCollection<int> Seznam
{
get { return seznam; }
set
{
seznam = value;
}
}
public Viewmodel()
{
Seznam = new ObservableCollection<int>();
for (int i = 0; i < 3; i++)
{
Seznam.Add(i);
}
}
bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
View:
<TreeView ItemsSource="{Binding Seznam}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
It still doesn't stop at breakpoint that I put on get { return isSelected; }
With your updated post, it is clear that you haven't implemented your view models correctly. In particular, your TreeView.ItemsSource is bound to the Seznam property of your only view model. This is a collection of int values.
This means that the data context for each item container in the TreeView, where you attempt to bind to the IsSelected property, is an int value. And of course, int values don't even have an IsSelected property.
(By the way, I am skeptical about your claim that "There are no binding errors". If you looked at the debug output, you certainly should have seen a binding error, where attempting to bind to the non-existent IsSelected property.)
And think about this for a moment: supposing the item container did manage to bind to the Viewmodel.IsSelected property. How many item containers do you think there are? And how many instances of Viewmodel do you think there are? You should believe that there are many item containers, i.e. one for each item in your collection. And that there is just one instance of Viewmodel. So, how would all those items' selection state even map to the single Viewmodel.IsSelected property?
The right way to do this would be to create a separate view model object for the collection, with a property for your int value, as well as properties for the IsSelected and IsExpanded states (since you originally had mentioned wanting both).
Here is the example I wrote earlier just to prove to myself that the usual approach would work as expected. You should not have any trouble adapting it to suit your needs…
Per-item view model:
class TreeItemViewModel : NotifyPropertyChangedBase
{
public ObservableCollection<TreeItemViewModel> Items { get; }
= new ObservableCollection<TreeItemViewModel>();
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set { _UpdateField(ref _isSelected, value, _OnBoolPropertyChanged); }
}
private bool _isExpanded;
public bool IsExpanded
{
get { return _isExpanded; }
set { _UpdateField(ref _isExpanded, value, _OnBoolPropertyChanged); }
}
private void _OnBoolPropertyChanged(bool obj)
{
_RaisePropertyChanged(nameof(FullText));
}
private string _text;
public string Text
{
get { return _text; }
set { _UpdateField(ref _text, value, _OnTextChanged); }
}
private void _OnTextChanged(string obj)
{
_RaisePropertyChanged(nameof(FullText));
}
public string FullText
{
get { return $"{Text} (IsSelected: {IsSelected}, IsExpanded: {IsExpanded})"; }
}
}
Main view model for window:
class MainViewModel : NotifyPropertyChangedBase
{
public ObservableCollection<TreeItemViewModel> Items { get; }
= new ObservableCollection<TreeItemViewModel>();
public ICommand ClearSelection { get; }
public MainViewModel()
{
ClearSelection = new ClearSelectionCommand(this);
}
class ClearSelectionCommand : ICommand
{
private readonly MainViewModel _parent;
public ClearSelectionCommand(MainViewModel parent)
{
_parent = parent;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged;
#pragma warning restore 67
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_parent._ClearSelection();
}
}
private void _ClearSelection()
{
_ClearSelection(Items);
}
private static void _ClearSelection(IEnumerable<TreeItemViewModel> collection)
{
foreach (TreeItemViewModel item in collection)
{
_ClearSelection(item.Items);
item.IsSelected = false;
item.IsExpanded = false;
}
}
}
XAML for window:
<Window x:Class="TestSO44513864TreeViewIsSelected.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO44513864TreeViewIsSelected"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:MainViewModel>
<l:MainViewModel.Items>
<l:TreeItemViewModel Text="One">
<l:TreeItemViewModel.Items>
<l:TreeItemViewModel Text="One A"/>
<l:TreeItemViewModel Text="One B"/>
</l:TreeItemViewModel.Items>
</l:TreeItemViewModel>
<l:TreeItemViewModel Text="Two"/>
<l:TreeItemViewModel Text="Three"/>
</l:MainViewModel.Items>
</l:MainViewModel>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content="Clear Selection" Command="{Binding ClearSelection}"
HorizontalAlignment="Left"/>
<TreeView ItemsSource="{Binding Items}" Grid.Row="1">
<TreeView.ItemContainerStyle>
<p:Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
</p:Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="l:TreeItemViewModel"
ItemsSource="{Binding Items}">
<TextBlock Text="{Binding FullText}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
And for completeness…
The boilerplate base class for INotifyPropertyChanged implementation:
class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
_RaisePropertyChanged(propertyName);
}
protected void _RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Instead of using the IsSelected on every node in the tree, use the TreeView.SelectedItem on the TreeView itself. From here you can bind, but the property is readonly.
I have a DataGrid and a ListView in my application. The ListView provides information about the datagrid's selected item. I put a HyperLink in that ListView that should change the datagrid's selected item to the "parent" of the currently selected item.
My approach is to set SelectedItemin my code behind. Everything works quite well but the datagrid doesn't highlight the new selected item. But I can clearly see it is selected because off its gray background color. Is it possible to set the highlighted cell programmatically?
<ListView>
<TextBlock Text="{Binding SelectedParameter.Definition.Name, StringFormat=Name: {0:C}}"
TextWrapping="Wrap"/>
<TextBlock Text="{Binding SelectedParameter.Definition.Type, StringFormat=Datentyp: {0:C}}"
TextWrapping="Wrap"/>
TextWrapping="Wrap"/>
<Hyperlink Command="{Binding GoToMasterParameterCommand}">
Masterparameter
</Hyperlink>
</ListView>
<DataGrid Name="m_DataGrid"
ItemsSource="{Binding Path=Parameters}"
SelectedItem="{Binding SelectedParameter}"
SelectionMode="Single"
AutoGenerateColumns="False"
TargetUpdated="m_ParameterDataGrid_TargetUpdated">
<DataGrid.Columns>
<DataGridTextColumn
Header="ID"
Binding="{Binding Id}"
IsReadOnly="True"/>
<DataGridTextColumn
Header="Value"
Binding="{Binding Value.CurrentInternalValue, NotifyOnTargetUpdated=True}"
</DataGrid.Columns>
</DataGrid>
internal void GoToMasterParameter()
{
string parentId = GetParentId(this.SelectedParameter);
this.SelectedParameter = this.Parameters.Single(item => item.Id == parentId);
}
The problem you have is that the selected rows/cells are not focused as the focus is still in your listView item. What you could do is style the DataGridCell element in xaml. Here is a small piece of code that demonstrate this:
<Window.Resources>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<DataGrid ItemsSource="{Binding Tests}"
SelectedItem="{Binding GridSelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="{Binding SelectedGridIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</DataGrid>
<Button Command="{Binding ChangeSelectedItemCommand}"
Content="Change Grid Selected item"
Grid.Column="1"
VerticalAlignment="Top"/>
</Grid>
and here the viewModel part:
public class MainWindowViewModel : INotifyPropertyChanged
{
#region Private members
private List<TestClass> _tests;
private TestClass _gridSelectedItem;
private ICommand _changeSelectedItemCommand;
private int _selectedGridIndex;
#endregion
#region Constructor
public MainWindowViewModel()
{
Tests = new List<TestClass>();
for (int i = 0; i < 25; i++)
{
TestClass testClass= new TestClass {Name = "Name " + i, Title = "Title" + i};
Tests.Add(testClass);
}
}
#endregion
#region Public properties
public List<TestClass> Tests
{
get { return _tests; }
set
{
_tests = value;
OnPropertyChanged("Tests");
}
}
public TestClass GridSelectedItem
{
get { return _gridSelectedItem; }
set
{
_gridSelectedItem = value;
OnPropertyChanged("GridSelectedItem");
}
}
public int SelectedGridIndex
{
get { return _selectedGridIndex; }
set
{
_selectedGridIndex = value;
OnPropertyChanged("SelectedGridIndex");
}
}
#endregion
public ICommand ChangeSelectedItemCommand
{
get { return _changeSelectedItemCommand ?? (_changeSelectedItemCommand = new SimpleCommand(p => ChangeSelectedGridItem())); }
}
private void ChangeSelectedGridItem()
{
SelectedGridIndex++;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
the demo objects class:
public class TestClass
{
public string Title { get; set; }
public string Name { get; set; }
}
and some command class:
public class SimpleCommand : ICommand
{
private readonly Predicate<object> _canExecuteDelegate;
private readonly Action<object> _executeDelegate;
#region Constructors
public SimpleCommand(Action<object> execute)
: this(execute, null)
{
}
public SimpleCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
_executeDelegate = execute;
_canExecuteDelegate = canExecute;
}
#endregion // Constructors
#region ICommand Members
public virtual bool CanExecute(object parameter)
{
return _canExecuteDelegate == null || _canExecuteDelegate(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_executeDelegate(parameter);
}
#endregion
}
Make sure to add the DataContext of the View so that it knows about your ViewModel:
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
I hope this helps you obtain the desired effect.
I'm trying to bind the IsSelected property of a ListViewItem to a property in a ViewModel. It works fine in WPF, but in Windows RT the IsSelected property is never getting set.
public class Item : INotifyPropertyChanged
{
private readonly string name;
private bool isSelected;
public event PropertyChangedEventHandler PropertyChanged;
public bool IsSelected
{
get { return isSelected; }
set { isSelected = value; RaisePropertyChanged("IsSelected"); }
}
public string Name { get { return name; } }
public Item(string name)
{
this.name = name;
}
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ViewModel
{
private readonly ObservableCollection<Item> items = new ObservableCollection<Item>(Enumerable.Range(0, 10).Select(p => new Item(p.ToString())));
public ObservableCollection<Item> Items { get { return items; } }
}
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataContext = new ViewModel();
}
}
xaml:
<StackPanel Orientation="Horizontal">
<ListView ItemsSource="{Binding Path=Items}" SelectionMode="Multiple">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</StackPanel>
I'm able to click on the items on the screen, but the IsSelected property isn't propagating to the ViewModel. Any ideas why?
A good and easy way to do this is to subclass ListView
public class MyListView : ListView
{
protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
// ...
ListViewItem listItem = element as ListViewItem;
Binding binding = new Binding();
binding.Mode = BindingMode.TwoWay;
binding.Source = item;
binding.Path = new PropertyPath("Selected");
listItem.SetBinding(ListViewItem.IsSelectedProperty, binding);
}
}
Alternatively, it seems you can also do it with WinRT XAML Toolkit.
<ListView
x:Name="lv"
Grid.Row="1"
Grid.Column="1"
SelectionMode="Multiple"
HorizontalAlignment="Left"
Width="500">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock
Extensions:ListViewItemExtensions.IsSelected="{Binding IsSelected}"
Extensions:ListViewItemExtensions.IsEnabled="{Binding IsEnabled}"
Text="{Binding Text}"
Margin="15,5"
FontSize="36" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Personally, I used the first approach because it's more flexible and I needed to bind a few Automation Properties.
Credits to ForInfo and ehuna:
http://social.msdn.microsoft.com/Forums/windowsapps/en-US/9a0adf35-fdad-4419-9a34-a9dac052a2e3/listviewitemisselected-data-binding-in-style-setter-is-not-working
WinRT doesn't support bindings in setters at all as of Windows 8.0. Bing for workarounds.
I'm really new to WPF so apologies in adavnced if this is an obvious question. I have a simple Checkbox in XAML as
<ListBox ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding Selections}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid >
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Path=Item.SelectionName}" />
</Grid >
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Simplified code behind to allow bindings and INotifyPropertyChanged is:
public ObservableCollection<CheckedListItem<Selection>> Selections { get; set; }
public class Selection
{
public String SelectionName { get; set; }
}
Selections = new ObservableCollection<CheckedListItem<Selection>>();
Selections.Add(new CheckedListItem<Selection>(new Selection()
{ SelectionName = "SomeName" }, isChecked: true));
public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isChecked;
private T item;
public CheckedListItem()
{ }
public CheckedListItem(T item, bool isChecked = false)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
I now need to add an additional TextBox associated with each Checkbox, so in XAML I have
<ListBox ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding Selections}" Margin="12,22,12,94">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid >
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Path=Item.SelectionName}" />
<<TextBox />
</Grid >
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'm a bit stumped how to include this as part of the ObservableCollection and set it up the binding on both the CheckBox and associated TextBox? Both are being added together using Selections.Add(new CheckedListItem<Selection>(new Selection()
{ SelectionName = "SomeName" }, isChecked: true)); which is causing me some confusion.
EDIT: Added full code
public partial class SelectionSettingWindow : Window
{
public ObservableCollection<CheckedListItem<Selection>> Selections { get; set; }
public class Selection
{
public String SelectionName { get; set; }
public string SelectionTextField { get; set; }
}
public SelectionSettingWindow()
{
InitializeComponent();
Selections = new ObservableCollection<CheckedListItem<Selection>>();
string fg = #"Item1,true,TExtbox1text:Item2,true,TExtbox2text:Item3,false,TExtbox3text"; //Test String
string[] splitSelections = fg.Split(':');
foreach (string item in splitSelections)
{
string[] spSelectionSetting = item.Split(',');
bool bchecked = bool.Parse(spSelectionSetting[1].ToString());
string tbText = spSelectionSetting[2].ToString();
Selections.Add(new CheckedListItem<Selection>(new Selection()
{ SelectionName = spSelectionSetting[0].ToString(),
SelectionTextField = bText }, isChecked: bchecked));
}
DataContext = this;
}
public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isChecked;
private T item;
private string textField;
public CheckedListItem()
{ }
public CheckedListItem(T item, bool isChecked = false)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
public string TextField
{
get { return textField; }
set
{
textField = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("TextField"));
}
}
}
}
<ListBox ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding Selections}" Margin="12,22,12,94">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}"
Content="{Binding Path=Item.SelectionName}" />
<TextBox Text="{Binding Item.SelectionTextField, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
replace SelectionTextField above with whatever the field is that needs to be edited using the textbox on your Selection class.
Note that I changed the <Grid> to a <StackPanel> So they wouldn't appear on top of eachother and changed the bindings to TwoWay so the changes are reflected in the model.
Make sure your Selection class implements INotifyPropertyChanged (ObservableCollection updates the UI when things get added to/removed from the collection, it doesn't know anything about notifying when it's content's properties change so they need to do that on their own)
Implementing INotifyPropertyChanged on many classes can be cumbersome. I find implementing a base class useful for this. I've got this along with an extra reflection helper for raise property changed available here and a snippet I've made available. It's silverlight but it should work fine for WPF. Using the code I've provided via download you can simply type proprpc and hit tab and visual studio will stub in a property that notifies on change. Some explanation is in one of my old blog posts here and gives credit for where I based the code and snippet from.
I have the following ui items - one checkbox list and one checkbox with toggle all checkboxes in that list -
<DockPanel>
<CheckBox
Name="SelectCheckboxes"
Command="{Binding ToggleCheckBoxes}"
Content="Whatever"/>
</DockPanel>
<DockPanel>
<ListBox Name="MyListBox"
ItemsSource="{Binding Path=MyProperty, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="MyCheckBox"
Content="{Binding myvalue}"
Tag="{Binding mycode}"
IsChecked="{Binding Path=isChecked, Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
And here is the MyProperty property -
public ObservableCollection<SomeEntity> MyProperty
{
get { return _someEntities; }
set
{
if (value == _someEntities)
return;
_someEntities = value;
base.OnPropertyChanged("MyProperty");
}
}
And here is a command ToggleCheckBoxes -
public ICommand ToggleCheckBoxes
{
get
{
if (_toggleCheckBoxesCommand == null)
{
_toggleCheckBoxesCommand = new RelayCommand(
param => this.toggleCheckBoxes()
);
}
return _toggleCheckBoxesCommand;
}
}
void toggleCheckBoxes()
{
foreach (var i in MyProperty)
{
if (i.isChecked)
i.isChecked = false;
else
i.isChecked = true;
}
}
When I click on the checkbox to toggle the checkboxes, I can look at the property in the code and see that the isChecked property is changed, but the ListBox does not update to reflect that all items are checked/unchecked.
Does anyone see anything that I am missing that might cause the ListBox not to update?
Thnaks for any thoughts.
Make sure that your isChecked member is actually a property and that SomeEntity implements INotifyPropertyChanged. Something like:
public class SomeEntity : INotifyPropertyChanged {
private bool _isChecked;
public bool isChecked
{
get { return _isChecked; }
set
{
if (value == _isChecked)
return;
_isChecked= value;
this.NotifyPropertyChanged("isChecked");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}