WPF GridView with Multiple Checkbox Columns and Select All Column Header - c#

I have a ListView containing several GridViewColumns. Several of the columns are checkboxes. Each column header consists of a checkbox as well, with the intention of checking/unchecking all the checkboxes in that column. Each row's checkboxes are bound to properties in my view model. I've seen several postings where the scenario is a single column of checkboxes, but none of those solutions will work for me, as I have 4 columns of checkboxes. I also need to persist the state of the selections from one visit to the next (all, some or none of the checkboxes in a column could be checked).
Here's an abbreviated version of the XAML for my ListView:
<ListView ItemsSource="{Binding AveragingParameters}">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Parameter">
<GridViewColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Text="{Binding Name}" />
</DockPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<CheckBox x:Name="chkAvg" IsChecked="{Binding CalcAverage}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
<Grid>
<CheckBox x:Name="chkAvgSelectAll" Content="Avg" ToolTip="Select All" />
</Grid>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<CheckBox x:Name="chkMin" IsChecked="{Binding CalMin}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
<Grid>
<CheckBox x:Name="chkMinSelectAll" Content="Min" ToolTip="Select All" />
</Grid>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I've tried using Commands and Command Parameters, PropertyChanged events on the Checked property, and handling the Checked event in my code behind, but nothing gives me enough information to know what to change in the collection I'm bound to.
I suppose I could create a separate event handler for each, but I'd like to be able to handle this in a single event handler if possible, because eventually I will be creating a custom control that is a bit more malluable in terms of the columns displayed.
Thanks in advance

I've solved my issue using a Command along with a Tag containing the name of the model property I want set and a CommandParameter set to a multi-binding that is bound to the Tag and the Checkbox's IsSelected property. The code follows:
My View's Xaml Resources:
<UserControl.Resources>
<converters:NameValueMultiBindingConverter x:Key="SelectAllConverter" />
</UserControl.Resources>
My View's Xaml:
<ListView ItemsSource="{Binding AveragingParameters}">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Parameter">
<GridViewColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Text="{Binding Name}" />
</DockPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<CheckBox x:Name="chkAvg" IsChecked="{Binding CalcAverage}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
<CheckBox x:Name="chkAvgSelectAll" Content="Avg"
Tag="CalcAvg" Command="SelectAllCheckedCommand"
ToolTip="Select All">
<MultiBinding Converter="{StaticResource SelectAllConverter}">
<Binding Path="Tag" RelativeSource="{RelativeSource self}" />
<Binding Path="IsChecked" RelativeSource="{RelativeSource self}" />
</MultiBinding>
</CheckBox>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<CheckBox x:Name="chkMin" IsChecked="{Binding CalMin}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
<CheckBox x:Name="chkMinSelectAll" Content="Avg"
Tag="CalcMin" Command="SelectAllCheckedCommand"
ToolTip="Select All">
<MultiBinding Converter="{StaticResource SelectAllConverter}">
<Binding Path="Tag" RelativeSource="{RelativeSource self}" />
<Binding Path="IsChecked" RelativeSource="{RelativeSource self}" />
</MultiBinding>
</CheckBox>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
My MultiValueConverter:
public class NameValueMultiBindingConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
var parameters = (object[])values;
return new NameValueConverterResult
{
Name = (string)parameters[0],
Value = parameters[1]
};
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The result object I'm using in the converter:
public class NameValueConverterResult
{
//The name of the model property to set
public string Name { get; set; }
//The value we're setting it to
public object Value { get; set; }
}
The DelegateCommand (I'm using PRISM) and handler in my View Model
public ICommand SelectAllCheckedCommand { get; private set; }
private void OnSelectAllCheckedCommand(object arg )
{
if (arg == null || !(arg is NameValueConverterResult)) return;
NameValueConverterResult prop = arg as NameValueConverterResult;
//Reflect on the model to find the property we want to update.
PropertyInfo propInfo = Averagers.FirstOrDefault().GetType().GetProperty(prop.Name);
if (propInfo == null) return;
//Set the property on the Model
foreach (var item in Averagers)
propInfo.SetValue(item, prop.Value, null);
}
I hope I'm not abusing StackOverflow providing the solution I came up with. I wasn't sure of the etiquette here.

Just hand over as much info as needed and do the rest via reflection, e.g.
<ListView ItemsSource="{Binding DpData}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsActive}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
<!-- Pass collection to Tag property, relevant member info is in the Content,
could also create an array in the Tag if the Content should be a nicer
looking string. -->
<CheckBox Content="IsActive" Click="CheckBox_Click" Tag="{Binding DpData}"/>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
// This method could be used for all columns,
// as long as they contain the necessary info
// which should be provided in the XAML:
// - Item Collection
// - Property Name
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
var cb = sender as CheckBox;
var items = cb.Tag as IEnumerable;
PropertyInfo prop = null;
foreach (var item in items)
{
if (prop == null)
{
var type = item.GetType();
var propname = cb.Content as string;
prop = type.GetProperty(propname);
}
prop.SetValue(item, cb.IsChecked, null);
}
}

If you want to stick with MVVM approach i would suggest to go for binding approach rather than event handling approach as MVVM avoids writing code in code behind file.
One way could be to simply bind two bool properties in view model with IsChecked Property of both checkboxes. Based on the change notification using INotifyPropetyChanged, you can iterate through your itemssource i.e. AveragingParameters and change either CalMin or CalcAverage

Related

How To Sort WPF ListView Column by MultiConverter Output

I would like to sort a ListView Column by MultiConverter Output
I am using a ListView and its GridViewColumns to display data from a Binding.
A handler "SortClickUniversal()" was added to the ListView, which in turn calls "Sort()"
If "Sort()" finds the Content String of the GridViewColumnHeader above it creates a SortDescription by the binding Element ("WartVPreis") and adds it to the Default View of the ListView ItemSource
This works fine for a simple Binding ("WartVPreis"). But I additionally have a Column which is filled by a Multibinding Converter:
<GridViewColumn Width="110" >
<GridViewColumn.Header>
<GridViewColumnHeader Content="WV Aktuell Netto €"
</GridViewColumnHeader>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding WartVPreis, ConverterCulture=de-DE, StringFormat={}{0:F2}}"
HorizontalAlignment="Right"
/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
C#
private void SortClickUniversal(object sender, RoutedEventArgs e)
{
//...
var sortBy = (e.OriginalSource as GridViewColumnHeader).Content.ToString();
bool sortieren = Sort(sortBy, direction, sender);
//...
}
private bool Sort(string sortBy, ListSortDirection direction, object sender)
{
switch (sortBy)
{
//...
case "WV Aktuell Netto €": sortBy = "WartVPreis"; break;
//...
SortDescription sd = new SortDescription(sortBy, direction);
dataView.SortDescriptions.Add(sd);
}
}
XAML
<GridViewColumn Width="90" >
<GridViewColumn.Header>
<GridViewColumnHeader Content="WV Ant. Net €"></GridViewColumnHeader>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock HorizontalAlignment="Right" >
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MultiWVAnteilConverterKey}" ConverterCulture="de-DE" StringFormat="{}{0:F2}">
<Binding Path="WartVPreis" />
<Binding Path="Dtvon" />
<Binding Path="Dtbis" />
<Binding Path="WartVZyklus" />
<Binding Path="WartVBJVON" />
<Binding Path="WartVBJBIS" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
How can I sort the Column by the MultiConverter Output with its MultiBinding?

WPF ListView not caching images

I have a WPF application that uses a ListView with a grid that displays images directly from the web. When the list is populated the images load as expected, but as I scroll down (the list contains around 200 items on average) it starts reusing the items that aren't in view (as it should). However, this causes the images to be released from memory and as a result they get reloaded all over again when the user scrolls back up.
MainWindow.xaml
<ListView Grid.Row="3" ItemsSource="{Binding SearchResults}" Background="{StaticResource PrimaryBackground}" Foreground="{StaticResource PrimaryForeground}"
ui:GridViewSort.AutoSort="True" ui:GridViewSort.ShowSortGlyph="False" IsSynchronizedWithCurrentItem="True" VirtualizingStackPanel.IsVirtualizing="False">
<ListView.View>
<GridView>
<GridViewColumn Width="80">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="{x:Type Foo}">
<Image>
<Image.Source>
<BitmapImage CacheOption="OnDemand" UriSource="{Binding PreviewImageUrl}" />
</Image.Source>
</Image>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title}" ui:GridViewSort.PropertyName="Title" />
<GridViewColumn Header="Remix" DisplayMemberBinding="{Binding Remix}" ui:GridViewSort.PropertyName="Remix" />
<GridViewColumn Header="Artist" DisplayMemberBinding="{Binding Artist}" ui:GridViewSort.PropertyName="Artist" />
<GridViewColumn Header="Duration" DisplayMemberBinding="{Binding Duration}" ui:GridViewSort.PropertyName="Duration" />
<GridViewColumn Header="BPM" DisplayMemberBinding="{Binding Bpm}" ui:GridViewSort.PropertyName="Bpm" />
<GridViewColumn Header="Year" DisplayMemberBinding="{Binding Date}" ui:GridViewSort.PropertyName="Date" />
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="0,0,10,0" />
</Style>
</StackPanel.Resources>
<Button Command="{Binding ElementName=Window, Path=DataContext.Download}" CommandParameter="{Binding}">Download</Button>
<Button Command="{Binding ElementName=Window, Path=DataContext.CopyLink}" CommandParameter="{Binding}">Copy link</Button>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Setting the Image.Source property with a different CacheOption makes no difference. You can also see I disabled virtualization which is bad but it's the only way to have it keep the images in memory. Is there an easy way to stop this from happening while also enabling virtualization?
Add a readonly PreviewImage property to your search results item class that creates and holds the BitmapImage when it is first accessed:
public class SearchResult : INotifyPropertyChanged
{
private Uri previewImageUrl;
public Uri PreviewImageUrl
{
get { return previewImageUrl; }
set
{
previewImageUrl = value;
previewImage = null;
NotifyPropertyChanged(nameof(PreviewImageUrl));
NotifyPropertyChanged(nameof(PreviewImage));
}
}
private ImageSource previewImage;
public ImageSource PreviewImage
{
get
{
if (previewImage == null && previewImageUrl != null)
{
previewImage = new BitmapImage(previewImageUrl);
}
return previewImage;
}
}
...
}
and bind to it like this:
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding PreviewImage}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
Try this:
<Image
HorizontalOptions="CenterAndExpand"
VerticalOptions ="CenterAndExpand">
<Image.Source>
<UriImageSource Uri="{Binding Image}"
CacheValidity="14"
CachingEnabled="true"/>
</Image.Source>
</Image>

How to do both filtering and grouping listview from SQL

How do I get my listview to do both grouping and filtering because I can only get it to do one of the thing at once. I had tried almost everything I could but none of it worked. when i remove
public string SelectedParam { get { return _selectedParam; } set { _selectedParam = value; OnPropertyChanged("SelectedParam");
if (_selectedParam == "Krydsmål") { BindData(); } else { hjuldata.ItemsSource = FilterKategori().Tables[0].DefaultView; ; } } }
then the grouping works but the filtering doesn't
i wonder if i can use the sql for filling instead for both filling and filtering and then get the listview to do the filtering like you can do with manualy added items
My combobox for filtering:
<ComboBox x:Name="Krydsmålbox" Foreground="#FFEAEAEA" Background="#FF303030" FontSize="12"
Style="{StaticResource ComboBoxTest2}" ItemTemplate="{StaticResource cmbTemplate2}"
ItemsSource="{Binding}" SelectedValuePath="Krydsmålene"
SelectedValue = "{Binding SelectedParam, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}},UpdateSourceTrigger=PropertyChanged}" BorderBrush="#FF303030" Height="40" DockPanel.Dock="Top" Margin="586,42,379,0"/>
Listview
<ListView x:Name="hjuldata" BorderBrush="#FF303030" Foreground="#FF00FB0B" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" Background="#FF303030" ItemsSource="{Binding}" Margin="-160,242,11,22" Grid.ColumnSpan="6" Grid.Row="3" Style="{DynamicResource ListViewStyle2}" DockPanel.Dock="Bottom" Height="576">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Background" Value="Black" />
<Setter Property="Foreground" Value="#FFEAEAEA"/>
<Setter Property="FontWeight" Value="Bold" />
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn Header="" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Billed, Converter={StaticResource nullImageConverter}}" Width="20" Height="20" Stretch="Fill" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,0,15,0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Model" Width="140" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding Model}" Foreground="#FF00FB0B" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Årgang" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding Årgang}" Foreground="#FF00FB0B" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Motor Type" Width="150" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding [Motor Type]}" Foreground="#FF00FB0B" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding Krydsmålet}" Foreground="#FF00FB0B" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding Centerhul}" Foreground="#FF00FB0B" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="ET" Width="auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding ET}" Foreground="#FF00FB0B" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Bolter" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding Bolter}" Foreground="#FF00FB0B" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Dæk" Width="300">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding Dæk}" Foreground="#FF00FB0B" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Fælge" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding Fælge}" Foreground="#FF00FB0B" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
grouping style
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="False" BorderBrush="#FFEAEAEA" BorderThickness="0,0,0,1" >
<Expander.Header>
<StackPanel Orientation="Horizontal" DataContext="{Binding Items}">
<Image Source="{Binding Billed, Converter={StaticResource nullImageConverter}}" Width="20" Height="20" Stretch="Fill" VerticalAlignment="Center" Margin="0,0,15,0"/>
<TextBlock Text="{Binding Mærke}" FontWeight="Bold" Foreground="#FFEAEAEA" FontSize="22" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding Krydsmålene}" FontWeight="Bold" Foreground="#FFFBFB00" FontSize="22" VerticalAlignment="Bottom" Margin="0,0,150,0" TextAlignment="Center" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
CS:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _selectedParam;
public MainWindow()
{
InitializeComponent();
BindData();
ICollectionView dataView = CollectionViewSource.GetDefaultView(hjuldata.ItemsSource);
dataView.GroupDescriptions.Add(new PropertyGroupDescription("Mærke"));
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string SelectedParam { get { return _selectedParam; } set { _selectedParam = value; OnPropertyChanged("SelectedParam");
if (_selectedParam == "Krydsmål") { BindData(); } else { hjuldata.ItemsSource = FilterKategori().Tables[0].DefaultView; ; } } }
private void BindData()
{
hjuldata.ItemsSource = Kategori().Tables[0].DefaultView;
}
public DataSet Kategori()
{
Data = #"Select ps.Mærket AS Mærke, P.DataID, P.Billed, P.Model, P.Årgang, P.[Motor Type], P.Krydsmålet, P.Centerhul, P.ET,P.Bolter, P.Dæk, P.Fælge ,PS.Krydsmålene from Data.Hjuldata P inner join Data.Mærke PS on P.MærkeID = PS.MærkeID ORDER BY ps.Mærket";
//SQL statement to fetch entries from Hjuldata
DataSet dsdata = new DataSet();
//Open SQL Connection
using (conn = new SqlConnection(connStrings))
{
conn.Open();
//Initialize command object
using (cmd = new SqlCommand(Data, conn))
{
SqlDataAdapter adapters = new SqlDataAdapter(cmd);
//Fill the result set
adapters.Fill(dsdata);
conn.Close();
}
}
return dsdata;
}
public DataSet FilterKategori()
{
Data = #"Select ps.Mærket AS Mærke, P.DataID, P.Billed, P.Model, P.Årgang, P.[Motor Type], P.Krydsmålet, P.Centerhul, P.ET,P.Bolter, P.Dæk, P.Fælge ,PS.Krydsmålene from Data.Hjuldata P inner join Data.Mærke PS on P.MærkeID = PS.MærkeID WHERE Krydsmålet = #param1";
//SQL statement to fetch entries from products
DataSet dsdata = new DataSet();
//Open SQL Connection
using (conn = new SqlConnection(connStrings))
{
conn.Open();
//Initialize command object
using (cmd = new SqlCommand(Data, conn))
{
cmd.Parameters.AddWithValue("#param1", SelectedParam);
SqlDataAdapter adapters = new SqlDataAdapter(cmd);
//Fill the result set
adapters.Fill(dsdata);
conn.Close();
}
}
return dsdata;
}
Can somebody help me with this please?
I think your problem is that you are using the ADO.NET data view abstraction rather than the WPF data view abstraction which should be easier to use. In WPF when you bind a collection or DataTable to an ItemsControl a data view object is created that basically serves as a layer over your collection or DataTable. By doing this you can potentially have the same collection bound to multiple ItemsControls but have different "views" of that data by having different filtering and grouping for the two distinct data views that are created for that same collection.
In your case you are binding to a DataTable which is messier than binding just to a collection that implements IList. For DataTable there is a DataView class that is part of ADO.NET that your WPF data view will basically serve as a layer over and this DataView is more limited in functionality (this ADO.NET DataView is what you are using in your code currently).
Either way, to get the WPF data view you just need to ask the ItemsSource for it like so:
ICollectionView dataView = CollectionViewSource.GetDefaultView(myListView.ItemsSource);
Now you will want to cast the ICollectionView to something more useful for setting the filter and grouping. In your case you have a DataTable so you will want to cast that to a BindingListCollectionView. Unfortunately this is more limited than the data view you would get for an IList (which is a ListCollectionView), but you have a DataTable so we will just roll with it for now (I always go with IList so I have never actually implemented a DataTable binding in production before).
The BindingListCollectionView doesn't have an operational Filter property so you have to use its CustomFilter property to specify the partial SQL that you want to use to filter your collection (basically what you would have put in a WHERE clause). As far as grouping goes I haven't ever used it on a DataTable binding but I would hope it just works by updating GroupDescriptions on the data view.
So basically in summary I would grab the WPF data view abstraction rather than the ADO.NET data view abstraction that you are currently using and set the filter and grouping on that. Also I would recommend just bringing in your data as an IList or else converting it to an IList since those are easier to work with in WPF.
i think this is something you should read
How to: Group, Sort, and Filter Data in the DataGrid Control
i had tested that one and it works with your datastructure and you dont have to do anything with your filtering only change out the grouping part and colectionview

How do I only show data by filtering the properties in a WPF ListView?

I am working with a WPF control that I created and I am trying to only show certain rows of my list by values of a property. An example is the following, I have a User class that holds a property of Active. How do I tell the .xaml that the list should only show the people that are Active?
Right now I am basically using linq to generate a new list and hand it to the listview based on what I want. However, I would rather just hand the ListView my entire list and let it do the work for me.
Here is my ListView code.
<ListView ItemsSource="{Binding}" DataContext="{Binding }" >
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Index}"/>
<TextBlock Text=". " />
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="{Binding LastName}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
You'll need some code behind to add a filter:
See: WPF filtering
ICollectionView view = CollectionViewSource.GetDefaultView(lstMovies.ItemsSource);
view.Filter = null;
view.Filter = new Predicate<object>(FilterMovieItem);
private bool FilterMovieItem(object obj)
{
MovieItem item = obj as MovieItem;
if (item == null) return false;
string textFilter = txtFilter.Text;
if (textFilter.Trim().Length == 0) return true; // the filter is empty - pass all items
// apply the filter
if (item.MovieName.ToLower().Contains(textFilter.ToLower())) return true;
return false;
}

Use Blend SDK Event triggers to fire a button click with relevant list view item MVVM

I'm trying to use the event trigger from Blend to fire a button click event of a listview item, it should work so that the item does not have to be selected for the relevant row to be referenced.
My code is...
Public void MyCommand(object obj)
{
// the tag of this has the search type
ListViewItem item = obj as ListViewItem;
// do my dreary domain work...
}
my xaml is...
<ListView ItemsSource="{Binding Path=SystemSetupItems}"
SelectedItem="{Binding Selected, Mode=TwoWay}"
MinHeight="120" >
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Description" DisplayMemberBinding="{Binding Description}" />
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseClick">
<i:InvokeCommandAction CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListViewItem, AncestorLevel=1}}" Command="{Binding MyCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
but this doesn't work at all, alternatively I can do this in my xaml button definition
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding OpenWorkSpaceCommand}" CommandParameter="{Binding Path=Name}" Content="Edit..." DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}" >
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
but this required the listview item to be previously selected, which is not the behaviour I want.
For my DataGrid I have a button on each item using the cell template. Each item is an object of type Meal. In my Meal.cs file I have an event definition like so:
public Meal()
{
RemoveMealCommand = new RelayCommand(() => RemoveMealCommandExecute());
}
public RelayCommand RemoveMealCommand
{
get;
set;
}
public delegate void RemoveMealEventHandler(object sender, EventArgs e);
public event RemoveMealEventHandler RemoveMealEvent;
private void RemoveMealCommandExecute()
{
RemoveMealEvent(this, null);
}
In my viewmodel for every meal in my list I can just add a handler to that event. And for my xaml button I just set the command to the Meal's RelayCommand.
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding Path=RemoveMealCommand}">
<Image Width="13" Height="13" Source="/Images/delete-icon.png"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Now when you click the button the Meal is responsible for firing the event and the viewmodel is responsible for handling it.

Categories