我需要一些帮助才能将ObservableCollection正确绑定到xaml。我可以正确绑定数据,但是当数据发生变化时,更改并未反映在屏幕上。我已经阅读了相关的博客并且似乎已经理解了,但是当我尝试将我所知道的内容应用到我自己的样本中时,它并没有像我想象的那样有效。
我有2个水果和水果类,其中水果是可观察到的水果收集,实现了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);
}
}
}
我的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>
和代码背后:
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;
}
}
}
我的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>
当我点击按钮时,我看到项目已添加到集合中,但集合未在列表中刷新。我必须没有正确地绑定集合或者可能错过了一些东西,因为我对wpf很新。
非常感谢你帮我指出我的忽视。
答案 0 :(得分:2)
此方法创建一个新的ObservableCollection
并返回它。您将其分配给MyList.ItemsSource
(不是绑定,这只是一项任务),然后您将项目添加到其他地方的其他ObservableCollection
。
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);
}
当然,您在_fruitList
的{{1}}的副本中看不到任何新项目。
ListBox
必须具有与您添加对象相同的实际集合对象。
ListBox
糟糕,没有过滤。
这仍然不是正确的方法。编写一个viewmodel,其中包含一个返回public static ObservableCollection<Fruit> getAllFruit(bool bSelected = false)
{
return _fruitList;
}
的公共Fruits
属性,并在XAML中使用ObservableCollection<Fruit>
进行过滤。如果需要,我们可以完成所有这些工作。您已经知道如何实施CollectionViewSource
,以便您继续前进。
我快速重写了Fruits应用程序作为MVVM的东西。通常我会使用委托命令将AddNewFruit命令作为viewmodel的属性,但我不想写一个委托命令类并将其粘贴进去。但是这里的确如此。
ViewModels.cs
INotifyPropertyChanged
的App.xaml
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
}
MainWindow.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.cs
<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>
截图:
标准HTML颜色名称(see System.Windows.Media.Colors for WPF predefined color constants)适用于颜色,因此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)
;
}
}
}
或#RRGGBB
十六种颜色也适用。