How to accomplish this easy?
First of all, I know I can - but I don't want to add additional property string to my viewmodel.
If I make this:
<ComboBox
IsEnabled="False"
IsReadOnly="True"
ItemsSource="{Binding SomeListOfIdValuePairs}"
DisplayMemberPath="Value"
SelectedValuePath="Id"
SelectedValue="{Binding SomeViewModel.DesiredId, Mode=TwoWay}">
It works fine! However the Arrow of the combobox is disturbing me here and there is no easy way HideArrow="True" to hide it but to write a long whole combobox template.
So, how could I do something this instead?
<TextBlock
ItemsSource="{Binding SomeListOfIdValuePairs}"
DisplayMemberPath="Value"
SelectedValuePath="Id"
SelectedValue="{Binding SomeViewModel.DesiredId, Mode=TwoWay}" />
I guess you need the ComboBox features and therefore a simple TextBlock is not a solution.
You have to override the ComboBox template as the arrow is part of the ToggleButton.
You can of course find the arrow in the visual tree and remove it by collapsing it:
<ComboBox Loaded="OnComboBoxLoaded" />
private void OnComboBoxLoaded(object sender, EventArgs e)
{
Path arrow = FindVisualChild<Path>(sender as DependencyObject);
arrow.Visibility = Visibility.Collapsed;
}
You can find a FindVisualChild implementation at How to: Find DataTemplate-Generated Elements. You probably already have your own version.
Related
In WPF I have a DataGrid. I am using MVVM.
<DataGrid x:Name="dataGrid" ItemsSource="{Binding DisplayedRows}"
SelectedItem="{Binding SelectedRow}" SelectionUnit="FullRow">
If I change row then the setter of the SelectedRow runs as it should.
There is one exception.
One of the columns is a DataGridTemplateColumn which contains ComboBox with IsEditable="True"
<DataTemplate x:Key="CategoryEditingTemplate">
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}, Path=DataContext.AppData.CategoryObjects}"
SelectedItem="{Binding Category, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}"
DisplayMemberPath="FullName" IsEditable="True">
</ComboBox>
</DataTemplate>
If I start to edit the content of the ComboBox then I click to another editable ComboBox in another row then the setter of the SelectedRow does not run. I can edit the content of the second ComboBox with keyboard. If I click to another cell in the same row then the system realizes that the row has changed and the setter starts. I want the setter to run as soon as I leave a row.
Update 1
Editing mode of a ComboBox in a DataGrid
The input control (ComboBox) steals the focus which prevents the DataGrid control from actually selecting the row.
You could handle the PreviewMouseLeftButtonDown event for the input control to overcome this:
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) =>
dataGrid.SelectedItem = ((ComboBox)sender).DataContext;
XAML:
<DataTemplate x:Key="CategoryEditingTemplate">
<ComboBox PreviewMouseLeftButtonDown="OnPreviewMouseLeftButtonDown" ItemsSource ...
I have a DataTemplate with a Combobox inside a ListView like this
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.Dimensions, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"
DisplayMemberPath="Description"
SelectedValuePath="Id"
SelectedItem="{Binding DimName}"/>
</DataTemplate>
The combobox is populated correctly, but it doesn't select the content according to underlying field (ie. Dimension.DimName).
Here's how the ListView is declared
<ListView
Name="lstCriteria"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Margin="5"
AlternationCount="2"
ItemContainerStyle="{StaticResource CriteriaItemStyle}"
ItemsSource="{Binding Source={StaticResource CriteriaList}}" DockPanel.Dock="Top"
IsSynchronizedWithCurrentItem="True">
If I replace combobox with a TextBlock it does show the DimName Field's value, like this
<TextBox Text="{Binding DimName}"/>
What am I missing ?
Does your DimName come directly from the Dimensions list?
By default, if a ComboBox's Items is set to a custom class, it will compare the SelectedItem to an item in the ItemSource by reference. It will not match the item if they do not refer to the exact same object in memory, even if the object's data is the same.
To get around that you can either set SelectedValue and SelectedValuePath instead of SelectedItem on your ComboBox, or you can overwrite the Equals() method of your DimName class to return true if an object's data is equal
Edit
In regards to your comment below, is DimName a Dimension object? If so then setting SelectedItem should work fine. If it's an long you'll need to set SelectedValue, not SelectedItem. If it's something else, you may need a converter to convert it into a Dimension object
As Rachel suggested, I added a new Property to my class called Dimension of class Dimension like this
public Dimension Dimension
{
get { return _dimension; }
set { _dimension = value; }
}
and then set SelectedItem="{Binding Dimension}" as follows,
<ComboBox ItemsSource="{Binding DataContext.Dimensions, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"
DisplayMemberPath="Description"
SelectedValuePath="Id"
SelectedItem="{Binding Dimension}">
</ComboBox>
The silly mistake I was making was,
As my combobox was populating correctly, I was hoping that WPF will somehow match the DimName content with one of the items in the ComboBox automatically, which is not possible.
I have a listbox which displays Name property from an array of Movie objects
<ListBox Name="listBox1" SelectionChanged="listBox1_SelectionChanged">
<ItemsControl ItemsSource="{Binding}" >
<ItemsControl.ItemTemplate >
<DataTemplate >
<TextBlock Name="textBlock1" Text="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ListBox>
How can I access the text of the textBlock that's inside the ListBox in Code?
I must use the value of the Name property in my code
The selected item reported by the listbox exposes you the object that owns the Name property bound in the TextBlock. At this point the game is over.
When you do the above every textblock inside the itemscontrol has a name textblock1 that too with a scope limited to each item container.
If you want each of those textblocks individually, I usually do something like:
<TextBlock Text="{Binding Name}" Loaded="TextBlock_Loaded"/>
And in the code register those textboxes in whatever way you wish. A list probably,
List<TextBlock> TextBlockList = new List<TextBlock>();
private void TextBlock_Loaded(object sender, RoutedEventArgs e)
{
TextBlockList.Add((TextBlock)sender);
}
And for example, access the stuff as:
String FirstItem = TextBlockList.ElementAt(0).Text;
I have some problem to access the Window's DataContext from within a DataGrid.
The DataGrid is bound to a IBindingList:
public IBindingList Items{ get; set; }
private void initItems()
{
//ItemFactory is a Linq2SQL Context, Items is the view of availabe Items
this.Items = this.ItemFactory.Items.GetNewBindingList();
}
From within my xaml I try to get those data to fill a ComboBox:
<DataGridComboBoxColumn Header="Typ"
DisplayMemberPath="Description"
SelectedValuePath="ItemID"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Mode=OneWay, Path=DataContext.Items, UpdateSourceTrigger=PropertyChanged}" />
But it doesn't work. I tried out many variants already. The ComboBox gets not populated.
Any help greatly appreciated!
Note:
The following ComboBox in the same Window does work:
<ComboBox x:Name="workingCombo" ItemsSource="{Binding Path=Items}" DisplayMemberPath="Description" SelectedValuePath="ItemID" />
The DataGridComboBoxColumn is not directly connected to the visual tree and therefore the FindAncestor-operation will fail (and also the DataContext will not be inherited).
The most simple solution is to
create a ViewModel for each line and provide there in the ItemsSource
for the ComboBox.
Using a DataGridTemplateColumn and placing the ComboBox in the
DataTemplate helps.
Here
is a another post concerning this problem. And look also at this
post.
Lets say I've got a DataTemplate like so
<DataTemplate x:Key="Body">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=Person.Children}"></ComboBox>
<Button Click="Button_Click">Hello</Button>
</StackPanel>
</DataTemplate>
Which shows a list of ComboBoxes followed by a button.
Now, on clicking the button I need to discover the value in the combo next to the button pressed. I can get the data context as below but can't work out how to get the combos SelectedItem
private void Button_Click(object sender, RoutedEventArgs e)
{
// Can get the data context
var p = ((Button)sender).DataContext as Person;
// How to get the value in the combo ...?
}
you can also reference the combobox in your codebehind if you give it a name. However its cleaner to use a separate class to do your logic than the code behind. Such as a viewmodel.
then you could also do something like this...
<DataTemplate x:Key="Body">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=Person.Children}"
SelectedItem="{Binding Path=SelectedChild}"
IsSynchronizedWithCurrentItem="True"/>
<Button Command="{Binding Path=ButtonCommand}">Hello</Button>
</StackPanel>
</DataTemplate>
Instead of using the Click event handler, use a Command and bind the CommandParameter property to the ComboBox.SelectedItem. Then in your command's executed logic, you can use the parameter.