Associate a list with observable color

I need help to properly bind ObservableCollection to xaml. I can bind data correctly, but when the data changed, the changes were not reflected on the screen. I read related blogs and seemed to understand, but trying to apply what I know into my own pattern, it doesn't work the way I thought.

I have 2 classes of Fruits and Fruit, where Fruits is an observable fruit team that implements INotifyPropertyChanged.

namespace TestCommand.Models { public class Fruit:INotifyPropertyChanged { private string _fruitname; public string FruitName { get { return _fruitname; } set { if (_fruitname!=value) { _fruitname = value; OnPropertyChanged("FruitName"); } } } private string _fruitcolor; public string FruitColor { get { return _fruitcolor; } set { if (_fruitcolor != value) { _fruitcolor = value; OnPropertyChanged("FruitColor"); } } } private bool _selected; public bool bSelected { get { return _selected; } set { if (_selected != value) { _selected = value; OnPropertyChanged("bSelected"); } } } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string name) { if (PropertyChanged!=null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } } } namespace TestCommand.Models { public class Fruits { private static ObservableCollection<Fruit> _fruitList; public static void Add(string f, string c) { _fruitList.Add(new Fruit { FruitName = f, FruitColor = c, bSelected = false }); } static Fruits() { _fruitList = new ObservableCollection<Fruit>(); _fruitList.Add(new Fruit { FruitName = "Mango", FruitColor = "Yellow", bSelected = false }); _fruitList.Add(new Fruit { FruitName = "Mango", FruitColor = "Yellow", bSelected = false }); _fruitList.Add(new Fruit { FruitName = "Water Melon", FruitColor = "Green", bSelected = false }); _fruitList.Add(new Fruit { FruitName = "Apple", FruitColor = "Red", bSelected = false }); _fruitList.Add(new Fruit { FruitName = "Banana", FruitColor = "Yellow", bSelected = false }); _fruitList.Add(new Fruit { FruitName = "Orange", FruitColor = "Orange", bSelected = false }); } public static ObservableCollection<Fruit> getAllFruit(bool bSelected = false) { var result = (bSelected ? _fruitList.Where(x => x.bSelected = true).ToList<Fruit>() : _fruitList.ToList<Fruit>()); return new ObservableCollection<Fruit>(result); } } } 

My xaml:

 <Window x:Class="TestCommand.MainWindow" xmlns="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:local="clr-namespace:TestCommand" xmlns:MyCommands='clr-namespace:TestCommand.Commands' mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <StackPanel Orientation='Vertical' Margin='10'> <ListBox x:Name='MyList' ItemTemplate='{StaticResource FruitTemp}'> </ListBox> <Button x:Name='AddFruit' Height='auto' Width='auto' Content='Add New Fruit 2' Margin='0,10,0,0' Command='{x:Static MyCommands:TestButtonCommand.AddFruit}'> <Button.CommandBindings> <CommandBinding Command='{x:Static MyCommands:TestButtonCommand.AddFruit}' Executed='CommandBinding_Executed' CanExecute='CommandBinding_CanExecute' /> </Button.CommandBindings> </Button> </StackPanel> </Window> 

and the code behind:

 namespace TestCommand { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); MyList.ItemsSource = Fruits.getAllFruit(); } private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { Fruits.Add("Durian", "Green"); } private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; } } } 

My ItemTemplate

 <Application x:Class="TestCommand.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:TestCommand" StartupUri="MainWindow.xaml"> <Application.Resources> <DataTemplate x:Key='FruitTemp'> <StackPanel Orientation='Horizontal' Margin='5'> <TextBlock x:Name='tbName' Text='{Binding FruitName}' Margin='10,0,0,0' Width='100'/> <TextBlock x:Name='tbColor' Text='{Binding FruitColor}' Margin='10,0,0,0' Width='100' /> <!--<CheckBox x:Name='cbSelected' Content='Selected' Checked='{Binding bSelected}' />--> </StackPanel> </DataTemplate> </Application.Resources> </Application> 

When I clicked on the button, I saw an item added to the collection, but the collection was not updated in the list. I must not have bound the collection correctly or maybe missed something because I am very new to wpf.

Your help to point out in my opinion is greatly appreciated.

0
source share
1 answer

This method creates a new ObservableCollection and returns it. You assign it to MyList.ItemsSource (this is not a binding, it is just an assignment) and then you add the element to another ObservableCollection somewhere else.

 MyList.ItemsSource = Fruits.getAllFruit(); 

...

 public static ObservableCollection<Fruit> getAllFruit(bool bSelected = false) { var result = (bSelected ? _fruitList.Where(x => x.bSelected = true).ToList<Fruit>() : _fruitList.ToList<Fruit>()); return new ObservableCollection<Fruit>(result); } 

Of course, you do not see any new items in the copy of _fruitList that you passed to the ListBox .

ListBox must have the same actual collection object to which you add the objects.

 public static ObservableCollection<Fruit> getAllFruit(bool bSelected = false) { return _fruitList; } 

Oops, no filtering.

And this is not the right way to do this. Write a viewmodel with the public Fruits property that returns an ObservableCollection<Fruit> , and use CollectionViewSource in your XAML to filter. We can go through all this if necessary. You already know how to implement INotifyPropertyChanged so that you are on your way.

Update

I quickly rewrote the Fruits app as an MVVM thing. Usually I made the AddNewFruit command as a viewmodel property using the delegate command, but I did not want to write the delegate command class and paste it. What is here, but still OK.

ViewModels.cs

 using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows.Data; using System.Windows.Media; namespace Fruits.ViewModels { public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } } public class Fruit : ViewModelBase { public Fruit() { } public Fruit(string name, String clrString) { FruitName = name; // Parse colors like so: (Color)ColorConverter.ConvertFromString(clrString); FruitColor = clrString; } public Fruit(string name, Color clr) { FruitName = name; FruitColor = clr.ToString(); } private string _fruitname; public string FruitName { get { return _fruitname; } set { if (_fruitname != value) { _fruitname = value; OnPropertyChanged("FruitName"); } } } private String _fruitcolor; public String FruitColor { get { return _fruitcolor; } set { if (_fruitcolor != value) { _fruitcolor = value; OnPropertyChanged("FruitColor"); } } } private bool _isSelected = true; // NOTE: I renamed this property public bool IsSelected { get { return _isSelected; } set { if (_isSelected != value) { _isSelected = value; OnPropertyChanged("IsSelected"); } } } } #region MainViewModel Class public class MainViewModel : ViewModelBase { public MainViewModel() { Fruits = new ObservableCollection<Fruit>(); } #region ShowSelectedFruitOnly Property private bool _showSelectedFruitOnly = true; public bool ShowSelectedFruitOnly { get { return _showSelectedFruitOnly; } set { if (value != _showSelectedFruitOnly) { _showSelectedFruitOnly = value; FruitsView.Refresh(); OnPropertyChanged("ShowSelectedFruitOnly"); } } } #endregion ShowSelectedFruitOnly Property #region Add Methods public void AddNewFruit() { Fruits.Add(new Fruit(NewFruitName, NewFruitColor)); NewFruitName = ""; NewFruitColor = ""; } public void AddNewFruit(string name, string color) { Fruits.Add(new Fruit(name, color)); } public void AddNewFruit(string name, Color color) { Fruits.Add(new Fruit(name, color)); } #endregion Add Methods #region NewFruitName Property private String _newFruitName = default(String); public String NewFruitName { get { return _newFruitName; } set { if (value != _newFruitName) { _newFruitName = value; OnPropertyChanged("NewFruitName"); } } } #endregion NewFruitName Property #region NewFruitColor Property private String _newFruitColor = default(String); public String NewFruitColor { get { return _newFruitColor; } set { if (value != _newFruitColor) { _newFruitColor = value; OnPropertyChanged("NewFruitColor"); } } } #endregion NewFruitColor Property public ICollectionView FruitsView { get; private set; } #region Fruits Property private ObservableCollection<Fruit> _fruits; public ObservableCollection<Fruit> Fruits { get { return _fruits; } private set { if (value != _fruits) { _fruits = value; FruitsView = CollectionViewSource.GetDefaultView(Fruits); FruitsView.Filter = FruitFilterPredicate; FruitsView.Refresh(); OnPropertyChanged("Fruits"); } } } protected bool FruitFilterPredicate(Object o) { if (ShowSelectedFruitOnly) { return (o as Fruit).IsSelected; } return true; } #endregion Fruits Property } #endregion MainViewModel Class } 

App.xaml

 <Application x:Class="Fruits.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Fruits" StartupUri="MainWindow.xaml" > <Application.Resources> <Style x:Key="ColorSwatch" TargetType="ContentControl"> <Setter Property="Width" Value="24" /> <Setter Property="Height" Value="24" /> <Setter Property="IsTabStop" Value="false" /> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="Gray" StrokeThickness="1" > <Rectangle.Fill> <SolidColorBrush Color="{Binding}" /> </Rectangle.Fill> </Rectangle> </DataTemplate> </Setter.Value> </Setter> </Style> <DataTemplate x:Key='FruitTemp'> <StackPanel Orientation='Horizontal' Margin='5'> <TextBlock x:Name='tbName' Text='{Binding FruitName}' Margin='10,0,0,0' Width='100'/> <TextBlock x:Name='tbColor' Text='{Binding FruitColor}' Margin='10,0,0,0' Width='100' /> <ContentControl Width="16" Height="16" Style="{StaticResource ColorSwatch}" Content="{Binding FruitColor}" /> <!-- The problem here was you were trying to bind Checked, an event, instead if IsChecked, a bool? property. --> <CheckBox x:Name='cbSelected' Content='Selected' Margin='10,0,0,0' IsChecked='{Binding IsSelected}' /> </StackPanel> </DataTemplate> </Application.Resources> </Application> 

MainWindow.xaml

 <Window x:Class="Fruits.MainWindow" xmlns="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:local="clr-namespace:Fruits" xmlns:sys="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525" > <Window.Resources> <RoutedCommand x:Key="AddFruit" /> </Window.Resources> <Window.CommandBindings> <CommandBinding Command='{StaticResource AddFruit}' Executed='AddFruitCommandBinding_Executed' CanExecute='AddFruitCommandBinding_CanExecute' /> </Window.CommandBindings> <Grid> <StackPanel Orientation='Vertical' Margin='10'> <CheckBox IsChecked="{Binding ShowSelectedFruitOnly}">Selected Fruit Only</CheckBox> <ListBox x:Name='MyList' ItemsSource="{Binding FruitsView}" ItemTemplate='{StaticResource FruitTemp}' /> <StackPanel Orientation="Horizontal" Margin="0,10,0,0"> <Label Width="100">New Name:</Label> <TextBox Width="200" Text="{Binding NewFruitName}" /> </StackPanel> <StackPanel Orientation="Horizontal" Margin="0,10,0,0"> <Label Width="100">New Color:</Label> <TextBox Width="200" Text="{Binding NewFruitColor, UpdateSourceTrigger=PropertyChanged}" /> <ContentControl Style="{StaticResource ColorSwatch}" Margin="2" VerticalAlignment="Center" Content="{Binding NewFruitColor}" /> </StackPanel> <Button x:Name='AddFruit' Height='auto' Width='auto' Content='Add New Fruit 2' Margin='0,10,0,0' Command='{StaticResource AddFruit}' /> </StackPanel> </Grid> </Window> 

MainWindow.xaml.cs

 using System; using System.Windows; using System.Windows.Input; using Fruits.ViewModels; namespace Fruits { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new MainViewModel(); ViewModel.AddNewFruit("Jackfruit", "Yellow"); ViewModel.AddNewFruit("Watermelon", "ForestGreen"); ViewModel.AddNewFruit("Apple", "Red"); ViewModel.AddNewFruit("Banana", "Yellow"); ViewModel.AddNewFruit("Orange", "DeepSkyBlue"); ViewModel.Fruits[0].IsSelected = false; ViewModel.Fruits[1].IsSelected = false; ViewModel.FruitsView.Refresh(); } public MainViewModel ViewModel { get { return DataContext as MainViewModel; } } private void AddFruitCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { ViewModel.AddNewFruit(); } private void AddFruitCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = ViewModel != null && !String.IsNullOrWhiteSpace(ViewModel.NewFruitName) && !String.IsNullOrWhiteSpace(ViewModel.NewFruitColor) ; } } } 

Screenshot:

enter image description here

The standard HTML color names ( see System.Windows.Media.Colors for predefined WPF color constants ) will work for colors, and therefore will be #RRGGBB or #AARRGGBB hexadecimal colors.

+2
source

Source: https://habr.com/ru/post/1266942/


All Articles