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;
}
});
}
Related
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();
}
}
So I have an online database. And I want the query results to be displayed in a listbox. I tried to use data binding but I think I'm doing it totally wrong.
Files
[![enter image description here][1]][1]
Table
class ProductTable
{
public string id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public string Type { get; set; }
public string Seller { get; set; }
public int Expiration { get; set; }
}
MainViewModel.cs
namespace Test
{
class MainViewModel
{
IMobileServiceTable<ProductTable> product = App.MobileService.GetTable<ProductTable>();
//In this method, load your data into Products
public async void Load()
{
// This query filters out completed TodoItems.
MobileServiceCollection<ProductTable, ProductTable> Products = await product
.Where(ProductTable => ProductTable.Price == 15)
.ToCollectionAsync();
// itemsControl is an IEnumerable that could be bound to a UI list control
IEnumerable itemsControl = Products;
}
}
}
XAML
<Page x:Name="PAGE"
xmlns:local="clr-namespace:Test;assembly=Version1">
<Grid>
<Grid.DataContext>
<local:MainViewModel />
</Grid.DataContext>
<ListBox Margin="10,10,10,100" x:Name="lb" ItemsSource="{Binding Products}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" FontSize="10"></TextBlock>
<TextBlock Text="{Binding Title}" FontSize="10"></TextBlock>
<TextBlock Text="{Binding Description}" FontSize="10"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Page>
Solution
Table needs JSON property name explicitly declared otherwise you won't be able to use data binding.
Table.cs
public class ProductTable
{
[JsonProperty(PropertyName = "id")]
public string id { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "Title")]
public string Title { get; set; }
[JsonProperty(PropertyName = "Description")]
public string Description { get; set; }
[JsonProperty(PropertyName = "Price")]
public double Price { get; set; }
[JsonProperty(PropertyName = "Type")]
public string Type { get; set; }
[JsonProperty(PropertyName = "Seller")]
public string Seller { get; set; }
[JsonProperty(PropertyName = "Expiration")]
public int Expiration { get; set; }
}
XAML.cs
You don't need to declare a new listbox, only use itemsource.
private async void button1_Click(object sender, RoutedEventArgs e)
{
IMobileServiceTable<ProductTable> productTest = App.MobileService.GetTable<ProductTable>();
// This query filters out completed TodoItems.
MobileServiceCollection<ProductTable, ProductTable> products = await productTest
.Where(ProductTable => ProductTable.Type == "Test")
.ToCollectionAsync();
lb.ItemsSource = products;
}
XAML
IN THE STACKPANEL, NEVER EVER USE HEIGHT"*" this will cause a critical error!
<ListBox x:Name="lb" Margin="10,10,10,100" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Width="300">
<TextBlock Text="{Binding Name}" FontSize="10"></TextBlock>
<TextBlock Text="{Binding Title}" FontSize="10"></TextBlock>
<TextBlock Text="{Binding Description}" FontSize="10"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You are creating two ListBoxes - one in code that isn't being displayed and one in XAML which is bound to whatever your DataContext is.
Remove the last two lines in your code, and change your XAML to:
ItemsSource="{Binding Products}"
Next, expose Products as a Property on a class (ViewModel if you want to use MVVM), and ensure that the DataContext of your ListBox is set to said ViewModel/class.
I'm not familiar with MobileServiceCollection, so I assume it is bindable. If it is not, expose Products as a ObservableCollection (if the values in it change over time), or any other supported collection type.
Elaboration on Properties
Create a class:
public class MainViewModel {
//This is a property
public ObservableCollection<ProductTable> Products { get; set; }
//In this method, load your data into Products
public void Load(){
//Products = xxx
}
}
In your XAML:
<Page [...]
xmlns:local="clr-namespace:YourNamespace;assembly=YourProjectName">
<Grid>
<Grid.DataContext>
<local:MainViewModel />
</Grid.DataContext>
<!-- ListBox goes in here somewhere, still with ItemsSource bound to Products -->
</Grid>
</Page>
Read more about namespaces here.
By doing this, your Windows' DataContext will be set to the MainViewModel (could also be named ProductsViewModel). Since your ListBox will be within your Window, it will inherit (due to Property Value Inheritance) the same DataContext.
The Binding will look for a property 'Products' on the ViewModel.
You will need to call the Load() method somewhere.
Part 2 - After looking at the code
Mind you, I am not able to run the code, so I'm flying somewhat blind.
Buy.xaml.cs
public sealed partial class Buy : Page
{
private readonly MainViewModel _viewModel;
public Buy()
{
this.InitializeComponent();
_viewModel = new MainViewModel();
DataContext = _viewModel;
}
private async void button1_Click(object sender, RoutedEventArgs e)
{
_viewModel.Load();
}
}
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
IMobileServiceTable<ProductTable> product = App.MobileService.GetTable<ProductTable>();
public List<ProductTable> Products { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public async void Load()
{
Products = await product
.Where(ProductTable => ProductTable.Price == 15)
.ToListAsync();
//Notify that the property has changed to alert to UI to update.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Products)));
}
}
You need to make ProductTable public, or you will get a compilation error.
Hopefully, the above will enable you to bind your ListBox using the ItemsSource binding described above.
And please note that the above code is not necessarily best practice.
you don't need that line in your code behind
ListBox lb = new ListBox();
and you can keep that line
lb.ItemsSource = Products;
or you can sit the Binding to the MobileServiceCollection in the XAML
<ListBox Margin="10,10,10,100" x:Name="lb" ItemsSource="{Binding Products}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Title}"/>
<TextBlock Text="{Binding Description}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In my application I have a ListBox and a DataGrid.
I'm trying to bind the DataGrid to an ObservableCollection which is a property of the returned SelectedItem from the ListBox - this doesn't work and I don't understand why. Output console doesn't print any errors.
The ObservableCollection is of type ReportItem, and it can contain instances of TextReportItem, which inherits from ReportItem.
XAML:
<ListBox x:Name="listBox" HorizontalAlignment="Left" Margin="10,10,0,36.667" Width="119" ItemsSource="{Binding ReportItems}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel HorizontalAlignment="Left" Height="274" Margin="134,10,0,0" VerticalAlignment="Top" Width="375" >
<StackPanel.Resources>
<Style x:Key="ControlBaseStyle" TargetType="{x:Type Control}">
<Setter Property="Margin" Value="0, 10, 0, 0" />
</Style>
</StackPanel.Resources>
<DataGrid Height="190" VerticalAlignment="Bottom" DataContext="{Binding ElementName=listBox, Path=SelectedItem.TItems}" />
</StackPanel>
ViewModel:
public class MainViewModel
{
public ObservableCollection<ReportItem> ReportItems { get; set; }
public object SelectedReportItem { get; set; }
public MainViewModel()
{
ReportItems = new ObservableCollection<ReportItem>();
ReportItems.Add(Example);
}
// line below is for debugging purposes
public TextReportItem Example = new TextReportItem() { Name = "ti1", DataFile = "ti1.txt"};
}
ReportItem:
public class ReportItem
{
public int Id { get; set; }
public string Name { get; set; }
public string DataFile { get; set; }
}
TextReportItem and TextParcel:
public class TextReportItem : ReportItem
{
public ObservableCollection<TextParcel> TItems { get; set; }
public TextReportItem()
{
TItems = new ObservableCollection<TextParcel>();
}
}
public class TextParcel
{
public char Delimiter { get; set; }
public string LineExp { get; set; }
public string Result { get; set; }
public string IgnoreLine { get; set; }
public int DesiredResultIndexInLine { get; set; }
}
I think the mere DataContext doesn't tell the DataGrid what it has to show. You have to give it an ItemsSource. Maybe you could try something like that:
<DataGrid
DataContext="{Binding ElementName=listBox, Path=SelectedItem}"
ItemsSource="{Binding TItems}" />
I believe that the DataContext of a DataGrid expects an IEnumerable, and not a single instance of an object. When you set the source to a single instance of a ReportItem, you're not passing it anything that it can iterate to construct it's rows.
If you make the necessary changes and give that a yank, you should be golden :)
you can use select index change method instead
protected void GridView_SelectedIndexChanged(object sender, EventArgs e){
string value = null;
value = GridView1.SelectedRow.Cells(2).Text.ToString();
}
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 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"