识别由触发事件中的模板创建的复选框
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;
}
我不确定(并且在 sender 和 eventArgs 中使用 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 对象列表。
就像我之前提到的,我不太确定你想要实现什么,但我认为这就是你想要做的事情。
我已经像这样创建了一个可展开的复选框列表。
<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;
}
我不确定(并且在 sender 和 eventArgs 中使用 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 对象列表。
就像我之前提到的,我不太确定你想要实现什么,但我认为这就是你想要做的事情。