search in DataGrid by textBox WPF - c#

I have grid with 10-15 columns. (I load data by datagrid.ItemsSource = myList.ToList()) Also I have textBox witch textChanged event. When I put here eg. "cat" I want to see only rows with value ...cat...
how do I make this?

LINQ queries are good for this sort of thing, the concept goes make a variable to store all of your rows (in the example called _animals) and then when the user presses a key in the text box use a query, and pass the result as the ItemsSource instead.
Here is a basic working example of how this would work, first the XAML for the Window.
<Window x:Class="FilterExampleWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FilterExampleWPF"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox x:Name="textBox1" Height="22" Margin="10,10,365,0" VerticalAlignment="Top" KeyUp="textBox1_KeyUp" />
<DataGrid x:Name="dataGrid1" Height="272" Margin="10,40,10,0" VerticalAlignment="Top" AutoGenerateColumns="True" />
</Grid>
</Window>
Next the code behind:
using System.Collections.Generic;
using System.Linq;
namespace FilterExampleWPF
{
public partial class MainWindow : System.Windows.Window
{
List<Animal> _animals;
public MainWindow()
{
InitializeComponent();
_animals = new List<Animal>();
_animals.Add(new Animal { Type = "cat", Name = "Snowy" });
_animals.Add(new Animal { Type = "cat", Name = "Toto" });
_animals.Add(new Animal { Type = "dog", Name = "Oscar" });
dataGrid1.ItemsSource = _animals;
}
private void textBox1_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
var filtered = _animals.Where(animal => animal.Type.StartsWith(textBox1.Text));
dataGrid1.ItemsSource = filtered;
}
}
public class Animal
{
public string Type { get; set; }
public string Name { get; set; }
}
}
For this example I created an Animal class, however you could substitute it for your own class that you need to filter. Also I enabled AutoGenerateColumns, however adding your own column bindings in WPF would still allow this to work.
Hope this helps!

This is my solution .
public class Animal
{
public string Type { get; set; }
public string Name { get; set; }
}
List<Animal> _animals = new List<Animal>();
public MainWindow()
{
InitializeComponent();
_animals.Add(new Animal { Type = "cat", Name = "Snowy" });
_animals.Add(new Animal { Type = "cat", Name = "Toto" });
_animals.Add(new Animal { Type = "dog", Name = "Oscar" });
dataGrid1.ItemsSource = _animals;
}
List<Animal> filterModeLisst = new List<Animal>();
private void searchBox_TextChanged(object sender, TextChangedEventArgs e)
{
filterModeLisst.Clear();
if (searchBox.Text.Equals(""))
{
filterModeLisst.AddRange(_animals);
} else
{
foreach (Animal anim in _animals)
{
if (anim.Name.Contains(searchBox.Text))
{
filterModeLisst.Add(anim);
}
}
}
dataGrid1.ItemsSource = filterModeLisst.ToList();
}

Related

Why combobox not select any value?

Here is the code of XAML:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ComboBox x:Name="CB" SelectedValue="{Binding Model,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Value" VerticalAlignment="Center">
</ComboBox>
</Grid>
</Window>
And here is the code of code-behind:
public partial class MainWindow : Window
{
public List<TestModel> Models { get; set; } = new List<TestModel>();
TestModel _Model = new TestModel() { Key = "Joe", Value = "456" };
public TestModel Model
{
get => _Model; set
{
if (_Model != value)
{
_Model = value;
}
}
}
public MainWindow()
{
InitializeComponent();
Models.Add(new TestModel() { Key = "John", Value = "123" });
Models.Add(new TestModel() { Key = "Joe", Value = "456" });
Models.Add(new TestModel() { Key = "Kay", Value = "547" });
Models.Add(new TestModel() { Key = "Rose", Value = "258" });
CB.ItemsSource = Models;
this.DataContext = this;
}
public class TestModel
{
public string Key { get; set; }
public string Value { get; set; }
}
}
I bind the SelectedValue to the Model which is already existed in the List. but the selection is still blank.
What's wrong with my code? I need the combobox select the item correctly.
Model should return one of the items from Models.
Add this to the constructor before DataContext setter:
Model = Models[1];
The problematic line is
_Model = new TestModel() { Key = "Joe", Value = "456" } - you create an instance of TestModel that is not present in the list. Even though it has the same property values, it is not the same object.
ComboBoxes use the .Equals() method of the objects they are displaying to find the SelectedItem in the ItemsSource.
Since the Equals() method for clasess by default works by comparing object references, you would need to set the SelectedItem to an exact same instance as an object inside the ItemsSource.
Another approach you could take is overriding the Equals() method on your object and making it Equal by value comparison or use a record class which automatically makes your object comparable by value.

List in dropdownbox in grid does not return value

I have an enum
enum SendDays
{
Maandag = 1,
Dinsdag,
Vandaag= 99
}
And a Class
public struct DayListModel
{
public int Id;
public string DayName;
}
I fill a list with days like this
private void Filldays()
{
foreach(int i in Enum.getValues(typeof(SendDays)))
{
DayListModel day =new DaylistModel()
{
Id = i,
Dayname = Enum.GetName(typeof(SendDays), i)
};
DayList.Add(day);
}
When I use this is in A telerik RadGridView in a radcombobox
like
<telerik:RadComboBox ItemsSource="{Bindning DayList}" DisplayMemberPath="DayName" SelectedValue="{Bindning DefaultSendDay}" SelectedValuePath="Id"/>
Whenever I change the selecteditem this is not passed.
Any suggestions?
Jeroen
Here is my advice, and how I got it to work:
Like #mm8 said, you should make the DayListModel a class not a struct, and turn the fields into properties. If you don't use properties, the DisplayMemberPath doesn't work. Some stuff doesn't work with structs (like using == to compare) so I would go with a class in this case.
Are your objects observable in your view model? I don't know telerik, but you should be calling some sort of PropertyChanged method so the UI can update. If you use a framework like MVVM light it has ObservableObjects and RaisePropertyChanged methods.
Here is a working example:
MainWindowVM.cs
using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
namespace WpfApp1
{
public class MainWindowVM : ObservableObject
{
private List<DayListModel> _DayList;
public List<DayListModel> DayList
{
get { return _DayList; }
set
{
if (value != _DayList)
{
_DayList = value;
RaisePropertyChanged();
}
}
}
private DayListModel _DefaultSendDay;
public DayListModel DefaultSendDay
{
get { return _DefaultSendDay; }
set
{
if (value != _DefaultSendDay)
{
_DefaultSendDay = value;
RaisePropertyChanged();
}
}
}
public MainWindowVM()
{
DayList = new List<DayListModel>();
foreach (int i in Enum.GetValues(typeof(SendDays)))
{
DayListModel day = new DayListModel()
{
Id = i,
DayName = Enum.GetName(typeof(SendDays), i)
};
DayList.Add(day);
}
DayList = new List<DayListModel>(DayList);
}
}
public enum SendDays
{
Maandag = 1,
Dinsdag,
Vandaag = 99
}
public class DayListModel
{
public int Id { get; set; }
public string DayName { get; set; }
}
}
MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowVM />
</Window.DataContext>
<Grid >
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<ComboBox ItemsSource="{Binding DayList}" SelectedItem="{Binding DefaultSendDay}" DisplayMemberPath="DayName" >
</ComboBox>
<TextBlock Text="{Binding DefaultSendDay.DayName}"/>
</StackPanel>
</Grid>
</Window>
Screenshot:
Make sure that the types matches, i.e. if your DefaultSendDay source property is a byte, the Id of the DayListModel should also be a byte.
If it still doesn't work, try to convert the fields into properties and the struct to a class:
public class DayListModel
{
public byte Id { get; set; }
public string DayName { get; set; }
}

Wpf datagrid get underlying object cell value

I have a simple wpf datagrid in a caliburn micro project. I am initializing the rows with instances of a TestClass. If the user selects one of this row headers I want to get the instance of TestClass. But all values and all the example on the Internet are only able to show the Text in the cell.
So How do I get the object I used to create the cell from the datagrid?
ShellViewModel.cs:
using System.Collections.Generic;
using System.Data;
using System.Windows.Controls;
using Caliburn.Micro;
namespace TestSelectionChanged.ViewModels
{
class ShellViewModel : Screen
{
private DataTable _profileColumnRows;
public DataTable ProfileColumnRows
{
get => _profileColumnRows;
set
{
if (Equals(value, _profileColumnRows)) return;
_profileColumnRows = value;
NotifyOfPropertyChange();
}
}
public ShellViewModel()
{
ProfileColumnRows = new DataTable("test");
ProfileColumnRows.Columns.Add("test1");
ProfileColumnRows.Columns.Add("test2");
// Here are the TestClass objects I want to get later
ProfileColumnRows.Rows.Add(new TestClass("testc","a","b"));
ProfileColumnRows.Rows.Add(new TestClass("testd", "c", "d"));
}
public void OnSelectionCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
var dataGrid = ((DataGrid) sender);
var column = dataGrid.SelectedCells[0].Column;
var columnIndex = dataGrid.SelectedCells[0].Column.DisplayIndex;
var rowIndex = dataGrid.Items.IndexOf(dataGrid.SelectedCells[0].Item);
}
}
class TestClass
{
public string name { get; set; }
public List<string> Items { get; set; }
public TestClass(string name, params string[] items)
{
this.name = name;
Items = new List<string>(items);
}
public override string ToString()
{
return name;
}
}
}
ShellView.xaml:
<UserControl x:Class="TestSelectionChanged.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<DataGrid
ItemsSource="{Binding ProfileColumnRows}"
cal:Message.Attach="[Event SelectedCellsChanged] = [Action OnSelectionCellsChanged($source,$eventArgs)]"/>
</StackPanel>
</UserControl>
I want to get the object in the first selected cell and if it is of the type TestClass I want to have access to its Items property. Is this even possible?
To begin with, it is unclear why you are attempting use a DataTable with TestClass instances as row. For this to work, you need to figure out how to convert each property of TestClass to different columns in DataTable. A better approach would be to bind the DataGrid to a List<TestClass> directly.
For example,
public ShellViewModel()
{
ProfileColumnRows.Add(new TestClass("testc", "a", "b"));
ProfileColumnRows.Add(new TestClass("testd", "c", "d"));
}
public List<TestClass> ProfileColumnRows {get;set;} = new List<TestClass>();
Now coming to your question of accessing the Selected Item, you could use the SelectedItem Property
<DataGrid
ItemsSource="{Binding ProfileColumnRows}" SelectedItem="{Binding SelectedItem}"
/>
And in ViewModel
public TestClass SelectedItem { get; set; }

ComboBox items don't show up until the 1st column is sorted

The 2nd column items "Point Setting" don't show up until the 1st column items are sorted, clicking on the header of the 1st column. The goal of this code is to link the 1st and 2nd column items, then use the 2nd column items as the search keys.
I'm new to C# and WPF.
I tired to put sequential numbers in front of the 1st column items (1., 2., and so on) because I thought it would solve the problem if those items are initially sorted. But, no luck. I heard that ObservableCollection<> doesn't manage the input order, so once I changed it with List<>. But it didn't solve this problem, too.
Actually, I don't want to sort the 1st column; they should be fixed and no need to change the order/number at all.
To avoid any confusions, let me show my entire codes (sorry).
MainWindow.xaml:
<Window x:Class="XY.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:XY"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="454.4">
<Grid>
<DataGrid ItemsSource="{Binding channels}"
SelectedItem="{Binding SelectedRow, Mode=TwoWay}"
Margin="0,0,0,-0.2">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding name}"
Header="Channel" Width="Auto"/>
<DataGridTemplateColumn Width="100" Header="Point Setting">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="piontsComboBox"
ItemsSource="{Binding DataContext.points,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
SelectionChanged="PrintText"
DisplayMemberPath="name"
SelectedValuePath="name"
Margin="5"
SelectedItem="{Binding DataContext.SelectedPoint,
RelativeSource={RelativeSource AncestorType={x:Type Window}},
Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<TextBox x:Name="tb" Width="140" Height="30" Margin="10,250,200,30"></TextBox>
<Button x:Name="Browse_Button" Content="Browse" Margin="169,255,129.6,0"
Width="75" Click="Browse_Button_Click" Height="30" VerticalAlignment="Top"/>
</Grid>
</Window>
MainWindow.xaml.cs:
using System;
using System.Windows;
using System.Windows.Controls;
namespace XY
{
public partial class MainWindow : Window
{
public GridModel gridModel { get; set; }
public MainWindow()
{
InitializeComponent();
gridModel = new GridModel();
this.DataContext = gridModel;
}
private void Browse_Button_Click(object sender, RoutedEventArgs e)
{
WakeupClass clsWakeup = new WakeupClass();
clsWakeup.BrowseFile += new EventHandler(gridModel.ExcelFileOpen);
clsWakeup.Start();
}
void PrintText(object sender, SelectionChangedEventArgs args)
{
var comboBox = sender as ComboBox;
var selectedPoint = comboBox.SelectedItem as Point;
tb.Text = selectedPoint.name;
}
}
public class WakeupClass
{
public event EventHandler BrowseFile;
public void Start()
{
BrowseFile(this, EventArgs.Empty);
}
}
}
ViewModelBase.cs:
using System.ComponentModel;
namespace XY
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
}
Point.cs:
namespace XY
{
public class Point : ViewModelBase
{
private string _name;
public string name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("name");
}
}
private int _code;
public int code
{
get { return _code; }
set
{
_code = value;
OnPropertyChanged("code");
}
}
}
}
Record.cs:
namespace XY
{
public class Record : ViewModelBase
{
private string _name;
public string name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("name");
}
}
private int _PointCode;
public int PointCode
{
get { return _PointCode; }
set
{
_PointCode = value;
OnPropertyChanged("PointCode");
}
}
private Record _selectedRow;
public Record selectedRow
{
get { return _selectedRow; }
set
{
_selectedRow = value;
OnPropertyChanged("SelectedRow");
}
}
private Point _selectedPoint;
public Point SelectedPoint
{
get { return _selectedPoint; }
set
{
_selectedPoint = value;
_selectedRow.PointCode = _selectedPoint.code;
OnPropertyChanged("SelectedRow");
}
}
}
}
GridModel.cs:
using System.Collections.ObjectModel;
using System.Windows;
namespace XY
{
public class GridModel : ViewModelBase
{
public ObservableCollection<Record> channels { get; set; }
public ObservableCollection<Point> points { get; set; }
public GridModel()
{
channels = new ObservableCollection<Record>() {
new Record {name = "1. High"},
new Record {name = "2. Middle"},
new Record {name = "3. Low"}
};
}
internal void ExcelFileOpen(object sender, System.EventArgs e)
{
points = new ObservableCollection<Point> { new Point { } };
MessageBox.Show("Please assume that Excel data are loaded here.");
points.Add(new Point { name = "point1", code = 1 });
points.Add(new Point { name = "point2", code = 2 });
points.Add(new Point { name = "point3", code = 3 });
points.Add(new Point { name = "point4", code = 4 });
}
}
}
The procedure goes like:
Click on the "Browse" button to load the data.
Click on the 1st column "Channel" to sort the list (GOAL: I'd like to GET RID OF this step).
Click on the "Point Setting" ComboBox to select the items (point1, point2, ..., etc.).
... I don't know if ObservableCollection<> is appropriate here. If List<> or any other type is better, please change it. Any suggestion would be helpful. Thank you in advance.
Change your points ObservableCollection like such, because you're setting the reference of the collection after the UI is rendered, you would need to trigger the PropertyChanged event to update the UI.
private ObservableCollection<Point> _points;
public ObservableCollection<Point> points
{
get { return _points; }
set
{
_points = value;
OnPropertyChanged(nameof(points));
}
}
An alternative would be to first initialise your collection.
public ObservableCollection<Point> points { get; set; } = new ObservableCollection<Point>();
internal void ExcelFileOpen(object sender, System.EventArgs e)
{
// Do not re-initialise the collection anymore.
//points = new ObservableCollection<Point> { new Point { } };
points.Add(new Point { name = "point1", code = 1 });
points.Add(new Point { name = "point2", code = 2 });
points.Add(new Point { name = "point3", code = 3 });
}

WPF - Bind combobox to List of custom class objects

I have a WPF project with a combobox that I'm trying to bind to a List of ComboboxItem objects. ComboboxItem is a class that I created for my sample project. This is partially working... I have my three items available to the combobox, but the displayed value is blank and the value of combobox.SelectedValue is null. I've seen several stackoverflow posts and other blog posts about how to do this. And as far as I can tell, I'm doing this right. But obviously I'm doing something wrong. Here is the source code for a test project...
XAML:
<Window x:Class="WpfTestApp_ComboBoxes.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ComboBox x:Name="cboMyCombo" Grid.Row="0"
SelectionChanged="cboMyCombo_SelectionChanged"></ComboBox>
</Grid>
</Window>
C# Code-behind:
public partial class MainWindow : Window
{
List<ComboboxItem> _list = new List<ComboboxItem>();
public MainWindow()
{
_list.Add(new ComboboxItem() { DisplayValue = "One", InternalValue = "1" });
_list.Add(new ComboboxItem() { DisplayValue = "Two", InternalValue = "2" });
_list.Add(new ComboboxItem() { DisplayValue = "Three", InternalValue = "3" });
InitializeComponent();
}
private void cboMyCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
ComboBox cb = sender as ComboBox;
MessageBox.Show(string.Format("Selected Item: {0}, Selected Value: {1}", cb.SelectedItem, cb.SelectedValue));
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
cboMyCombo.ItemsSource = _list;
cboMyCombo.DisplayMemberPath = "DisplayValue";
cboMyCombo.SelectedValuePath = "InternalValue";
}
}
ComboboxItem Class:
public class ComboboxItem
{
public string DisplayValue;
public string InternalValue;
}
change
public class ComboboxItem
{
public string DisplayValue;
public string InternalValue;
}
to
public class ComboboxItem
{
public string DisplayValue {get;set;}
public string InternalValue {get;set;}
}

Categories