Creating an autocomplete textbox with dropdown - wpf

My Problem:
I have a list of 118 chemical element names. And I wish to make a textbox in which as I type it will throw a dropdown menu with suggestion of names. I made this textbox in winforms and it was piece of cake however my efforts of making it in wpf are in vain. I've tried extended wpf toolkit, nimgoble and some other autocomplete textbox libs. So far dead end...I'm also new to wpf so maybe I'm missing something with these libs? I can't make them list my items and some won't even show the dropdown menu.
Heres what I wanted:
Here's how I finally achieved it:
So I solved this by using a combination of textbox and listbox. Where in textbox user types and as it changes (textbox changed event) it's checking for matches inside a list that holds names of all 118 elements and displays matches for the text typed in, inside listbox. Here's the code:
private void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
listBox.Items.Clear();
if (textBox.Text.Trim() != "")
{
string regexPattern = (textBox.Text.ToString()) + "\\w*";
regexPattern = char.ToUpper(regexPattern[0]) + regexPattern.Substring(1); //prvo slovo veliko
Match match = Regex.Match(ElementNames.allElements, regexPattern);
while (match.Success && match.Value != "")
{
listBox.Items.Add(match.Value.ToString());
listBox.Visibility = Visibility.Visible;
match = match.NextMatch();
}
}
if (listBox.Items.IsEmpty || listBox.Items.Count == 119)
{
listBox.Visibility = Visibility.Collapsed;
if (listBox.Items.Count == 119) listBox.Items.Clear();
}
HighlightElementsOnTable();
OtherButtonsHighlight();
BringBackColors();
}

You can use a ComboBox with IsEditable=true.

Use a Combobox with
IsEditable = true
SelectedItem and Text data bound to the same property
StaysOpenOnEdit = true
<ComboBox ItemsSource="{Binding Models}" SelectedItem="{Binding Model}"
IsEditable="True" StaysOpenOnEdit="True" Text="{Binding Model, UpdateSourceTrigger=PropertyChanged}"/>

Related

WPF: ICollectionView.Refresh() refreshing only when UIElement loses focus, instead of Text and Value Change

I have an ICollectionView, called RepozitorijumWrapper which should display my entities based on six fields. The fields are two TextBox and four DateTimePicker elements. Basically, every time any of those elements change (even by a number/letter) I want the list to update.
My DateTimePicker and TextBox elements are bound to DateTime and string properties, that have the RepozitorijumWrapper.Refresh() code in their setter. When I tested my application, the filter did work, but only once you left the field. After that I tried calling the Refresh() method from the controller, or rather using the TextChanged event for the TextBox elements, and the ValueChanged for the DateTimePickers. This changed nothing. The filter does work, but it isn't refreshed the way I'd like it to refresh.
Since my code is pretty much six copies of the same thing, with changed names and types, I'll only paste one instance of each relevant part of code.
Here's the property:
public string SifraTima
{
get {return sifraTima;}
set
{
if(!sifraTima.Equals(value))
{
sifraTima = value;
RepozitorijumWrapper.Refresh();
}
}
}
Here's the XAML for that property:
<TextBox x:Name="txtSifraTima" Grid.Column="1" Grid.Row="2" Margin="3,3,30,3" Text="{Binding Path=SifraTima}" TextChanged="txtSifraTima_TextChanged" />
Here's the event handler:
private void txtSifraTima_TextChanged(object sender, TextChangedEventArgs e)
{
presenter.RepozitorijumWrapper.Refresh();
}
Here's my ICollectionView, created in the constructor of my presenter class:
RepozitorijumWrapper = CollectionViewSource.GetDefaultView(rezervacije.Kolekcija);
RepozitorijumWrapper.Filter = itm =>
((Rezervacija)itm).SifraTerena.Contains(SifraTerena) &&
((Rezervacija)itm).SifraTima.Contains(SifraTima) &&
((Rezervacija)itm).VremeZauzimanja <= VremeZauzimanjaDo &&
((Rezervacija)itm).VremeZauzimanja >= VremeZauzimanjaOd &&
((Rezervacija)itm).VremeOslobadjanja <= VremeOslobadjanjaDo &&
((Rezervacija)itm).VremeOslobadjanja >= VremeOslobadjanjaOd;
Your binding on the Text property of the TextBox is set to only update the property after the focus is lost by default. To change that behavior, you can specify a value for UpdateSourceTrigger like so:
Text="{Binding Path=SifraTima, UpdateSourceTrigger=PropertyChanged}"
MSDN has more information on UpdateSourceTrigger

Programatically bringing a Datagrid row into view in WPF, MVVM

I would like to bring a row of my data grid into view programatically. I have a more than 100 rows. When I create a row(which I am doing by adding an item to a observable collection) I would like that new row to be selected and bring that into view. I was able to select the new row in my code but could not do the scrolling. More over I want the first cell of the row to be in edit mode so that the user can input text. I am following MVVM pattern for the application and would like to keep zero code in my views. How Can I achieve this?
Any help or suggestion will be appreciated....
Update:
This what I did in my XAML
<telerik:RadGridView ItemsSource="{Binding AllPartClasses}"
SelectedItem="{Binding SelectedPartClassViewModel, Mode=TwoWay}"
SelectionMode="Single" IsSynchronizedWithCurrentItem="True">
in my view model I did this
void AddNewPartClassExecute()
{
PartClass newPartClass = new PartClass();
PartClassViewModel tempPartClass = new PartClassViewModel(newPartClass);
tempPartClass.IsInValid = true;
AllPartClasses.Add(tempPartClass);
SelectedPartClassViewModel = tempPartClass;
Global.DbContext.PartClasses.AddObject(newPartClass);
//OnPropertyChanged("AllPartClasses");
}
public PartClassViewModel SelectedPartClassViewModel
{
get
{
return _selectedPartClassViewModel;
}
set
{
_selectedPartClassViewModel = value;
OnPropertyChanged("SelectedPartClassViewModel");
}
}
It did not work for me.
For the regular WPF DataGrid you can use ScrollIntoView. In your view hookup the SelectionChanged event to the following in your view code-behind cs file.
private void OnSelectionChanged( object sender, SelectionChangedEventArgs e )
{
Selector selector = sender as Selector;
DataGrid dataGrid = selector as DataGrid;
if ( dataGrid != null && selector.SelectedItem != null && dataGrid.SelectedIndex >= 0 )
{
dataGrid.ScrollIntoView( selector.SelectedItem );
}
}
When following MVVM pattern you should not do a UI-specific stuff like scrolling from a code.
Solution would be simple - just bind DataGrid.SelectedItem to a property in ViewModel and when adding a new item in the items collection just update a property bound to SelectedItem so it would reference to just added item and data grid should select an appropriate row automatically.
<DataGrid
ItemsSource="{Binding UnderyingItemsCollection}"
SelectedItem="{Binding RecentlyAddedItem, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True">

How Can Determine Selected Cell's Value In DataGrid? (WPF)

How can I determine SelectedCell's Value In DataGrid? (WPF)
My DataGrid has 9 coloums and 5 rows and I want to know the Value of clicked row[0]'s Value.
I used this code in Windows Form:
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
var a = dataGridView1[e.ColumnIndex, e.RowIndex].Value;
}
but I don't know an equivalent code in wpf.
You should use DataGrid_SelectedCellsChanged event.
private void dataGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
foreach (var item in e.AddedCells)
{
var col = item.Column as DataGridColumn;
var fc = col.GetCellContent(item.Item);
if (fc is CheckBox)
{
Debug.WriteLine("Values" + (fc as CheckBox).IsChecked);
}
else if(fc is TextBlock)
{
Debug.WriteLine("Values" + (fc as TextBlock).Text);
}
//// Like this for all available types of cells
}
}
HTH
Determining a selected cell's value is more of a WinForms thing. WPF is designed to work differently; your UI is meant to be separated from logic. The DataGrid thus becomes an instrument for presentation, not something to be poked and prodded for values.
Instead, with WPF, you want to deal with the objects you have bound to the grid themselves, independent of how they are displayed. Forget the grid - just find the object that is currently "selected" by the user out of a list of bound objects.
The SelectedItem is a property on the grid itself and thanks to WPF's superior binding mechanisms, you can bind this value to a property on a ViewModel via XAML:
ItemsSource="{Binding Orders, Mode=OneWay}"
SelectedItem="{Binding SelectedOrder, Mode=TwoWay}"
When the user selects an item in the grid, the two-way binding will update the SelectedItem property on the ViewModel with the value of that object in that row.
In that way, you don't even have to deal with the knowledge of the grid or the UI.
I hope that makes sense. I know it's a different approach and a different way of thinking coming from WinForms.
I found a solution posted by others in another thread in stackoverflow: WPF Toolkit DataGrid SelectionChanged Getting Cell Value
Try it.
private void dataGrid1_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
var item = e.AddedCells[0];
{
var col = item.Column as DataGridColumn;
var fc = col.GetCellContent(item.Item);
if (fc is CheckBox)
{
}
else if (fc is TextBlock && col.DisplayIndex == 0)
{
textBlock1.Text = (fc as TextBlock).Text;
}
}
}
sometimes binding to SelectedItem doesn't work (depending how crazy your Model has to be. I have to transpose the model, so everything is upside down and normal defaults don't work all the time.
given that,
in dataGrid selectedCellChanged you could access the bound object by:
assuming from previous example of Orders[] where each Order will have an array of SubOrders
foreach (var selectedCell in e.AddedCells)
{
var order = (Order)selectedCell.Item;
var subOrder = order.SubOrders[selectedCell.Column.DisplayIndex-1];
var someValue = subOrder.Value;
}

Databinding mode Explicit

I've got a listbox that contains a list of objects (lets say addresses)
The list box items source is bound to this observable collection
<ListBox x:Name="listDetails"
ItemsSource="{Binding}"
...
Then i've got a text box, this is bound to the name fild of the current object
<TextBox x:Name="textBoxName" Text="{Binding Name, UpdateSourceTrigger=Explicit}" />
So I expect that the Name property of my current object won't be change unless i explicitly update it..
However it is getting updated... any ideas to why?
Also this is in a window, if i close the window and reopen the window somehow the same selection on the listbox is preserved..
I'd expected once the window was closed then it would forget all about the current selection of it's listbox?
Figured out how to solve it, i added an event to the listgbox
private void OnListValueChanged(object sender, SelectionChangedEventArgs e)
{
ListBox lb = e.Source as ListBox;
if (lb != null)
{
object dc = null;
if (lb.SelectedIndex != -1)
dc = lb.Items[lb.SelectedIndex];
gridDetails.DataContext = dc;
}
}
the grid details context was set independently each and every time.

WPF ComboBox which updates its ItemsSource from the database as the Text property changes

I would like to have a ComboBox control on a form which will be used to search a list of investments as the user types. I can do this easily if I cache the entire collection of investments from the database on startup (currently 3,000 or so items), but I would prefer to not do that if it isn't necessary.
The behavior that I am trying to implement is:
The user types text into the editable ComboBox.
As the user enters each character, the database search function is triggered, narrowing down the search results with each successive keystroke.
As the search results are updated, the dropdown panel opens and displays the relevant matches
I have tried binding the Text property of the ComboBox to the InvestmentName (string) property on my ViewModel, and the ItemsSource property of the ComboBox to the InvestmentList (generic List) property on my ViewModel. When I do this, the Text property auto-completes from the ItemsSource, however the dropdown appears empty.
I have been able to achieve these results using a TextBox stacked on top of a ListBox, but it isn't very elegant and it takes up more screen real estate. I've also been able to get it to work with a TextBox stacked on top of a ComboBox, although the ComboBox steals the focus when the IsDropDownOpen property is set to "true" when there are valid search items. It also isn't very visually pleasing to use two controls for this.
I feel like I'm really close to getting it to work the way I want it to, but there is something which eludes me.
The XAML for this control is:
<ComboBox Height="23" Width="260" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left"
ItemsSource="{Binding InvestmentList}" DisplayMemberPath="FullName"
IsDropDownOpen="{Binding DoShowInvestmentList}"
ItemsPanel="{DynamicResource ItemsTemplate}" IsEditable="True"
Text="{Binding Path=InvestmentName, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
The relevant ViewModel properties are:
private bool _doShowInvestmentList;
public bool DoShowInvestmentList
{
get { return _doShowInvestmentList; }
set { if (_doShowInvestmentList != value) { _doShowInvestmentList = value; RaisePropertyChanged("DoShowInvestmentList"); } }
}
private List<PFInvestment> _investmentList;
public List<PFInvestment> InvestmentList
{
get { return _investmentList; }
set { if (_investmentList != value) { _investmentList = value; RaisePropertyChanged("InvestmentList"); } }
}
private string _investmentName;
public string InvestmentName
{
get { return _investmentName; }
set
{
if (_investmentName != value)
{
_investmentName = value;
this.InvestmentList = DataAccess.SearchInvestmentsByName(value).ToList();
if (this.InvestmentList != null && this.InvestmentList.Count > 0)
this.DoShowInvestmentList = true;
else
this.DoShowInvestmentList = false;
RaisePropertyChanged("InvestmentName");
}
}
}
I've done a fair bit of research on this, but I haven't quite found the answer yet.
Check out this great article on CodeProject by... me :)
A Reusable WPF Autocomplete TextBox
Look towards the end for the Google suggest example, it is similar to what you need, where every keypress triggers another query to the server.

Resources