2017-04-11 169 views
0

我需要一些幫助才能將ObservableCollection正確綁定到xaml。我可以正確地綁定數據,但是當數據發生變化時,更改並未反映在屏幕上。我已經閱讀過相關的博客,並且似乎已經理解了,但是當試圖將我所瞭解的知識應用到我自己的示例中時,它並不像我想象的那樣工作。將列表框綁定到observablecollection

我有2類水果和水果,其中水果是水果的ObservableCollection其中實現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; 
     } 
    } 
} 

我的項目模板

<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很陌生。

你的幫助指出我的俯視是非常讚賞。

回答

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你遞給了ListBox

ListBox必須具有與要添加對象相同的實際集合對象。

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

哎呀,不過濾。

而這仍然不是正確的做法。使用公共Fruits屬性編寫一個視圖模型,該屬性返回ObservableCollection<Fruit>,並在您的XAML中使用CollectionViewSource進行過濾。如果需要,我們可以遍歷所有這些東西。你已經知道如何實現INotifyPropertyChanged,所以你在路上。

更新

我做的水果應用程序作爲MVVM東西的快速重寫。通常,我會使用委託命令將AddNewFruit命令作爲視圖模型的屬性,但我不想編寫委託命令類並粘貼它。儘管如此,這裏還是可以的。

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 
} 

應用。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) 
       ; 
     } 
    } 
} 

截圖:

enter image description here

標準HTML顏色名稱(see System.Windows.Media.Colors for WPF predefined color constants)將適用於顏色,並且#RRGGBB#AARRGGBB六角形顏色也適用。

+2

只需添加到答案中,當您使用「CollectionViewSource」時,您將能夠在xaml級別進行過濾。然而,CVS與ObservableCollection不太匹配,所以我只會使用某種'IEnumerable',我會把選擇留給你。這是我的2便士。 [這裏](https://wpftutorial.net/DataViews.html)是一個很好的鏈接。 – XAMlMAX

+0

@Ed Plunkett:謝謝你指出這個缺陷。從來沒有想過這個!非常感謝!將嘗試您的建議 – user1205746

+0

@Ed Plunkett:你會介意走我需要採取的步驟,我應該把我的東西轉換成MVVM嗎?如果你沒有時間但主要重要步驟,則不需要詳細說明。我目前有定義來自INotifyPropertyChanged的水果的模型,以及類Fruit作爲ObservableCollection來檢測水果變化的事件。 viewmodel應該如何鏈接到xaml? – user1205746

相關問題