【问题标题】:Filtering animals using wpf radiobuttons使用 wpf 单选按钮过滤动物
【发布时间】:2010-08-25 06:05:02
【问题描述】:

我正在使用以模型-视图-演示者方式分层的示例 WPF 应用程序。 Model 是 Animals 的集合,通过绑定到 Presenter 类中的属性显示在视图中。 XAML 有一个项目控件,用于显示模型中的所有动物。

模型类有一个名为“IsMammal”的布尔属性。我想以单选按钮组的形式在 XAML 中引入一个过滤器,该过滤器根据“IsMammal”属性过滤动物集合。选择单选按钮“哺乳动物”会更新项目控件,其中包含“IsMammal”值设置为 true 的所有动物,当该值切换为“非哺乳动物”时,显示会更新为具有该特定布尔值的所有动物设置为假。进行过滤的逻辑非常简单。困扰我的是逻辑的位置。我不希望在 *.xaml.cs 中嵌入任何逻辑。我希望单选按钮的切换触发演示器中的逻辑,该逻辑使我的动物收藏逐渐变细以重新显示到显示器。

我们将非常感谢这里的一些指导。

谢谢

【问题讨论】:

  • 我认为在没有代码的情况下这是不可能的。后面有 6 行代码真的有问题吗?
  • @lukas - 如果您可以发布将在“代码隐藏”中使用的六行代码来实现此功能,那就太好了?

标签: c# wpf xaml radio-button mvp


【解决方案1】:

我建议您在 Presenter 中执行以下操作:

=> 创建一个 ListCollectionView 字段。将其设置为等于集合的“默认集合视图”,并将其用作列表控件的 ItemsSource。比如:


    public class Presenter()
    {
        private ListCollectionView lcv;

        public Presenter()
        {
            this.lcv = (ListCollectionView)CollectionViewSource.GetDefaultView(animalsCollection);
            listControl.ItemsSource = this.lcv;
        }
    }

=> 在 RadioButton 对应事件的处理程序/逻辑中,过滤 ListCollectionView。比如:


void OnCheckedChanged()
{
     bool showMammals = radioButton.IsChecked;
     this.lcv.Filter = new Predicate((p) => (p as Animal).IsMammal == showMammals);
     this.lcv.Refresh();
}

希望这会有所帮助。

编辑:

虽然使用 MVP 可以做到这一点,但使用 MVVM 应该是更好的选择,恕我直言(正如另一个答案所述)。为了帮助您,我编写了一个示例,通过 MVVM 实现您的要求。见下文:

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <Grid>

        <StackPanel Orientation="Vertical">

            <RadioButton x:Name="rb1" GroupName="MyGroup" Content="IsMammal = true" Checked="rb1_Checked"/>
            <RadioButton x:Name="rb2" GroupName="MyGroup" Content="IsMammal = false" Checked="rb2_Checked"/>

            <ListBox ItemsSource="{Binding Path=AnimalsCollectionView}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=Name}"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>

    </Grid>
</Window>

代码隐藏:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Threading;
using System.ComponentModel;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void rb1_Checked(object sender, RoutedEventArgs e)
        {
            (this.DataContext as ViewModel).UpdateFilter(true);
        }

        private void rb2_Checked(object sender, RoutedEventArgs e)
        {
            (this.DataContext as ViewModel).UpdateFilter(false);
        }
    }

    public class ViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<Animal> animals = new ObservableCollection<Animal>();
        private ListCollectionView animalsCollectionView;

        public ListCollectionView AnimalsCollectionView
        {
            get { return this.animalsCollectionView; }
        }

        public void UpdateFilter(bool showMammals)
        {
            this.animalsCollectionView.Filter = new Predicate<object>((p) => (p as Animal).IsMammal == showMammals);
            this.animalsCollectionView.Refresh();
        }

        public ViewModel()
        { 
            this.animals.Add(new Animal() { Name = "Dog", IsMammal = true });
            this.animals.Add(new Animal() { Name = "Cat", IsMammal = true });
            this.animals.Add(new Animal() { Name = "Bird", IsMammal = false });

            this.animalsCollectionView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.animals);

        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

        #endregion
    }

    public class Animal : INotifyPropertyChanged
    {
        private string name;
        public string Name
        {
            get { return this.name; }
            set
            {
                this.name = value;
                this.OnPropertyChanged("Name");
            }
        }

        private bool isMammal;
        public bool IsMammal
        {
            get { return this.isMammal; }
            set
            {
                this.isMammal = value;
                this.OnPropertyChanged("IsMammal");
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

        #endregion
    }

}

【讨论】:

  • 谢谢。我对“listControl.ItemsSource = this.lcv”有点困惑。这是否意味着 Presenter 类需要知道视图上存在的控件?同样对于您提到的“OnCheckedChanged()”,它是 RadioButton 的相应事件,您能否在示例中详细说明将在演示者中定义的 OnCheckedChange 如何由 XAML 上的 RadioButton 调用。
  • 这真的取决于你的 Presenter 实现。我假设您的 Presenter 对象具有对 View 的引用,因此我在上面的示例。我想我想说的关键思想是:1)使用 ListCollectionView,以及 2)在 Presenter 中有一些东西会在 RadioButton 的值更新时更新 ListCollectionView 的过滤器。
【解决方案2】:

在学习 WPF 之前,我有 MVP 背景,我发现将 MVP 模式适应 WPF 充其量是一项困难的练习。 “正确”(阅读,最不令人沮丧)的方法是利用 Model-View-ViewModel (MVVM) 模式,该模式大量使用 WPF 的数据绑定功能来最大限度地减少最终查看隐藏文件的代码量。

在最简单的情况下,您最终会在 ViewModel 上获得两个属性:

  • 布尔过滤哺乳动物
  • ObservableCollection MammalsToDisplay

在 XAML 中,您将第一个绑定到您的单选按钮组,第二个绑定到 ListBox 的 ItemsSource。每当单选按钮组值发生更改时,WPF 数据绑定框架将调用您的属性设置器,在这里您可以过滤然后更新项目列表。

我不确定您对 MVVM 的熟悉程度,所以我将在此停止。让我知道更多细节是否有帮助:)。

【讨论】:

  • 非常感谢您指出 MVVM。详细信息肯定会有所帮助,而且深入了解为什么这种方法(在 Presenter 中创建两个附加属性并将其绑定到 XAML)对 MVP 无效。
  • 这并不是说它不适用于 MVP,而是创建和绑定属性在那时基本上不是 MVP(尽管可以说大多数 UI 表示模式在某些方面相互重叠)。您可以将 MVVM 视为从 Presenter 中移除“呈现”责任,并让 WPF 的数据绑定功能来处理它。这留下了冷硬的数据,它本质上是一个专门为您的视图构建的模型,因此是“视图模型”。有意义吗?
猜你喜欢
  • 2020-08-30
  • 1970-01-01
  • 2020-02-08
  • 2016-02-14
  • 1970-01-01
  • 1970-01-01
  • 2021-08-30
  • 2015-09-22
  • 2015-09-03
相关资源
最近更新 更多