I want to bind a ComboBox item to a string, but it does not work. My code is below.
Code in view:
<ComboBox
SelectedValuePath="content"
SelectedItem="{Binding ProductName}"
......
<ComboBoxItem>1111111111</ComboBoxItem>
<ComboBoxItem>2222222222222</ComboBoxItem>
<ComboBoxItem>333333333333</ComboBoxItem>
</ComboBox>
Code in view model:
private string _productName;
public string ProductName
{
get { return _productName; }
set
{
if (_productName != value)
{
_productName = value;
RaisePropertyChangedEvent("ProductName");
}
}
}
I assume you want to get the text from the ComboboxItem and not the ComboBoxItem iteself.
So you are binding the wrong information. This should work.
<ComboBox
SelectedValuePath="content"
Text="{Binding ProductName}"
......
<ComboBoxItem>1111111111</ComboBoxItem>
<ComboBoxItem>2222222222222</ComboBoxItem>
<ComboBoxItem>333333333333</ComboBoxItem>
</ComboBox>
Selected Item is of type ComboBoxItem, it will not accept String.
If you want to display product name in some other place try maybe something like this:
<TextBox Text="{Binding ElementName=my_ComboBox, Path=SelectedItem}"/>
Just a suggestion.
You already use a binding for the SelectedItem, why don't you set up another binding for the Items using the ItemsSource? So you would not need to add them statically in your view.
In addition you would not have the trouble to wonder whether you deal with instances of ComboxItem or String with your SelectedItem binding.
In case of the binding via ItemsSource you can be sure that the SelectedItem is a string.
Here is the code:
<ComboBox
SelectedValuePath="content"
SelectedItem="{Binding ProductName}"
ItemsSource="{Binding ProductNames}"
</ComboBox>
In your view model (or code behind) you define the ProductNames:
public String[] ProductNames
{
get
{
return _productNames;
}
set
{
if (_productNames!= value)
{
_productNames = value;
RaisePropertyChangedEvent("ProductNames");
}
}
}
String[] _productNames;
public NameOfConstructor()
{
List<String> productNames = new List<String>();
productNames.Add("A");
productNames.Add("B");
productNames.Add("C");
ProductNames = productNames.ToArray();
}
If it was possible that the list of names changes during execution, I would use a ObservableCollection<string> instead String[].
It should be like this
Create an observable collection of Product in View Model. Lets Say ProductCollection
Bind to the ComboBox ItemSource as given below
<ComboBox Name="productComboBox" Width="200" Height="30" ItemsSource="{Binding ProductCollection}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ProductName}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
if you want to show in textbox somewhere
use this
<TextBox Text="{Binding ElementName=productComboBox, Path=SelectedItem}"/>
Related
EDIT
Solution was actually a proper setting of SelectedItem, SelectedValue and SelectedValuePath properties.
<ComboBox
Grid.Column="1"
Padding="5"
DisplayMemberPath="PositionName"
IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Positions, Mode=OneWay}"
SelectedItem="{Binding SelectedOperatorPosition, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding SelectedOperatorPosition.PositionName}"
SelectedValuePath="PositionName" />
QUESTION
I am trying to bind ComboBox SelectedValue with DataGrid SelectedItem using MVVM pattern.
ComboBox ItemSource is Shifts property. I want it's SelectedValue to be bound with SelectedShift property, which is updated each time user selects another OperatorModel from DataGrid.
Although SelectedOperator setter sets SelectedShift value to SelectedOperator.Shift, ComboBox doesn't get updated.
View
Debugging ViewModel Property setter
My ViewModel:
private Operator selectedOperator;
public Operator SelectedOperator
{
get
{
return selectedOperator;
}
set
{
selectedOperator = value;
if (selectedOperator != null)
{
SelectedShift = selectedOperator.Shift;
}
OnPropertyChanged(nameof(SelectedOperator));
}
}
private Shift selectedShift;
public Shift SelectedShift
{
get
{
return selectedShift;
}
set
{
selectedShift = value;
OnPropertyChanged(nameof(SelectedShift));
}
}
XAML:
<ComboBox
Grid.Column="1"
Padding="5"
DisplayMemberPath="Name"
ItemsSource="{Binding Shifts}"
SelectedValue="{Binding SelectedShift}"
SelectedValuePath="Name" />
i think you should use SelectedItem="{Binding SelectedShift}
I write this combobox
<ComboBox
x:Name="ComboBoxRole"
SelectedItem="{Binding ApplicationModel.CategoryName}"
ItemsSource="{Binding Categories}"
Style="{StaticResource ComboBoxStyle}" Text="Choose"
/>
for this model
public class CategotyModel : INotifyPropertyChanged, IDataErrorInfo
{
private string id;
private string name;
public string Id
{
get => id;
private set
{
id = value;
NotifyPropertyChanged("Id");
}
}
public string Name
{
get => name;
private set
{
name = value;
NotifyPropertyChanged("Name");
}
}
}
for Item source create this property
public IList<CategotyModel> Categories
{
get
{
var categoriesDTO = _categoryManager.GetAllCategories();
this.categories = mapper.DefaultContext.Mapper.Map<IList<CategotyModel>>(categoriesDTO);
return categories;
}
}
it work fun, but I don't know how sent to combo just 1 parametre , because I take "AppStore.WPF.MVVMLight.Models.CategotyModel" object.
Note: I take the result from server. it's never mind.
(without foreach the IList<CategoryModel> and write to list of the string - i think it's bad way).
Edit
<ComboBox
x:Name="ComboBoxRole"
SelectedItem="{Binding ApplicationModel.CategoryName}"
SelectedValuePath="Name"
DisplayMemberPath="Name"
ItemsSource="{Binding Categories}"
Style="{StaticResource ComboBoxStyle}"
Text="Choose"
/>
You need to fix a few things in your ComboBox: To display the Name properties of the items, add DisplayMemberPath="Name". To select just the name property of the selected item instead of the whole object, add SelectedValuePath="Name", and bind ApplicationModel.CategoryName to SelectedValue instead of SelectedItem.
SelectedItem will still be the whole object, even when SelectedValuePath is in use.
<ComboBox
x:Name="ComboBoxRole"
SelectedValue="{Binding ApplicationModel.CategoryName}"
SelectedValuePath="Name"
DisplayMemberPath="Name"
ItemsSource="{Binding Categories}"
Style="{StaticResource ComboBoxStyle}"
Text="Choose"
/>
you want DisplayMemberPath="Name"
<ComboBox
x:Name="ComboBoxRole"
DisplayMemberPath="Name"
SelectedItem="{Binding ApplicationModel.CategoryName}"
ItemsSource="{Binding Categories}"
Style="{StaticResource ComboBoxStyle}"
Text="Choose"/>
I have a listbox and a textbox, I want the textbox show data according to the listbox selection. The problem is I already have the listbox binding to an object like this:
<ListBox x:Name = "listbox" SelectionMode="Single" ItemsSource="{Binding Products}" SelectedItem="{Binding SelectedProduct}" DisplayMemberPath="{Binding SelectedProduct}">
The textbox which I want to populate data is not the property of SelectedProduct that I binding to, it only has relation to the listbox index. For example:
private int[] _InputStartAddress = new int[20];
textbox.text = _InputStartAddress[listbox.SelectedIndex];
The ViewModel of SelectedProduct:
private Product selectedProduct;
public Product SelectedProduct
{
get { return selectedProduct; }
set
{
if (selectedProduct != value)
{
selectedProduct = value;
NotifyPropertyChanged();
}
}
}
What should I do to achieve this?Thanks!
bind directly to ListBox property using ElementName:
<ListBox Name="ListProducts" SelectionMode="Single" ItemsSource="{Binding Products}" SelectedItem="{Binding SelectedProduct}" DisplayMemberPath="{Binding SelectedProduct}">
<TextBox Text="{Binding Path=SelectedIndex, ElementName=ListProducts}"/>
I'm trying to make a DataGrid where the user can edit data and apply oder cancel changes afterwards.
Moreover one DataGridCell has a TemplateSelector to help the user to enter just the valid data. (DateTime, Boolean,...)
My Model has some properties. For my problem there are two relevant:
Model.Type and Model.Value
In CodeBehind my properties have the type:
enum Type;
string Value;
When the user edits the Value the TemplateSelector should get the right DataTemplate depending on Type
My XAML looks like this:
<DataGrid x:Name="datagrid"
ItemsSource="{Binding Variables}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutoGenerateColumns="False"
IsSynchronizedWithCurrentItem="True">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="{StaticResource VariablesType}"
ItemsSource="{Binding Source={StaticResource VariableTypes}, Mode=OneWay}"
SelectedItemBinding="{Binding Type}"
Width="80"/>
<DataGridTemplateColumn Header="{StaticResource VariablesValue}" Width="2*">
<DataGridTemplateColumn.CellEditingTemplateSelector>
<TemplateSelector:TemplateSelector_Variables>
<!--Definitin of Templates,..-->
</TemplateSelector:TemplateSelector_Variables>
</DataGridTemplateColumn.CellEditingTemplateSelector>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Let's get to the TemplateSelector:
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item == null)
return base.SelectTemplate(item, container);
RaVariableType VariablenType = ((RaVariable)item).Type;
//SelectingLogic
//Here I would need the CURRENT type but it gives me the type of my Model
}
I have alread had a solution:
If I use:
SelectedItemBinding="{Binding Type, UpdateSourceTrigger="PropertyChanged"}"
It works BUT I would like to handle an OK or Cancel of the User and this code changes my Model.
Another question:
How do you handle such a decition. (How do you update the Model - code of the command)
Thank you!
Are you using MVVM pattern. You need to handle it in View Model just bind button to the SelectedItem of the DataGrid and use it whenever the Command is invoked. Ideal way to do this kind of stuff is using commands (Basically MVVM pattern) you can create a command in your Data Object(ViewModel) and call Button.Command , So that there wont be any code behind like Button click.
Example is shown here
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<Button
Command="{Binding Path=DataContext.OKCommand,
RelativeSource= {RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}}">
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
View model code
RelayCommand _okCommand;
public ICommand okCommand
{
get
{
if (_ok == null)
{
_ok= new RelayCommand(param => this.Show());
}
return _ok;
}
}
private void Show()
{
if (Parent != null)
{
// Handle ok logic Here
}
}
Are you using any patterns like Ninject, Unity etc and entity framework for handling your db layer code ?. Your viewmodel will contain properties and the column names of your grid should come from model property. Once the property value changes you need to write save logic probably using EF or linq to sql db code. Save logic will be handled by seperate class and if the user clicks a button your view model should call the interface method and do the save. Instance of your class which handles save logic will be supplied by Unity or Ninject (depending on your implementation). Your dependencies will be injected into your view models, and your view models would expose properties that are bound to by the view.
For passing the selected item from grid to model
Create a Property in the ViewModel for saving the selected User: public User SelectedUser { get; set; }
Bind SelectedItem of the datagrid view to this Property: SelectedItem="{Binding SelectedUser}"
There are a few way to select items in the DataGrid. It just depends which one works best for your situation
First and most basic is SelectedIndex this will just select the Row at that index in the DataGrid
<DataGrid SelectedIndex="{Binding SelectedIndex}" />
private int _selectedIndex;
public int SelectedIndex
{
get { return _selectedIndex; }
set { _selectedIndex = value; NotifyPropertyChanged("SelectedIndex"); }
}
SelectedIndex = 2;
SelectedItem will select the row that matches the row you set
<DataGrid SelectedItem="{Binding SelectedRow}" />
private DataRow _selectedRow;
public DataRow SelectedRow
{
get { return _selectedRow; }
set { _selectedRow = value; NotifyPropertyChanged("SelectedRow");}
}
SelectedRow = items.First(x => x.whatever == something);
The most common one is SelectedValue with SelectedValuePath set, in this case you set the column you want to select with and then to can select the row by setting the corresponding value
<DataGrid SelectedValuePath="Size Quantity" SelectedValue="{Binding SelectionValue}"
private string _selectedValue
public string SelectionValue
{
get { return _selectedValue; }
set { _selectedValue = value; NotifyPropertyChanged("SelectionValue"); }
}
SelectionValue = "Blue";
XAML Example
<Window x:Class="WpfApplication21.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="202" Width="232" Name="UI">
<Grid DataContext="{Binding ElementName=UI}">
<DataGrid SelectedValuePath="Size Quantity"
SelectedValue="{Binding SelectionValue}"
SelectedIndex="{Binding SelectedIndex}"
ItemsSource="{Binding SizeQuantityTable}"
AutoGenerateColumns="True"
Margin="0,0,0,41" />
<StackPanel Orientation="Horizontal" Height="37" VerticalAlignment="Bottom" >
<Button Content="SelectedIndex" Height="26" Width="107" Click="Button_Click_1"/>
<Button Content="SelectedValue" Height="26" Width="107" Click="Button_Click_2"/>
</StackPanel>
</Grid>
</Window>
I'm databinding a listbox to an object that contains two array of strings. Each listbox item is set to a data template made up of a textbox and a combo box. The first string array is bound to the list, and the second string array is bound to the combo box. Well, at least that's I'm trying to achieve. The problem is that I can't figure out the binding syntax to set the second array to the combo box. Here's what I have:
The first thing is my class with my two string arrays. Pretty straightforward. Please note that the string array content is there for simplicity.
public class JobAssignments
{
public JobAssignments()
{
m_people.Add("John");
m_people.Add("Bill");
m_people.Add("Frank");
m_people.Add("Steve");
m_jobs.Add("Architect");
m_jobs.Add("Teacher");
m_jobs.Add("Carpenter");
m_jobs.Add("Plumber");
}
private List<string> m_people = new List<string>();
public List<string> People { get { return m_people; } set { m_people = value; } }
private List<string> m_jobs = new List<string>();
public List<string> Jobs { get { return m_jobs; } set { m_jobs = value; } }
};
In code, I set an instance of this class as the datacontext of this listbox:
<ListBox x:Name="listBox"
Grid.Row="0"
HorizontalContentAlignment="Stretch"
DataContext="{Binding}"
ItemsSource="{Binding People}"
ItemTemplate="{StaticResource JobAssignmentDataTemplate}">
</ListBox>
With a data template that looks like this:
<DataTemplate x:Key="JobAssignmentDataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding}"/>
<ComboBox Grid.Column="2"
SelectedIndex="0"
ItemsSource="{Binding Jobs ???? }"/>
</Grid>
</DataTemplate>
What I usually get out my experiments is a list box of People, but the combo box of each list item is empty.
I can get it to work if I use
ItemsSource="{Binding ElementName=listBox, Path=DataContext.Jobs }"/>
but I don't want to use ElementName as it hardcodes the source of the array a specific listbox which I'd like to avoid.
Trying something like
ItemsSource="{Binding RelativeSource={RelativeSource Self}, Path=Parent.Jobs}"/>
Doesn't seem to work either as it's looking for Jobs inside the Grid.
A little push in the right direction would help greatly.
Thanks!
I had roughly the same problem as you except I had a listbox within a tabcontrol. I was able to solve it by using getting the tab control that contained the listbox. Sample code as follows:
<TabControl ItemsSource="{Binding Groups, Mode=TwoWay}" SelectedItem="{Binding SelectedGroup, Mode=TwoWay}">
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=TabControl, AncestorLevel=1}, Path=DataContext.ItemsInGroup, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemGroup}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
When you are at DataTemplate that represents just one People your JobAssignments instance is not there. Try the below
ItemsSource="{Binding ElementName= listBox, Path=Jobs}"/>
But I dont recommend the above step. You need to refactor your ViewModel logic. I guess 'Jobs' can be a static instance so that you can do a x:Static binding
ItemsSource="{Binding Source={x:Static JobAssignments}, Path=Jobs}"
You probably need to modify your object structure to work like this:
public class Person
{
public string Name;
public IEnumerable<string> Jobs;
}
public class JobAssignments
{
public JobAssignments()
{
Jobs.Add("Architect");
...
People.Add(new Person() { Name = "Bob", Jobs = Jobs });
...
}
private List<Person> m_people = new List<Person>();
public List<Person> People { get { return m_people; } }
private List<string> m_jobs = new List<string>();
public List<string> Jobs { get { return m_jobs; } }
}
Then, you can remove the question marks, and things should work.