I created a combo box that has a text box inside. Now I want it to filter the corresponding items for me when I type in the text box. How can I do this?
<ComboBox Grid.Column="2" Grid.Row="2" Margin="0,10,10,27" Name="comboBox3" >
<TextBox Name="text2" Width="90" TextChanged="text2_TextChanged"
Style="{StaticResource ComboBoxEditableTextBox}"></TextBox>
<ComboBoxItem >salam</ComboBoxItem >
<ComboBoxItem >khobi</ComboBoxItem >
<ComboBoxItem>سلام</ComboBoxItem>
<ComboBoxItem>خوبی</ComboBoxItem>
<ComboBoxItem>عرض ادب</ComboBoxItem>
<ComboBoxItem>سپاسگذارم</ComboBoxItem>
<ComboBoxItem>مرسی</ComboBoxItem>
</ComboBox>
Code:
private void text2_TextChanged(object sender, TextChangedEventArgs e)
{
int c = comboBox3.Items.Count;
MessageBox.Show(c.ToString());
object[] st = new object[c];
List<string> listsource = new List<string>();
for (int i = 1; i <= 8; i++)
{
MessageBox.Show(comboBox3.Items[i].ToString());
// listsource.Add(ComboBox3.Items.IndexOf(ComboBox3.SelectedItem));
// listsource.Add(comboBox3.Items[i].ToString());
// foreach (ComboBoxItem cbi in comboBox3.Items)
}
comboBox2.ItemsSource = listsource;
}
No need to add a TextBox. If you set IsEditable="True" on the ComboBox then you can type a search term directly in the ComboBox.
To filter items handle KeyUp event on the ComboBox. IsDropDownOpen and StaysOpenOnEdit are set to true for testing purpose.
<ComboBox Grid.Column="2" Grid.Row="2" Margin="0,10,10,27" Name="comboBox3"
IsEditable="True" IsDropDownOpen="True" StaysOpenOnEdit="True" KeyUp="comboBox3_KeyUp" >
<ComboBoxItem >salam</ComboBoxItem >
<ComboBoxItem >khobi</ComboBoxItem >
<ComboBoxItem>سلام</ComboBoxItem>
<ComboBoxItem>خوبی</ComboBoxItem>
<ComboBoxItem>عرض ادب</ComboBoxItem>
<ComboBoxItem>سپاسگذارم</ComboBoxItem>
<ComboBoxItem>مرسی</ComboBoxItem>
</ComboBox>
Code behind
private void comboBox3_KeyUp(object sender, KeyEventArgs e)
{
var defaultView = (CollectionView)CollectionViewSource.GetDefaultView(comboBox3.Items);
var searchTerm = comboBox3.Text;
defaultView.Filter = (item) =>
{
var comboBoxItem = (ComboBoxItem)item;
if (string.IsNullOrEmpty(searchTerm) || ((string)comboBoxItem.Content).Contains(searchTerm))
{
return true;
}
else
{
return false;
}
};
defaultView.Refresh();
}
Related
I have a combo box that is editable, and i have a button that is enable when SelectedReplacement that binds to the combobox is not null and disable when it is. When it's null, i would input some random text to make the button enable, the problem is it wouldn't become enable when there I input text. making the Mode TwoWay doesn't help. i assumed setting the propertychangedevent would bind the new text to SelectedReplacement, but Im wrong, so any help is appreciated.
<ComboBox ItemsSource="{Binding SelectedError.Suggestions}"
Text="{m:Binding Path=SelectedError.SelectedReplacement, Mode=TwoWay}"
IsEditable="True"
HorizontalAlignment="Stretch"/>
i also tried to get the propertychanged
private void ViewModelPropertyChanged(SpellcheckViewModel sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(sender.SelectedError.SelectedReplacement))
{
_correctCommand?.Refresh();
}
}
I try to write a demo project to meet your requirement.
Mainly, the enable state is controlled by another boolean property IsButtonEnabled in the view model, the value for that property is controlled by InputText property which is controlled by the text you input in the ComboBox control.
Here is the UI:
<StackPanel Margin="10">
<ComboBox
x:Name="cmb"
IsEditable="True"
ItemsSource="{Binding AllItems}"
TextBoxBase.TextChanged="cmb_TextChanged"
TextSearch.TextPath="Name">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox
x:Name="hiddenTextBox"
Text="{Binding InputText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="Collapsed" />
<Button
x:Name="btn"
Margin="0,10,0,0"
Content="Show message"
IsEnabled="{Binding IsButtonEnabled}" />
</StackPanel>
And here is the main logic in the view model:
public ObservableCollection<Item> AllItems
{
get { return _allItems; }
set { _allItems = value; this.RaisePropertyChanged("AllItems"); }
}
public bool IsButtonEnabled
{
get { return _isButtonEnabled; }
set { _isButtonEnabled = value; this.RaisePropertyChanged("IsButtonEnabled"); }
}
/// <summary>
/// When InputValue changed, change the enable state of the button based on the current conditions
/// </summary>
public string InputText
{
get { return _inputText; }
set
{
_inputText = value;
this.RaisePropertyChanged("InputText");
// You can control the enable state of the button easily
if (AllItems.Any(item => item.Name == value))
{
// SelectedItem is not null
IsButtonEnabled = true;
}
else if (!string.IsNullOrEmpty(value))
{
// SelectedItem is null
IsButtonEnabled = true;
}
else
{
IsButtonEnabled = false;
}
}
}
Finally, here is the project: ComboBoxDemo
I have a combobox and the checkbox is present inside the combobox. I want the value of the multi selection checkbox. My code:
<ComboBox Name="LocationFilterComboBox" Width="100" SelectedItem="{Binding LocationValue}">
<ComboBox.ItemTemplate >
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<CheckBox Content="{Binding LocationValue}" IsChecked="{Binding ElementName=all, Path=IsChecked, Mode=TwoWay}" Width="120" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Back-end code:
//code to get the value
public partial class Location
{
#region Property
private string LOCAID;
public string LocaId
{
get
{
return LOCAID;
}
set
{
value = LOCAID;
}
}
private string LOCADESC;
public string LocationValue
{
get
{
return LOCADESC;
}
set
{
value = LOCADESC;
}
}
#endregion
}
}
//code for binding the location
public IList<Location> BindAllLocation()
{
if (Repository != null) Repository.Dispose();
Repository = GetInvoiceRepository();
IList<Location> locationList = Repository.GetLocations(((App)Application.Current).DataContextFactory);
return locationList;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
LocationFilterComboBox.ItemsSource = BindAllLocation();
}
I would not set the ItemsSource directly. Try binding it:
private ObservableCollection<Location> _locationList = new ObservableCollection<Location>();
public ObservableCollection<Location> LocationList
{
get { return _locationList; };
set
{
if (_locationList == value)
return;
_locationList = value;
OnPropertyChanged();
}
private Location _currentLocation;
public Location CurrentLocation
{
get { return _currentLocation; };
set
{
if (_currentLocation == value)
return;
_currentLocation = value;
OnPropertyChanged();
}
public IList BindAllLocation()
{
if (Repository != null) Repository.Dispose();
Repository = GetInvoiceRepository();
IList<Location> locationList = Repository.GetLocations(((App)Application.Current).DataContextFactory);
return locationList;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
foreach (var item in BindAllLocation())
LocationList.Add(item);
}
In Xaml:
<ComboBox ItemsSource="{Binding *binding to LocationList*}" Width="100" SelectedItem="{Binding CurrentLocation}">
<ComboBox.ItemTemplate >
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<CheckBox Content="{Binding LocationValue}" IsChecked="{Binding IsChecked, Mode=TwoWay}" Width="120" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I do not know the DataContext on the ComboBox so if you have Problems with that try this.
Then you can sort out the selected Values using:
var result = LocationList.Where(x => x.IsChecked);
Of course you have to have a IsChecked Property for that.
Since the IsChecked property of each CheckBox in the ComboBox is bound to the IsChecked property of the "all" CheckBox, you should be able to get the value directly from this one:
bool isChecked = all.IsChecked;
The other option would otherwise be to add a bool property to the Location class and bind to this one:
<CheckBox Content="{Binding LocationValue}" IsChecked="{Binding IsChecked}" Width="120" />
You could then get the value of each individual CheckBox in the ComboBox by simply iterating over its ItemsSource:
foreach(var location in LocationFilterComboBox.Items.OfType<Location>())
{
bool isChecked = location.IsChecked;
}
please have look on my code,
this become an infinite loop while calling lostfocus event of combo box
i need some data from database and user can select data only form list with typing options.
mainwindow.xaml
<Grid>
<TextBox x:Name="txt1" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120" Margin="112,10,0,0"/>
<ComboBox x:Name="cmb" GotFocus="cmbgotfocus" LostKeyboardFocus="cmblost" KeyDown="cmbkeydown" IsEditable="True" HorizontalAlignment="Left" VerticalAlignment="Top" Width="238" Margin="112,50,0,0" />
</Grid>
Class
private void cmbkeydown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return || e.Key == Key.Escape)
{
cmb.IsDropDownOpen = false;
}
else
{
cmb.IsDropDownOpen = true;
}
}
private void cmblost(object sender, RoutedEventArgs e)
{
if (cmb.SelectedIndex < 0 && cmb.Text!="" )
{
MessageBox.Show("Please select a valid data from list only", "Warning");
cmb.Focus();
}
}
If I got you correctly, you want user to type some text in the ComboBox, and if user's entry doesn't match any item, focus should remain on the TextBox present in the ComboBox.
<ComboBox x:Name="Cmb1" IsEditable="True"
Control.PreviewLostKeyboardFocus="Control_PreviewLostKeyboardFocus" ...>
Handler code :
private void Control_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (!(e.OriginalSource is TextBox)) return;
TextBox tb = (TextBox)e.OriginalSource;
if (Cmb1.SelectedIndex < 0)
{
Cmb1.Text = "";
e.Handled = true;
}
}
Please tell if this solves your issue.
I am writing a Silverlight for Windows Phone 7.5 app.
I want to reference the ContextMenu inside my LongListSelector because I want to set .IsOpen to false as soon as the ContextMenu Click event is called. My thought was that this should happen automatically but it does not.
One of my MenuItem's sets the visibility of a <Grid> from collapsed to visible which mimics a PopUp. Whilst the code executes fine and the Visibility does indeed change. The UI of the app does not show the Grid unless the ContextMenu closes.
My XAML of the LongListSelector which contains a ContextMenu called Menu that I wish to reference in the ContextMenuItem Click event.
<toolkit:LongListSelector x:Name="moviesLongList" Background="Transparent" IsFlatList="False" GroupHeaderTemplate="{StaticResource GroupHeaderTemplate}" GroupItemTemplate="{StaticResource GroupItemTemplate}" SelectionChanged="moviesLongList_SelectionChanged" GroupViewClosing="moviesLongList_GroupViewClosing" GroupViewOpened="moviesLongList_GroupViewOpened">
<toolkit:LongListSelector.GroupItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel/>
</ItemsPanelTemplate>
</toolkit:LongListSelector.GroupItemsPanel>
<toolkit:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Height="91" Margin="20,0,0,20" Orientation="Horizontal">
<toolkit:ContextMenuService.ContextMenu >
<toolkit:ContextMenu x:Name="Menu" Opened="ContextMenu_Opened" Loaded="Menu_Loaded" Unloaded="Menu_Unloaded">
<toolkit:ContextMenu.ItemTemplate>
<DataTemplate>
<toolkit:MenuItem Header="{Binding}" Click="ContextMenuButton_Click" LostFocus="MenuItem_LostFocus" />
</DataTemplate>
</toolkit:ContextMenu.ItemTemplate>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<Border HorizontalAlignment="Left" Width="61" Height="91" Background="{Binding ID, Converter={StaticResource ThumbImageConvert}}" />
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" Width="395">
<TextBlock x:Name="titleTextBox" Text="{Binding Title, Converter={StaticResource TitleConvert}}" Margin="6,0,6,0" d:LayoutOverrides="Width" FontSize="{StaticResource PhoneFontSizeLarge}" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<TextBlock x:Name="yearTextBox" Text="{Binding Year}" Margin="12,0,0,0" HorizontalAlignment="Left" FontSize="{StaticResource PhoneFontSizeMedium}" Foreground="{StaticResource PhoneSubtleBrush}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</toolkit:LongListSelector.ItemTemplate>
</toolkit:LongListSelector>
My code behind ContextMenuItem Click event
private void ContextMenuButton_Click(object sender, RoutedEventArgs e)
{
//
// This is where I want to set Menu.IsOpen = false to close the ContextMenu.
//
if ((sender as MenuItem).Header.ToString() == "lend movie")
{
DisableAppBarIcons();
LendPopUpOverlay.Visibility = System.Windows.Visibility.Visible;
}
if ((sender as MenuItem).Header.ToString() == "return to collection")
{
... Do stuff
}
if ((sender as MenuItem).Header.ToString() == "add to boxset")
{
... Do stuff
}
if ((sender as MenuItem).Header.ToString() == "delete")
{
... Do stuff
}
}
I set the ItemSource of the ContextMenu in the ContextMenu_Opened event. The fields are both of type List<String>.
private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
LentMovieObj = (sender as ContextMenu).DataContext as Movies;
if (LentMovieObj.IsLent)
{
(sender as ContextMenu).ItemsSource = menuItemsReturn;
}
else
{
(sender as ContextMenu).ItemsSource = menuItemsLendOut;
}
}
Not sure why the ContextMenu is not closing, but here are two solutions. The first is to get the parent of the MenuItem.
private T GetParentOfType<T>(DependencyObject obj) where T : class
{
if (obj == null) return null;
var parent = VisualTreeHelper.GetParent(obj);
while (parent != null)
{
if (parent is T) return parent as T;
parent = VisualTreeHelper.GetParent(parent);
}
return null;
}
Then get the Menu from your click handler
var menu = GetParentOfType<ContextMenu>(sender as MenuItem);
menu.IsOpen = false;
The second solution is to bind IsOpen to a backing viewmodel
<toolkit:ContextMenuService.ContextMenu >
<toolkit:ContextMenu x:Name="Menu" Opened="ContextMenu_Opened" Loaded="Menu_Loaded" Unloaded="Menu_Unloaded" IsOpen="{Binding IsOpen}" ItemsSource="{Binding Items}">
<toolkit:ContextMenu.ItemTemplate>
<DataTemplate>
<toolkit:MenuItem Header="{Binding}" Click="ContextMenuButton_Click" LostFocus="MenuItem_LostFocus" />
</DataTemplate>
</toolkit:ContextMenu.ItemTemplate>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
Change your open event:
private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
LentMovieObj = (sender as ContextMenu).DataContext as Movies;
if (LentMovieObj.IsLent)
{
(sender as ContextMenu).DataContext = new ContextMenuViewModel(menuItemsReturn);
}
else
{
(sender as ContextMenu).DataContext = ContextMenuViewModel(menuItemsLendOut);
}
}
Then a viewmodel
public class ContextMenuViewModel : INotifyPropertyChanged
{
private bool _isOpen = true;
public ContextMenuViewModel(IEnumerable<string> items)
{
Items = items;
}
public event PropertyChangedEventHandler PropertyChanged;
public bool IsOpen
{
get { return _isOpen; }
set { _isOpen = value; OnPropertyChanged("IsOpen"); }
}
public IEnumerable<String> Items { get; set; }
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then set the IsOpen property of your ViewModel to false;
I have listview with textbox in one column. Listview is bound to IEnumerable collection.
When I edit text in textboxes and click OK, bound collection has only original values.
I Snooped listview and see changes made in textboxes appear in listviewitem's objects while listview is on screen, but in OK button handler they all gone.
Here is window's xaml and code-behind (payee is result of linq-to-xml query produced by window's caller):
<Grid>
<ListView HorizontalAlignment="Stretch" Margin="0,38,0,0" Name="lvPayee"
VerticalAlignment="Stretch" GridViewColumnHeader.Click="lvPayee_Click" Background="AliceBlue">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Listed" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Margin="15,0,0,0" IsChecked="{Binding Listed}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Payee" Width="425">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="{x:Type TextBox}">
<TextBox Width="420" Text="{Binding Name}" Background="Transparent"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Use Count" Width="80" DisplayMemberBinding="{Binding UseCount}"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<CheckBox Content="Listed Only" Height="27" HorizontalAlignment="Left" Margin="57,12,0,0" Name="cbListedOnly" VerticalAlignment="Top" Width="129" IsChecked="True" Click="Listed_Clicked" />
<Button Content="OK" Height="27" HorizontalAlignment="Left" Margin="381,6,0,0" Name="btnOK" VerticalAlignment="Top" Width="89" Click="OK_Clicked" />
<Button Content="Cancel" Height="27" HorizontalAlignment="Left" Margin="493,6,0,0" Name="btnCancel" VerticalAlignment="Top" Width="80" IsCancel="True" />
</Grid>
public partial class PayeeManager : Window
{
private IEnumerable<Payee> payees = null;
private IEnumerable<Payee> payto = null;
private bool reverse = false;
private string lastColumn = "";
public PayeeManager(Window owner, IEnumerable<Payee> payees)
{
this.Owner = owner;
this.payees = payees;
InitializeComponent();
payto = from p in this.payees
where p.Listed == true
orderby p.Name
select p;
lvPayee.ItemsSource = payto;
}
public class Payee
{
public string Name { get; set; }
public int UseCount { get; set; }
public bool Listed { get; set; }
public string OldName { get; set; }
public bool OldListed { get; set; }
}
private void Listed_Clicked(object sender, RoutedEventArgs e)
{
payto = from p in this.payees
where cbListedOnly.IsChecked == true ? p.Listed == true : true
select p;
lvPayee.ItemsSource = payto;
}
private void lvPayee_Click(object sender, RoutedEventArgs e)
{
if (!(e.OriginalSource is GridViewColumnHeader)) return;
string header = (string)((GridViewColumnHeader)e.OriginalSource).Column.Header;
if (lastColumn != header)
{
lastColumn = header;
reverse = false;
}
else reverse = !reverse;
switch (header)
{
case "Listed":
payto = from p in this.payees
where cbListedOnly.IsChecked == true ? p.Listed == true : 1 == 1
orderby p.Listed
select p;
break;
case "Payee":
payto = from p in this.payees
where cbListedOnly.IsChecked == true ? p.Listed == true : 1 == 1
orderby p.Name
select p;
break;
case "Use Count":
payto = from p in this.payees
where cbListedOnly.IsChecked == true ? p.Listed == true : 1 == 1
orderby p.UseCount
select p;
break;
default:
return;
}
if (reverse) payto = payto.Reverse();
lvPayee.ItemsSource = payto;
}
private void OK_Clicked(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
}
I found the problem. I used as listview's ItemSource Linq query, and it was refreshed when accessed, loosing changes. I changed ItemSource to List, using ToList() on query and it works now.