I have this column/code in my DataGrid:
<sdk:DataGridTemplateColumn CanUserReorder="True" CanUserResize="True" CanUserSort="True" Width="Auto" Header="Province/State">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=SelectedProvince.ProvinceName, Mode=OneWay}"/>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
<sdk:DataG ridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=ProvinceList, Mode=TwoWay}"
SelectedItem="{Binding Path=SelectedProvince, Mode=TwoWay}"
DisplayMemberPath="ProvinceName" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
Then this is the code behind(I have cut out the unrelated code):
public class BatchSeedingAddressVM : ViewModelBase
{
public BatchSeedingAddressVM()
{
_saveAddressButtonCommand = new RelayCommand(SaveAddressButtonCommand_OnExecute);
CreateJurisdictionList();
}
public BatchSeedingAddressVM(int? batchSeedingAddressOID, string address1, string address2, string city, string postalCode, string province2Code)
{
_saveAddressButtonCommand = new RelayCommand(SaveAddressButtonCommand_OnExecute);
CreateJurisdictionList();
BatchSeedingAddressOID = batchSeedingAddressOID;
Address1 = address1;
Address2 = address2;
City = city;
PostalCode = postalCode;
//SelectedProvince2Code = province2Code;
SelectedProvince = _provinceList.Where(x => x.Province2Code == province2Code).FirstOrDefault();
}
private ObservableCollection<Province> _provinceList = new ObservableCollection<Province>();
public ObservableCollection<Province> ProvinceList
{
get
{
return _provinceList;
}
set
{
if (_provinceList != value)
{
_provinceList = value;
RaisePropertyChanged("ProvinceList");
}
}
}
private Province _selectedProvince;
[Display(Name = "Province")]
public Province SelectedProvince
{
get
{
return _selectedProvince;
}
set
{
if (_selectedProvince != value && value != null)
{
_selectedProvince = value;
RaisePropertyChanged("SelectedProvince");
}
}
}
}
Here is the issue: the DataGrid cell has 2 templates: CellTemplate and CellEditingTemplate. When the CellTemplate is active the textbox in it picks up the SelectedProvince as planned and displays the name of the province. The problem is that when CellEditingTemplate becomes active the ComboBox in it does not pick up the (default)SelectedItem value and displays an empty box.
Is there something I am missing? How the binding has to be setup so that it would be possible to set the default SelectedItem in the combobox in CellEditingTemplate?
Thanks much in advance!
I think TwoWay binding for ItemsSource can be the problem.
ItemsSource="{Binding Path=ProvinceList, Mode=TwoWay}"
I suggest to change it to OneTime or OneWay depending on your design.
I might be wrong, but I think that if you're using SelectedValuePath, you need to use SelectedValue instead of SelectedItem, so change this:
SelectedItem="{Binding Path=SelectedProvince, Mode=TwoWay}"
to this:
SelectedValue="{Binding Path=SelectedProvince, Mode=TwoWay}"
Related
I have class DimensionType, it has properties Name, Id, etc. I constructed Property in my ViewModel = "dimStyleId" to retrieve selected form ComboBox. I am getting null in this property although I checked it in TextBlock and get it.
<!--Dimension Type Combobox-->
<ComboBox x:Name="DimensionType"
ItemsSource="{Binding dimTypes , Mode=TwoWay}"
SelectedValue="{Binding dimStyleId , Mode=TwoWay}"
SelectedValuePath="DimensionType"
DisplayMemberPath="Name"
Padding="3" />
and here is my VM Class
public class GridsDimViewModel : INotifyPropertyChanged
{
public ElementId dimensionType;
private ElementId _dimStyleId { get; set; }
public ElementId dimStyleId
{
get
{
return _dimStyleId;
}
set
{
if (_dimStyleId != value)
{
_dimStyleId = value;
NotifyPropertyChanged(nameof(dimStyleId));
}
}
}
}
and here is my check textbox which gets the id in it
<TextBlock Text="{Binding dimStyleId}"
Padding="3" />
Swap
SelectedValuePath="DimensionType"
to
SelectedValuePath="Id"
I have seen a couple of posts in this site similar to my issue like this one and this one But I haven't gotten this to work for me.
I have a datagrid bound to by a list of objects of type Foo and I have a combobox added for each row. The ComboBox ItemSource is not a part of the Foo class but rather it's created in the view model. I know doing so means that this combobox is the same for every row but isn't there a way in my Xaml to filter SelectedItem to just the row?
Here is my Xaml:
<DataGridTemplateColumn Header="Foo Column" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Mode=OneWay,Path=DataContext.FooCollection,
RelativeSource= {RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}}"
SelectedItem="{Binding DataContext.SelectedComboBoxItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged,
RelativeSource= {RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}}">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Here is my ViewModel:
public ObservableCollection<string> FooCollection
{
get
{
return _FooCollection;
}
set
{
if (_FooCollection != value)
{
_FooCollection = value;
RaisePropertyChanged(nameof(FooCollection));
}
}
}
private ObservableCollection<string> _FooCollection = new ObservableCollection<string>();
public string SelectedComboBoxItem
{
get
{
return __SelectedComboBoxItem;
}
set
{
if (_SelectedComboBoxItem != value)
{
_SelectedComboBoxItem = value;
RaisePropertyChanged(nameof(SelectedComboBoxItem));
}
}
}
private string _SelectedComboBoxItem = string.Empty;
I am seeing my combobox collection populated but when I make a selection every other combobox gets the same value. Can anyone help me understand what I am doing wrong? Many thanks.
To make your code working you'll need to bind the SelectedComboBoxItem to DataGrid item. In your case this is a Foo type
I have a datagrid bound to by a list of objects of type Foo
Place this code to Foo class
public string SelectedComboBoxItem
{
get
{
return __SelectedComboBoxItem;
}
set
{
if (_SelectedComboBoxItem != value)
{
_SelectedComboBoxItem = value;
RaisePropertyChanged(nameof(SelectedComboBoxItem));
}
}
}
private string _SelectedComboBoxItem = string.Empty;
and update your binding according that
SelectedItem="{Binding SelectedComboBoxItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
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"/>
Goodday,
I want my combobox to select the first item in it. I am using C# and WPF. I read the data from a DataSet. To fill the combobox:
DataTable sitesTable = clGast.SelectAll().Tables[0];
cbGastid.ItemsSource = sitesTable.DefaultView;
Combo box XAML code:
<ComboBox
Name="cbGastid"
ItemsSource="{Binding}"
DisplayMemberPath="Description"
SelectedItem="{Binding Path=id}"
IsSynchronizedWithCurrentItem="True" />
If I try:
cbGastid.SelectedIndex = 0;
It doesn't work.
Update your XAML with this:
<ComboBox
Name="cbGastid"
ItemsSource="{Binding}"
DisplayMemberPath="Description"
SelectedItem="{Binding Path=id}"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="0" /> // Add me!
Try this, instead of SelectedIndex
cbGastid.SelectedItem = sitesTable.DefaultView.[0][0]; // Assuming you have items here.
or set it in Xaml
<ComboBox
Name="cbGastid"
ItemsSource="{Binding}"
DisplayMemberPath="Description"
SelectedItem="{Binding Path=id}"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="0" />
It works for me if I add a SelectedIndex Property in my VM with the proper binding in the xaml. This is in addition to the ItemSource and the SelectedItem. This way SelectedIndex defaults to 0 and I got what I wanted.
public List<string> ItemSource { get; } = new List<string> { "Item1", "Item2", "Item3" };
public int TheSelectedIndex { get; set; }
string _theSelectedItem = null;
public string TheSelectedItem
{
get { return this._theSelectedItem; }
set
{
this._theSelectedItem = value;
this.RaisePropertyChangedEvent("TheSelectedItem");
}
}
And the proper binding in the xaml;
<ComboBox MaxHeight="25" Margin="5,5,5,0"
ItemsSource="{Binding ItemSource}"
SelectedItem="{Binding TheSelectedItem, Mode=TwoWay}"
SelectedIndex="{Binding TheSelectedIndex}" />
Update your XAML with this code :
<ComboBox
Name="cbGastid"
ItemsSource="{Binding}"
DisplayMemberPath="Description"
SelectedItem="{Binding Path=id, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"
IsSynchronizedWithCurrentItem="True" />
Hope it works :)
Try this,
remove from de C# code the following line:
cbGastid.ItemsSource = sitesTable.DefaultView;
and add this:
cbGastid.DataContext = sitesTable.DefaultView
Try this..
int selectedIndex = 0;
cbGastid.SelectedItem = cbGastid.Items.GetItemAt(selectedIndex);
XAML Code:
<ComboBox
Name="cbGastid"
ItemsSource="{Binding}"
DisplayMemberPath="Description"
SelectedItem="{Binding Path=id}"
IsSynchronizedWithCurrentItem="True" />
This works for me... Given an Authors and Books table with a one-to-many relationship. The XAML Looks like this:
<ComboBox DisplayMemberPath="AuthorName" ItemsSource="{Binding Authors}" Name="ComboBoxAuthors"
SelectedItem="{Binding SelectedAuthor}"
IsSynchronizedWithCurrentItem="True" Grid.Row="0" Grid.Column="0"/>
<ComboBox DisplayMemberPath="BookTitle" ItemsSource="{Binding Books}" Name="ComboBoxBooks"
SelectedItem="{Binding SelectedBook}"
IsSynchronizedWithCurrentItem="True" Grid.Row="0" Grid.Column="1" />
Then my ViewModel looks like this:
enter public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
BooksEntities ctx = new BooksEntities();
List<Author> _authors;
List<Book> _books;
Author _selectedAuthor;
Book _selectedBook;
public MainViewModel()
{
FillAuthors();
}
public List<Author> Authors
{
get { return _authors; }
set
{
_authors = value;
NotifyPropertyChanged();
if (_authors.Count > 0) SelectedAuthor = _authors[0]; // <--- DO THIS
}
}
public Author SelectedAuthor
{
get { return _selectedAuthor; }
set
{
_selectedAuthor = value;
FillBooks();
NotifyPropertyChanged();
}
}
public List<Book> Books
{
get { return _books; }
set
{
_books = value;
NotifyPropertyChanged();
if (_books.Count > 0) SelectedBook = _books[0]; // <--- DO THIS
}
}
public Book SelectedBook
{
get { return _selectedBook; }
set
{
_selectedBook = value;
NotifyPropertyChanged();
}
}
#region Private Functions
private void FillAuthors()
{
var q = (from a in ctx.Authors select a).ToList();
this.Authors = q;
}
private void FillBooks()
{
Author author = this.SelectedAuthor;
var q = (from b in ctx.Books
orderby b.BookTitle
where b.AuthorId == author.Id
select b).ToList();
this.Books = q;
}
#endregion
}
Take a look at the Authors and Books properties of the ViewModel class. Once they are set, the usual PropertyChanged event is raised and the SelectedAuthor / SelectedBook is set to the first item.
Hope this helps.
Let me share my solution, that worked for me after several trials.
Here is my combo box:
<ComboBox
Name="fruitComboBox"
ItemsSource="{Binding Fruits}"
SelectedIndex="0"
SelectedValue="{Binding ComboSelectedValue}"
IsSynchronizedWithCurrentItem="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding displayFruitName}"
CommandParameter="{Binding SelectedValue, ElementName=fruitComboBox}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding displayFruitName}"
CommandParameter="{Binding SelectedValue, ElementName=fruitComboBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
In my case, I had to invoke a command every time a new item was selected in the comboBox or when the itemsource was being updated. But, the element at zero index was not getting selected when the item source was updated. So, what did I do? I added:
IsSynchronizedWithCurrentItem="True"
in the comboBox properties. It did the trick for me.
A little code from my ViewModel is below:
/// item source for comboBox
private List<string> fruits = new List<string>();
public List<string> Fruits
{
get { return fruits; }
set
{
fruits = value;
OnPropertyChanged();
ComboSelectedValue = value[0];
}
}
// property to which SelectedValue property of comboxBox is bound.
private string comboselectedValue;
public string ComboSelectedValue
{
get { return comboselectedValue; }
set
{
comboselectedValue = value;
OnPropertyChanged();
}
}
You can refer to this stackoverflow link and msdn link for further clarification regarding IsSynchronizedWithCurrentItem="True"
Hope it Helps! :)
At the moment I have the following:
<DataGridTextColumn Header="Customer Name"
x:Name="columnCustomerSurname"
Binding="{Binding Path=Customer.FullName}" SortMemberPath="Customer.Surname"
IsReadOnly="True">
</DataGridTextColumn>
where Customer.FullName is defined as:
public string FullName
{
get { return string.Format("{0} {1}", this.Forename, this.Surname); }
}
The binding works, but not ideally.
If someone updates the Forename or Surname properties the update is not reflected in the DataGrid until it is refreshed.
I found issues similar to this, e.g. https://stackoverflow.com/a/5407354/181771 which uses MultiBinding but this works with a TextBlock rather than with a DataGrid.
Is there another way for me to get this working?
One option would be to create a compound template column based on two textblocks which would still allow the form to update when changes to either property are made.
eg.
<DataGridTemplateColumn Header="Customer Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Customer.ForeName}"/>
<TextBlock Text="{Binding Path=Customer.SurName}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
You should raise a property change notification for FullName from both Forename and Surname, e.g.
public string Forename
{
get{ return _forename; }
set
{
if(value != _forename)
{
_forename = value;
RaisePropertyChanged("Forename");
RaisePropertyChanged("Fullname");
}
}
}
Or, you cache the generated value of FullName like this
public string Forename
{
get{ return _forename; }
set
{
if(value != _forename)
{
_forename = value;
RaisePropertyChanged("Forename");
UpdateFullName();
}
}
}
private void UpdateFullName()
{
FullName = string.Format("{0} {1}", this.Forename, this.Surname);
}
public string FullName
{
get{ return _fullname; }
private set
{
if(value != _fullname)
{
_fullname = value;
RaisePropertyChanged("FullName");
}
}
}