I have a combobox which allows users to select from a collection of images. Because of the size of the images I would like to not display the image in the combobox once an image has been selected. I simply want nothing displayed in the combobox when an item is selected.
So far I have tried setting the selectedImageIndex to -1 once the selectedImageSource has been set when the user makes a selection however this did not work as the first image at [0] is still displayed in the combobox by default. I am using MVVM.
XAML
<ComboBox Grid.Row="1" SelectedIndex="{Binding SelectedImageIndex}" ItemsSource="{Binding SymbolImageCollection}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Img}" Width="50" Height="50"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="300" HorizontalAlignment="Left"/>
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
View Model
public ObservableCollection<SymbolImage> SymbolImageCollection { get { return AppearanceLayerProperties.Instance.SymbolImageCollection; } }
private string _selectedImageSource;
public string SelectedImageSource
{
get { return _selectedImageSource; }
set
{
SetProperty(ref _selectedImageSource, value);
//SelectedImageIndex = -1;
}
}
private int _selectedImageIndex;
public int SelectedImageIndex
{
get { return _selectedImageIndex; }
set
{
var selectedImage = AppearanceLayerProperties.Instance.SymbolImageCollection[value].ImgSource;
SelectedImageSource = selectedImage;
SetProperty(ref _selectedImageIndex, -1);
}
}
Changing the selection back to null after selecting an item of the ComboBox is not good practice, because if you need to use the selected value, it is not available anymore.
A solution could be to have a different template for the selected item of the ComboBox. That way, you could remove the image, but in its place put something else, like text, so the user as an idea of what item is selected. Here is a previous StackOverflow post explaining how to do this :
Can I use a different Template for the selected item in a WPF ComboBox than for the items in the dropdown part?
I hope this helps!
Related
Please help!
I did many research on the internet, but didn't find any solution for my question.
I have a form with foods. There is a grid on the form and with it I can navigate on the food table. There is a combobox on the screen (not in the grid) which contains the categories. The combobox is filled up with the categories from categories table. When I change the record on the datagrid every field updated on the form except the combobox.
first record
second record
So my question is: what I have to do to refresh the combobox, to show the saved category when I navigate on the grid?
In the category table the category has "id" field and in the food table there is a "categoryid" field.
I have this in the xaml file:
<ComboBox x:Name="categoryComboBox" Grid.Row="5" Grid.Column="1" Margin="3,4,20,0" Grid.ColumnSpan="3"
ItemsSource="{Binding Source={StaticResource categoryViewSource}}"
SelectedValuePath="CategoryId"
DisplayMemberPath="CatName"
SelectedItem="{Binding CategoryId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="25" VerticalAlignment="Top">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
As I can see you have a small error in your code. I should use the SelectedValue instead of SelectedItem. So change it and I think it will work properly. And in addition You don't need any workaround with the scaling as I suggested before.
Here is example:
XAML
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" >
<ComboBox x:Name="categoryComboBox" ItemsSource="{Binding Source={StaticResource categoryViewSource}}"
SelectedValuePath="CategoryId"
DisplayMemberPath="CatName"
SelectedValue="{Binding CategoryId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="25" VerticalAlignment="Top">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
<Button Content="Change Category" Command="{Binding SelectionChangedCommand}"></Button>
</StackPanel>
DataContext
public class MyComboDataContext:BaseObservableObject
{
private int _categoryId;
private ICommand _selectionChangedCommand;
public MyComboDataContext()
{
CategoryId = 1;
}
public int CategoryId
{
get { return _categoryId; }
set
{
_categoryId = value;
OnPropertyChanged();
}
}
public ICommand SelectionChangedCommand
{
get { return _selectionChangedCommand ?? (_selectionChangedCommand = new RelayCommand(SelectionChanger)); }
}
private void SelectionChanger()
{
CategoryId += 1;
if (CategoryId == 4)
CategoryId = 1;
}
}
Explanations:
First of all, this is an example that is simulating the update of a combo. Here the combobox selected value is changed on each button click. In your example the category selection should effect the combo selected value. So each time the grid category selection happens you should push selected category id into the property the combo SelectedValue is bound to it.
In order to help you please update your question with next things:
Are there any Binding expression errors in your output window?
How do you handle a DataGrid selection in your code.
How the data grid selection effects the combobox SelectedValue(it have to be selected value)?
Let me know if you need more help. And feel free to mark you question as answered if my answer was helpful.
I want to use combo box and the following code is working but now I want to
add to the combo box header default value ,i.e. there is value and like
Item and when you open it you have the option to change it,how I can do that?
<ComboBox ItemsSource="{Binding Items}" SelectedValue="{Binding SelectedItem}"
Name="comboBox1" Text="Item" Grid.Column="3" Grid.Row="2" />
the code
private List<String> _items;
private String _selectedItem;
private String _selectedBusinessItem;
public List<String> Items
{
get { return _items; }
set
{
_items = value;
OnPropertyChanged("Items");
}
}
public String SelectedItem1
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged("Items");
}
}
private void InitCombo()
{
Items = new List<string> { "item", "Item2", "Item3" };
SelectedItem1 = Items[0];
}
It's hard to understand you are asking, but I think you are just looking for the ComboBox to show the first value in the collection of Items.
I believe you can do this a few ways.
First you need to fix SelectedValue binding to match your property name, and drop the Text="Item":
<ComboBox ItemsSource="{Binding Items}" SelectedValue="{Binding SelectedItem1}"
Name="comboBox1" Grid.Column="3" Grid.Row="2" />
From your code you can set the SelectedValue1 property that you have to any of the string items. Examples of this:
SelectedValue1 = "item";
-or-
SelectedValue1 = Items.FirstOrDefault();
I used FirstOrDefault as a safety incase these items didn't exist.
-or-
SelectedValue1 = Items[0];
And there are several more options here. But I'm going to try and limit the scope of the answer.
Also, you should be able to set the ComboBox.SelectedIndex to 0.
<ComboBox ItemsSource="{Binding Items}" SelectedValue="{Binding SelectedItem1}"
Name="comboBox1" Grid.Column="3" Grid.Row="2"
SelectedIndex="0"/>
I think that you're talking about the ComboBox.Text Property... from the linked page:
Gets or sets the text of the currently selected item
This is not a free field that you can display a message in. It displays the value of the currently selected item from the ComboBox.Items collection. If the text is not in one of the items, then this TextBox 'should' not display that value.
However, there are always workarounds. The correct way to do it would be to define a new ControlTemplate for the ComboBox that contains a TextBlock that is overlayed on top of the selected item TextBox and hidden when required.
Some people think that that is too much work though and so you can find a number of alternative solutions in the How to display default text “--Select Team --” in combo box on pageload in WPF? post here on StackOverflow.
List Items = new List { "item", "Item 2", "Item 3" };
Set the Selected Index = 0, It will select the first element in the combo box Item-source
XAML:
<ComboBox ItemsSource="{Binding Items}"
Name="comboBox1" Grid.Column="3" Grid.Row="2"
SelectedIndex="0"/>
Thank you for reading my question.
The situation:
I have an ObservableCollection<CheckableListItem<T>> CheckableItems
The class CheckableListItem<T> has 2 elements: bool IsChecked and T Item.
The class acts as a wrapper class that adds a checkbox to each Item.
In this project the Item passed has a string element called Name.
How it is displayed in XAML code:
<ListBox ItemsSource="{Binding CheckableItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Path=Item.Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This gives me a Listbox with every entry containing a checkbox and the content of the checkbox is the Item.Name string.
The problem:
I have added a textbox in XAML <TextBox></TextBox> And Now I would like the listbox to only display the objects from the observable collection which match the text from the TextBox.
How I think it could be done:
Create a view of some kind to bind to the listbox and update the view with only the objects that match the search criteria. If no text is entered in the searchbox then all object must be displayed, If only the letter E for example is entered, only the objects containing a letter E in the Item.Name property should be displayed.
I think best would be to bind the text to a string variable in my datacontext and fire an event each time the string changes, something like this:
string SearchString
<TextBox Text="{Binding Path=SearchString, UpdateSourceTrigger=PropertyChanged}" TextChanged="TextBox_TextChanged" />
The function:
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
// Called each time the text changes, perform search here?
}
I just lack the knowledge of WPF syntax for how to create this or how to google the right terms.
Edit:
I now have an ICollectionView checkableItemsView of my ObservableCollection<CheckableListItem<T>> CheckableItems But how to filter it on the Item.Name property?
The binding works, just the filtering I need help with:
<ListBox Grid.Column="1" ItemsSource="{Binding CheckableItemView}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Path=Item.Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Any input is welcome. Thanks in advance.
Rather than binding directly to your ObservableCollection, you could bind to an ICollectionView.
This would allow you to set a Filter property (using a predicate) that would filter out entries at the UI level, without changing the underlying collection.
Take a look at the filtering section of this page:
http://wpftutorial.net/DataViews.html
edited to add example of filtering:
ICollectionView _customerView = CollectionViewSource.GetDefaultView(customers);
_customerView.Filter = CustomerFilter
private bool CustomerFilter(object item)
{
Customer customer = item as Customer;
return customer.Name.Contains( _searchString );
}
.After some more research I came with the following solution:
Credits go to Paul for pointing me in the right direction.
Source: http://jacobmsaylor.com/?p=1270
private bool CustomFilter(object item)
{
CheckableListItem<Item> checkableItem = item as CheckableListItem<Item>;
if (checkableItem != null && checkableItem.Item.Name.Contains(SearchString))
{
return true;
}
return false;
}
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
checkableItemsView.Filter = CustomFilter;
}
let me start by introducing my current setup:
I have a ListView that binds its SelectedItem property to the ViewModel, like this:
<ListView Name="FileListView" ItemsSource="{Binding ImageList}"
SelectionChanged="ImageSelectionChanged"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<view:FileListItem />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
It's item template (view:FileListItem) is the following:
<Grid MouseDown="FileListItemMouseDown" KeyDown="FileListItemKeyDown">
....
<TextBlock Name="NewNameTextBlock"
Text="{Binding NewName}"
Grid.Column="2"
Visibility="{Binding TextBlockVisibility}" />
<TextBox Name="NewNameTextBox"
Text="{Binding NewName, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="2"
Visibility="{Binding TextBoxVisibility}" />
</Grid>
The idea here is to switch on the TextBox and switch off the TextBlock when the corresponding ListView item is being edited. This works ok, but when I hit a particular key, I want the ListView to select the next item and put that item into editing mode. I catch the KeyDown event as seen above in the ItemTemplate and broadcast a message, which is caught in the DataContext of my ListView like this:
public ImageFile SelectedItem {
get { return _selectedItem; }
set { _selectedItem = value; NotifyPropertyChanged("SelectedItem"); }
}
public void SelectAndEditThisHandler (object x)
{
ImageFile file = x as ImageFile;
SelectedItem = file;
}
The result is that the selection actually changes for a split second, but then it changes back to the previous selection. I suspect some other UI elements might be handling my key-presses and doing something to change the selection back, but I can't figure out which elements and how to pinpoint them.
Any help would be greatly appreciated! Thanks!
EDIT:
As requested, the SelectionChanged handler:
private void ImageSelectionChanged(object sender, SelectionChangedEventArgs e)
{
System.Collections.IList filelist = FileListView.SelectedItems;
if (filelist.Count == 1)
{
ImageFile selectedFile = FileListView.SelectedItem as ImageFile;
Mediator.Instance.NotifyColleagues(Mediator.Operations.ImagePathSelected, selectedFile.OriginalPath);
}
}
The mediator message broadcast doesn't do anything related to these controls/this problem at all.
I'm a bit puzzled:
this works:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Label Content="Rol" />
<ComboBox ItemTemplate="{StaticResource listRollen}"
Height="23" Width="150"
SelectedItem="{Binding Path=SelectedRol, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding Path=allRollen, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
and the property for SelectedRol is:
public TblRollen SelectedRol
{
get { return _selectedRol; }
set
{
if (_selectedRol != value)
{
_selectedRol = value;
OnPropertyChanged("SelectedRol");
}
}
}
But this doesn't work:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Label Content="Soort" />
<ComboBox ItemTemplate="{StaticResource listSoorten}"
Height="23" Width="150"
ItemsSource="{Binding Path=allSoorten}"
SelectedItem="{Binding Path=SelectedProduct, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
with following property SelectedProduct:
public TblProduktSoorten SelectedProduct
{
get { return _selectedPSoort; }
set
{
if (_selectedPSoort != value)
{
_selectedPSoort = value;
OnPropertyChanged("SelectedProduct");
}
}
}
somewhere in my code I set SelectedProduct = p.TblProduktSoorten and while debugging, I see the property gets set correctly...
Combobox inside a DataGrid?
If the combobox is in a DataGrid you must add this :
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged
See this : https://stackoverflow.com/a/5669426/16940
Try to use not selected item but value path look at the code sample
<ComboBox Name="projectcomboBox" ItemsSource="{Binding Path=Projects}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="FullName"
SelectedValuePath="Name" SelectedIndex="0" Grid.Row="1" Visibility="Visible" Canvas.Left="10" Canvas.Top="24" Margin="11,6,13,10">
</ComboBox>
the binding property is
public ObservableCollection<Project> Projects
{
get { return projects; }
set
{
projects = value;
RaisePropertyChanged("Projects");
}
}
This might be related to the fact that apparently attribute order does matter, in your second case the ItemsSource and SelectedItem declarations are swapped.
If you set the SelectedProduct property when SelectedProduct is changed in the property changed event handler, you need to set this property asynchronously.
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedProduct")
App.Current.Dispatcher.InvokeAsync(() => SelectedProduct = somevalue);
}
My problem was caused by my own tired brain. Same symptom, maybe it will kick you into seeing your problem.
Setting the SelectedItem must be given an item in the List!! (duhh) Normally this happens naturally but I had a case I got a "Role" from another service (Same object type) and was trying to set it and expecting the combobox to change! ;(
instead of -
Roles = roles;
CurrentRole = role;
remember to do this -
Roles = roles;
CurrentRole = roles.FirstOrDefault(e=> e.ID == role.ID); //(System.Linq)
I don't know if you fixed it yet, but I encountered the same issue today.
It was fixed by making sure the collection for selecteditems was an ObservableCollection.
I think this problem is caused by the type of ItemSource and SelectedItem is mitchmatched.
For example, if the ItemSource is binded to a List of int and the SelectedItem is binded to a string. If you set selected item to null or empty string, the combobox cannot know what item is selected. So the combobox will show nothing.
This might be old but I have not seen the trick that did it for me; I had to add NotifyOnSourceupdate=true to my SelectedItem in the ComboBox
This had me stumped for a while inside a DataGrid, using SelectedItem. Everything was fine but I am deserializing the app state which loads the items and also has a selected item. The collection was there but the selected isn't actually visible until I used the Text="{Binding Path=Score.SelectedResult.Offset}"
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ToolTip="Score offset results"
ItemsSource="{Binding Score.SearchResults,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Score.SelectedResult, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Path=Score.SelectedResult.Offset}"
SelectedValuePath="Offset"
DisplayMemberPath="Offset"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>