使用 wpf 单选按钮过滤动物

发布于 2024-09-15 11:53:22 字数 397 浏览 3 评论 0原文

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

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

这里的一些指导将非常感激。

谢谢

I am playing with a sample WPF application that is tiered in a Model-View-Presenter manner. The Model is a collection of Animals which is displayed in a view via binding to properties in a presenter class. The XAML has an items control that displays all the animals in a model.

The model class has a boolean attribute called 'IsMammal'. I want to introduce a filter in the XAML in the form of a radio button group that filters the collection of animals based on the 'IsMammal' attribute. Selection of the radiobutton 'Mammals' updates the items control with all the Animals that have the 'IsMammal' value set to true and when the value is toggled to 'Non-Mammals', the display is updated with all animals that have that particular boolean set to false. The logic to do the filtering is extremely simple. What is troubling me is the placement of the logic. I don't want any logic embedded in the *.xaml.cs. I want the toggling of the radiobutton to trigger a logic in the presenter that tapers my animal collection to be rebound to the display.

Some guidance here will be extremely appreciated.

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

雄赳赳气昂昂 2024-09-22 11:53:22

我建议您在 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
    }

}

I suggest you do the following in your Presenter:

=> Create a ListCollectionView field. Set it to be equal to the "Default Collection View" of your collection, and use it as the ItemsSource for the list control. Something like:


    public class Presenter()
    {
        private ListCollectionView lcv;

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

=> In the handler/logic for the RadioButton's corresponding event, filter the ListCollectionView. Something like:


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

Hope this helps.

EDIT:

While doing this is possible using MVP, using MVVM should be a better choice, IMHO (and as mentioned by the other answer). To help you out, I wrote a sample that implements your requirements via MVVM. See below:

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>

Code-behind:

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
    }

}
很酷不放纵 2024-09-22 11:53:22

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

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

  • bool FilterMammals
  • ObservableCollection MammalsToDisplay

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

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

I came from an MVP background before learning WPF and I have come to find that adapting the MVP pattern to WPF is a difficult exercise at best. The "proper" (read, least frustrating) approach is to utilize the Model-View-ViewModel (MVVM) pattern, which makes heavy use of the databinding features of WPF to minimize the amount of code that ends up the view-behind file.

In the simplest case, you would end up with two properties on your ViewModel:

  • bool FilterMammals
  • ObservableCollection MammalsToDisplay

In the XAML, You would bind the first to your radio button group and the second to the ItemsSource of the ListBox. The WPF databinding framework will call your property setter whenever the radiobutton group value changes, and in here you can filter and then update the list of items.

I'm not sure what your familiarity with MVVM is, so I'll stop here. Let me know if more detail would help :).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文