识别由触发事件中的模板创建的复选框

Recognizing checkbox created by a template in a fired event

我已经像这样创建了一个可展开的复选框列表。

<Expander x:Name=...>
  <ListBox ItemsSource="{x:Static local:MainWindow.AllTypes}">
    <ListBox.ItemTemplate>
      <DataTemplate>
        <CheckBox Content="{Binding Name}"
                  Checked="ToggleButton_OnToggled"
                  Unchecked="ToggleButton_OnToggled"/>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
</Expander>

我还有一个签名如下的方法

private void FilterStuffOut(String condition)
{
  CollectionViewSource source 
    = new CollectionViewSource { Source = dataGrid.ItemsSource };
  ICollectionView view = source.View;
  view.Filter = element => BringItOut(element, condition);
  dataGrid.ItemsSource = view;
}

我不确定(并且在 sendereventArgs 中使用 intellisense 来找我什么都没有)如何知道哪个复选框是火热的。我应该在下面的方法中在哪里寻找它?

private void ToggleButton_OnToggled(
  Object sender, RoutedEventArgs eventArgs) { ... }

您通常会如下所示编写它,明确不使用 as 运算符,而是转换为所需的类型。这是因为您期望这些类型,而任何其他类型都应该导致运行时错误,即 InvalidCastException.

private void ToggleButton_OnToggled(object sender, RoutedEventArgs eventArgs)
{
    var element = (FrameworkElement)sender;
    var myType = (MyType)element.DataContext;

    // do something with myType.MyValue
}

如果您需要更多派生类型的属性,例如ToggleButton.IsChecked,您将使用该类型而不是 FrameworkElement。

如约而至。

我比较喜欢用电影做例子,请见谅。我不确定你的 UI 到底是什么样子,或者你想在这里实现什么,但我想我理解了大致的想法。

如果您不熟悉 MVVM 设计模式,我建议您学习 this 教程。

我们开始了。

这些是我的模型:

//This is a base class which handles our notify property changed stuff which will update the UI
//when properties change.
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged([CallerMemberName]string propertyName = null)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

上面的 class 是所有模型的基础 class,如果视图模型需要,有时还包括视图模型。它非常有用,我建议您在所有 MVVM 应用程序中实现类似的东西。

public class Filter : NotifyPropertyChangedBase
{
    public event EventHandler OnEnabledChanged;

    public string Genre { get; set; }

    private bool _IsEnabled;

    public bool IsEnabled
    {
        get { return _IsEnabled; }
        set 
        { 
            _IsEnabled = value;
            OnPropertyChanged();

            if (OnEnabledChanged != null)
                OnEnabledChanged(this, new EventArgs());
        }
    }

    public Filter(string genre)
    {
        this.Genre = genre;
    }
}

public class Movie
{
    //We don't need to implement INotifyPropertyChanged here
    //because these values will never change.
    public string Name { get; set; }
    public string Genre { get; set; }
}

上面的模型代表电影,以及电影类型的过滤器。所有模型都不 "do" 任何东西,它们只是代表数据。

这是 ViewModel:

public class MovieViewModel : NotifyPropertyChangedBase
{
    private ObservableCollection<Movie> _FilteredMovies;

    public ObservableCollection<Movie> FilteredMovies
    {
        get { return _FilteredMovies; }
        set 
        {
            _FilteredMovies = value;

            //Need to implement INotifyPropertyChanged here because 
            //I am instantiating a new observable collection in the enabled changed
            //method. This will refresh the binding on the DataGrid.
            OnPropertyChanged();
        }
    }

    public ObservableCollection<Movie> Movies { get; set; }
    public ObservableCollection<Filter> Filters { get; set; }

    public MovieViewModel()
    {
        this.Movies = new ObservableCollection<Movie>();
        this.Filters = new ObservableCollection<Filter>();

        #region Sample Data

        this.Movies.Add(new Movie()
            {
                Name = "Movie Action",
                Genre = "Action"
            });

        this.Movies.Add(new Movie()
        {
            Name = "Movie Romance",
            Genre = "Romance"
        });

        this.Movies.Add(new Movie()
        {
            Name = "Movie Comedy",
            Genre = "Comedy"
        });

        this.Filters.Add(new Filter("Action"));
        this.Filters.Add(new Filter("Romance"));
        this.Filters.Add(new Filter("Comedy"));

        foreach (Filter filter in this.Filters)
            filter.OnEnabledChanged += filter_OnEnabledChanged;

        #endregion
    }

    void filter_OnEnabledChanged(object sender, EventArgs e)
    {
        var filteredMovies = (from m in this.Movies
                                join f in this.Filters on m.Genre equals f.Genre
                                where f.IsEnabled
                                select m).ToList();

        this.FilteredMovies = new ObservableCollection<Movie>(filteredMovies);
    }
}

它有电影合集和滤镜合集。选中复选框后,将调用 OnEnabledChanged 方法并将 FilteredMovies 属性 设置为所选过滤器的那个。这反过来将调用通知 属性 更改的代码并更新 UI.

这里是 UI:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ViewModels="clr-namespace:WpfApplication1"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <ViewModels:MovieViewModel/>
</Window.DataContext>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <Expander>
        <ItemsControl ItemsSource="{Binding Filters}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding IsEnabled}"
                              Content="{Binding Genre}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Expander>

    <DataGrid Grid.Row="1"
              ItemsSource="{Binding FilteredMovies}"/>
</Grid>

与您的实现类似,有一个 DataGrid 绑定到 ViewModel 中的 FilteredMovies 属性,过滤器列表表示为 CheckBox 对象列表。

就像我之前提到的,我不太确定你想要实现什么,但我认为这就是你想要做的事情。