is there a way when I build an UWP app to select the first entry in a combobox in pure XAML? Normally I would set IsSynchronizedWithCurrentItem="True", but in UWP I only get an error that it cannot be set.
The SelectedItem does not work, only if I set it explicitly to the same as the ItemsSource with an [0] after it, but then the second ComboBox does not update on changes and shows empty entries again.
Here is my code:
<ComboBox x:Name="MainSystemComboBox"
ItemsSource="{Binding Path=EditRulesViewModel.RuleSet.RuleMainSystems}"
SelectedItem="{Binding Path=EditRulesViewModel.RuleSet.RuleMainSystems, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
DisplayMemberPath="Name" SelectedValuePath="Id" />
<ComboBox x:Name="SubSystemComboBox"
ItemsSource="{Binding Path=SelectedItem.RuleSubSystems, ElementName=MainSystemComboBox}"
DisplayMemberPath="Name" SelectedValuePath="Id" />
Oh and it shows to me in the designer that the Path of the second ComboBox is incorrect, because it couldn't resolve property 'RuleSubSystems' in data context of type 'object', but after compiling it works well (except the selection of the first entry). Is there a cleaner way of binding one ComboBox to another?
Objects are simple
public class RuleMainSystem
{
public string Name { get; set; }
// Some more single properties...
public ObservableCollection<RuleSubSystem> RuleSubSystems { get; set; }
}
and
public class RuleSubSystem
{
public string Name { get; set; }
// Some more single properties...
}
The SelectedItem does not work, only if I set it explicitly to the same as the ItemsSource with an [0] after it
You bind the ItemsSource and SelectedItem to the same one property RuleMainSystems, it's not correct. You would have to bind the SelectedItem to a specific item. E.g, RuleMainSystems[0]
but then the second ComboBox does not update on changes and shows empty entries again.
That's because you have not bound SelectedItem, you need to do like the following:
<ComboBox x:Name="SubSystemComboBox"
ItemsSource="{Binding Path=SelectedItem.RuleSubSystems, ElementName=MainSystemComboBox}" SelectedItem="{Binding Path=SelectedItem.RuleSubSystems[0], ElementName=MainSystemComboBox}"
DisplayMemberPath="Name" SelectedValuePath="Id" />
Updated on [2018/5/28]
<ComboBox x:Name="MainSystemComboBox"
ItemsSource="{Binding Path=RuleMainSystems}"
SelectedItem="{Binding Path=MySelectedItem, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
DisplayMemberPath="Name" SelectedValuePath="Id"/>
<ComboBox x:Name="SubSystemComboBox"
ItemsSource="{Binding RuleSubSystems}" SelectedItem="{Binding SubSelectedItem,Mode=TwoWay}"
DisplayMemberPath="Name" SelectedValuePath="Id" />
public class RuleMainSystem
{
public string Name { get; set; }
// Some more single properties...
public ObservableCollection<RuleSubSystem> RuleSubSystems { get; set; }
}
public class RuleSubSystem
{
public string Name { get; set; }
// Some more single properties...
}
public MainPage()
{
this.InitializeComponent();
this.DataContext = new EditRulesViewModel();
}
public class EditRulesViewModel:ViewModelBase
{
public ObservableCollection<RuleMainSystem> RuleMainSystems { get; set; }
private RuleMainSystem _MySelectedItem;
public RuleMainSystem MySelectedItem
{
get { return _MySelectedItem; }
set
{
_MySelectedItem = value;
RuleSubSystems = MySelectedItem.RuleSubSystems;
SubSelectedItem = MySelectedItem.RuleSubSystems.FirstOrDefault();
RaisePropertyChanged("MySelectedItem");
}
}
private ObservableCollection<RuleSubSystem> _RuleSubSystems;
public ObservableCollection<RuleSubSystem> RuleSubSystems
{
get { return _RuleSubSystems; }
set
{
_RuleSubSystems = value;
RaisePropertyChanged("RuleSubSystems");
}
}
private RuleSubSystem _SubSelectedItem;
public RuleSubSystem SubSelectedItem
{
get { return _SubSelectedItem; }
set
{
_SubSelectedItem = value;
RaisePropertyChanged("SubSelectedItem");
}
}
public EditRulesViewModel()
{
RuleMainSystems = new ObservableCollection<RuleMainSystem>();
ObservableCollection<RuleSubSystem> SubSystems = new ObservableCollection<RuleSubSystem>();
SubSystems.Add(new RuleSubSystem() {Name="Sub1" });
SubSystems.Add(new RuleSubSystem() {Name="Sub2" });
ObservableCollection<RuleSubSystem> SubSystems1 = new ObservableCollection<RuleSubSystem>();
SubSystems1.Add(new RuleSubSystem() { Name = "Sub3" });
SubSystems1.Add(new RuleSubSystem() { Name = "Sub4" });
RuleMainSystems.Add(new RuleMainSystem() {Name="Rule1",RuleSubSystems = SubSystems });
RuleMainSystems.Add(new RuleMainSystem() { Name = "Rule2", RuleSubSystems = SubSystems1 });
MySelectedItem = RuleMainSystems.FirstOrDefault();
}
}
Related
I have a Combo box that is bound to a list Cities.
<ComboBox Name="cmbCities"
ItemsSource="{Binding Name}"
DisplayMemberPath="Name"/>
public class Cities
{
public string Name { get; set; }
public int Pin { get; set; }
}
I want to programmatically select NewYork in the combo whenever I click on btnSelectFavorite.
I tried-
cmbCities.SelectedItem as Cities= "NewYork";
I get an error-
The left-hand side of an assignment must be a variable, property or indexer
Any help is appreciated.
Here's some code and markup to consider:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Cities}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedCity}"
Width="160"
/>
<TextBlock Text="{Binding SelectedCity.Name}"/>
</StackPanel>
</Window>
and
public class MainWindowViewModel : BindableBase
{
public ObservableCollection<City> Cities { get; set; } = new ObservableCollection<City>(
new List<City>
{
new City{Name="Liverpool", Pin=1},
new City{Name="Manchester", Pin=2},
new City{Name="London", Pin=3}
}
);
private City selectedCity;
public City SelectedCity
{
get => selectedCity;
set { selectedCity = value; RaisePropertyChanged(nameof(SelectedCity)); }
}
}
public class City : BindableBase
{
public string Name { get; set; }
public int Pin { get; set; }
}
The window's datacontext is an instance of mainwindowviewmodel.
That presents a collection of city and it has a selectedcity property.
The comnbobox will set that selectedcity when the user selects one.
The city's name can then be used.
I'm binding it to the text of a textblock just to demonstrate it's selecting a city ok and the property is set.
EDIT:
Let's change this a bit so we can select a city.
The code I have uses prism so let's add a command - a delegatecommand.
This will get a reference to one of the cities and set selectedcity to that instance.
I'll also show you the Bindablebase SetProperty. This raises property changed (for the property) as well as setting the backing field.
And of course we need a button to click so we can invoke the command.
Title="MainWindow" >
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Cities}"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedCity}"
Width="160"
/>
<TextBlock Text="{Binding SelectedCity.Name}"
MinWidth="100"/>
<Button Content="New York Select"
Command="{Binding SelectNYCommand}"/>
</StackPanel>
</Window>
and
public class MainWindowViewModel : BindableBase
{
public ObservableCollection<City> Cities { get; set; } = new ObservableCollection<City>(
new List<City>
{
new City{Name="Liverpool", Pin=1},
new City{Name="Manchester", Pin=2},
new City{Name="London", Pin=3},
new City{Name="New York", Pin=4}
}
);
private City selectedCity;
public City SelectedCity
{
get => selectedCity;
set { SetProperty(ref selectedCity, value); }
}
public DelegateCommand SelectNYCommand { get; private set; }
public MainWindowViewModel()
{
SelectNYCommand = new DelegateCommand(() =>
{
var ny = Cities.Where(x => x.Name == "New York").FirstOrDefault();
if(ny != null)
{
SelectedCity = ny;
}
});
}
I am using mvvm in wpf. I am setting a form to update user data. I have a combobox to select gender of user. i have added combobox items manually in source. when loading data to form all fields other fields are displaying correctly. but combobox is not displaying anything. I have used twoWay binding and the values i am selecting from form are getting in the viewModel.I have been searching for hours and found many similar problem, but nothing worked for me. I am inserting my code segment bellow. Please give me a solution.
<ComboBox
Grid.Column="2"
SelectedItem="{Binding SelectedEmployees.gender, Mode=TwoWay}"
SelectedValue="{Binding SelectedEmployees.gender, Mode=TwoWay}"
>
<ComboBoxItem Content="Male"/>
<ComboBoxItem Content="Female"/>
</ComboBox>
my viewModel code is as bellow
class EmployeesModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private iServiceClient serviceClient = new iServiceClient();
public EmployeesModel()
{
this.RefreshEmployees();
}
private void RefreshEmployees()
{
this.serviceClient.GetAllEmployeesCompleted += (s, e) =>
{
this.employees = e.Result;
};
this.serviceClient.GetAllEmployeesAsync();
}
private IEnumerable<Employee> employees;
public IEnumerable<Employee> Employees
{
get
{
return this.employees;
}
set
{
this.employees = value;
this.OnPropertyChanged("Employees");
}
}
private Employee selectedEmployees;
public Employee SelectedEmployees
{
get
{
return this.selectedEmployees;
}
set
{
this.selectedEmployees = value;
this.OnPropertyChanged("SelectedEmployees");
}
}
public void OnPropertyChanged(string PropertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
and SelectedEmployees class is
public class Employee
{
[Key]
public int id { get; set; }
public DateTime JoiningDate { get; set; }
public string name { get; set; }
public string gender { get; set; }
public string mobile { get; set; }
public string post { get; set; }
public string salaryType { get; set; }
public decimal salary { get; set; }
public string docname { get; set; }
public int validit { get; set; }
}
I suspect that SelectedEmployees.gender is not type comboboxitem.
Taking the shortcut of creating comboboxitems directly in the combobox is a bad move.
When I do:
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Window.DataContext>
<local:MainWIndowViewModel/>
</Window.DataContext>
<Window.Resources>
<x:Array Type="sys:String" x:Key="Genders">
<sys:String>Male</sys:String>
<sys:String>Female</sys:String>
</x:Array>
</Window.Resources>
<Grid>
<ComboBox
SelectedItem="{Binding gender, Mode=TwoWay}"
ItemsSource="{StaticResource Genders}"
/>
</Grid>
I get a string instead of a comboboxitem in my bound gender.
You probably want something rather more like that.
This is probably the best approach, particularly if you mean to learn MVVM: Use an enum type for Gender. "LOL" is never a valid gender so don't let anybody try to use it. Populate the ComboBox by binding it to a static collection. Initialize SelectedEmployees.gender to the value you want to be the default and the binding will take care of the rest.
<ComboBox
SelectedItem="{Binding SelectedEmployees.gender}"
ItemsSource="{Binding SelectedEmployees.Genders}"
/>
C#
public class SelectedEmployeesViewModel : ViewModelBase
{
/* ...other stuff... */
private Gender _gender = Gender.Male;
public Gender gender
{
get { return _gender; }
set
{
if (value != _gender)
{
_gender = value;
OnPropertyChanged();
}
}
}
}
public enum Gender
{
Male, Female
}
public static class EnumValues
{
public static IEnumerable<Gender> Genders => Enum.GetValues(typeof(Gender)).Cast<Gender>();
}
There are other approaches. I advise against going with a string, but this is illustrative at least:
private String _gender = "Male";
public String gender
{
get { return _gender; }
set
{
if (value != _gender)
{
_gender = value;
OnPropertyChanged();
}
}
}
Does your SelectedEmployees class implement INotifyPropertyChanged, and does SelectedEmployees.gender raise PropertyChanged when its value changes?
Get rid of Mode=TwoWay on the binding; you don't need to do that explicitly. It's the default for any binding you put on ComboBox.SelectedValue or on ComboBox.SelectedItem.
As Andy pointed out in comments, your SelectedValue and SelectedItem are both going to be instances of ComboBoxItem, because that's how you populated your ComboBox. The string you want is in the Content property of the ComboBoxItems, so use SelectedValuePath to tell the ComboBox about that, and bind to the SelectedValue property. SelectedItem will be the ComboBoxItem itself, which is useless to you.
<ComboBox
SelectedValue="{Binding SelectedEmployees.gender}"
SelectedValuePath="Content"
>
<ComboBoxItem Content="Male" />
<ComboBoxItem Content="Female" />
</ComboBox>
Here's another approach: Populate the ComboBox with strings.
<ComboBox
SelectedItem="{Binding SelectedEmployees.gender}"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<sys:String>Male</sys:String>
<sys:String>Female</sys:String>
</ComboBox>
See Andy's answer for yet another way to populate the ComboBox with strings via ItemsSource.
I am new to C# WPF, I am tring to set DataContext to combobox my xml is a below
<Grid Name="groupEditArea" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#FFD8D8D8" Margin="-14,0,192,0">
<Label Content="Group Name" FontSize="18" HorizontalAlignment="Left" Margin="116,50,0,0" VerticalAlignment="Top" Width="136"/>
<Label Content="Group Type" FontSize="18" HorizontalAlignment="Left" Margin="116,123,0,0" VerticalAlignment="Top" Width="136"/>
<TextBox x:Name="groupGroupNameTxt" HorizontalAlignment="Left" FontSize="16" Height="31" Margin="368,50,0,0" TextWrapping="Wrap" Text="{Binding Path = GroupName, Mode=TwoWay, StringFormat=\{0:n3\}, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="226" TextChanged="groupGroupNameTxt_TextChanged" /> <!-- GotFocus="GroupGroupNameTxt_OnGotFocus" TextInput="GroupGroupNameTxt_OnTextInput" -->
<ComboBox x:Name="groupGroupNameCombo" HorizontalAlignment="Left" Margin="368,123,0,0" VerticalAlignment="Top" Width="226" Height="31" SelectionChanged="groupGroupNameCombo_SelectionChanged" DisplayMemberPath="GroupName" SelectedValuePath="CategoriesVal" SelectedValue="{Binding Categories}"/>
</Grid>
my POCO as below :-
public class Test: INotifyPropertyChanged
{
public Test()
{
}
public virtual string TestId { get; set; }
public virtual Categories CategoriesVal { get; set; }
public virtual string Name{ get; set; }
public virtual string GroupName
{
get { return Name; }
set
{
Name = value;
OnPropertyChanged("GroupName");
}
}
}
public class Categories : INotifyPropertyChanged
{
public Categories ()
{
}
public virtual string CategorieId { get; set; }
public virtual string Name{ get; set; }
public virtual string GroupName
{
get { return Name; }
set
{
Name = value;
OnPropertyChanged("GroupName");
}
}
}
}
and in my backend code i am setting DataContext as below :-
Categories cate = new Categories ();
cate.CategorieId = "cate1ID";
cate.GroupName = "CateGroupName1"
Test test = new Test();
test.TestId = "TestID";
test.CategoriesVal = cate;
test.Name = "TestName1";
and groupGroupNameCombo is set using ItemsSource and that contain entire list on Categories when i set using below its working fine
groupGroupNameCombo.SelectedItem = cate;
but when i set using below to grid it wont work :-
groupEditArea.DataContext = test;
can someone guide me how can i set combobox by setting grid DataContext instead of setting manually combobox.
instead
SelectedValuePath="CategoriesVal" SelectedValue="{Binding Categories}"
write
SelectedItem="{Binding CategoriesVal}"
SelectedValuePath means: the name of property (from the ComboBoxItem DataContex - Categories class In our case) from which the value of SelectedValue will be provided.
This is useful in case you want to representation of a instance-item (each form Combobox.Items) is not done by the item itself, but by one characteristic. In your case I do not see any point in it.
see more: Difference between SelectedItem, SelectedValue and SelectedValuePath
I have a combobox and the list is populated using the AccountType class and the list is getting populated properly.
However when I bind the selected Item property to the Selected Account which is class account. On page load the selected item is not getting updated. All the other controls like textbox are getting updated.
Any help will be highly appreciated.
View
ComboBox ItemsSource="{Binding AllAccountTypes}" DisplayMemberPath="AccountTypeName"
SelectedValuePath="AccountTypeName" SelectedItem="{Binding SelectedAccount}" />
AccountType class
public class AccountType:IAccountType
{
public string AccountTypeName { get; set; }
}
Account Class
public class Account: IAccount
{
public int AccountNo { get; set; }
public string AccountName { get; set; }
public string AccountTypeName { get; set; }
public int SubAccount { get; set; }
public string Description { get; set; }
public double Balance { get; set; }
public string Note { get; set; }
public bool Active { get; set; }
}
Selected Account in ViewModel
public IAccount SelectedAccount { get { return selectedAccount; }
set { selectedAccount = value; }
}
First, your ViewModel needs to be raising the PropertyChanged event of INotifyPropertyChanged.
Second, your binding should specify two-way binding:
<ComboBox ItemsSource="{Binding AllAccountTypes}" DisplayMemberPath="AccountTypeName"
SelectedValuePath="AccountTypeName" SelectedItem="{Binding SelectedAccount, Mode=TwoWay}" />
But third, and I think the main issue here, is that your Combo box is bound to a list of AccountTypes (i.e. IAccountType), yet you want the selected item to be an IAccount. But there is no property of type IAccount on an IAccountType.
So you need to bind the SelectedItem to an IAccountType property, or bind SelectedValue to a string property on your ViewModel. e.g.:
<ComboBox ItemsSource="{Binding AllAccountTypes}" DisplayMemberPath="AccountTypeName"
SelectedItem="{Binding SelectedAccountType, Mode=TwoWay}" />
and in your ViewModel have a property to bind to:
public IAccountType SelectedAccountType
{
get { return selectedAccountType; }
set
{
if (Equals(value, selectedAccountType)) return;
selectedAccountType = value;
OnPropertyChanged("SelectedAccountType");
}
}
This is because you are binding SelectedItem to an IAccount object, but you are selecting a string from your dropdown list.
I would bind to a string instead, and then in the setter do what needs to be done to set the SelectedAccount property, something like this:
public string SelectedAccountName
{
get { return _selectedAccountName; }
set
{
_selectedAccountName = value;
SelectedAccount = AllAccounts.Where(x => x.AccountName == _selectedAccountName).First();
}
}
With XAML like this (I added height and width values so the dropdown isn't massive):
<ComboBox Height="20" Width="100" ItemsSource="{Binding AllAccountTypes}" DisplayMemberPath="AccountTypeName" SelectedValuePath="AccountTypeName" SelectedItem="{Binding SelectedAccountName}" />
I would like to know How to refresh our ListBox item.
I tried OnPropertyChanged method, ObservableCollection, but it didn't work. I tried set again the itemsource property, so that worked, but now I have got 2 ListBox and now it's complicated. It's a wp7 project there is the main interface. You can see I have 2 listbox
<ListBox Name="lsbNameDays" ItemsSource="ComplexNameDays">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding NameDay.Name}" FontSize="50"/>
<ListBox ItemsSource="ComplexNameDays.FacebookFriends" x:Name="asdf">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Lastname}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
there is the properties:
List<SelectedNameDays> complexNameDays;
public List<SelectedNameDays> ComplexNameDays
{
get { return complexNameDays; }
set
{
complexNameDays = value;
OnPropertyChanged("ComplexNameDays");
}
}
public class SelectedNameDays : Notifier
{
NameDay _nameday;
public NameDay NameDay
{
get { return _nameday; }
set { _nameday = value; OnPropertyChanged("NameDay"); }
}
public List<FacebookFriend> FacebookFriends { get; set; }
public SelectedNameDays()
{
_nameday = new NameDay();
}
}
public class FacebookFriend
{
public long Id { get; set; }
public string Name { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Birthday { get; set; }
public string Gender { get; set; }
public Uri Picture { get; set; }
}
The begining of the code is correct, that works, because when the Constructor set the datas I set retry the itemsource of lbsNameDays, but i cant find the "asdf" listbox, i can't set their datas again.
so the 2 main question are that.
1. how can i fire the property changed if that, and the observable collecton doesn't works.
2. how can I use asdf listbox in the datatemplate
thank the answer, and I sorry my grammer mistakes
Your Bindings won't work, because you dont use the right syntax:
ItemsSource="ComplexNameDays"
should be
ItemsSource="{Binding ComplexNameDays}"
The second binding is also wrong:
ItemsSource="ComplexNameDays.FacebookFriends"