I'm trying to define a selected item in a combobox. It working fine if I'm just using a String to declare the selected item but not if using an object.
<ComboBox HorizontalAlignment="Left"
VerticalAlignment="Top" Width="81" materialDesign:HintAssist.Hint="Woche" Margin="10"
ItemsSource="{Binding weekSelection}"
DisplayMemberPath="name"
SelectedItem="{Binding nodeWeek, Mode=TwoWay}"
SelectedValue="name" />
-
private week _nodeWeek;
public week nodeWeek
{
get
{
return _nodeWeek;
}
set
{
_nodeWeek = value;
RaisePropertyChanged("nodeWeek");
}
}
-
public class week
{
public int val { get; set; }
public String name { get; set; }
}
-
setting the selected item
this.nodeWeek = new week() { val = times.GetIso8601WeekOfYear(DateTime.Now), name = "KW " + times.GetIso8601WeekOfYear(DateTime.Now).ToString() };
Is there a way to fix that?
The selected item must be always one of the list of your items source. You cannot create new objects and assign them to the SelectedItem. The Combobox simply compares object references not the content.
Related
I've recently started learning C# and I've encountered a problem. I display a set of keywords in a generated checkbox in my WPF and I want to check the element (IsChecked) based on an input check from a TXT file.
If the currently selected element from a different listbox matches the read modelclass(from a txt file) then set the checked key true.
I'm generating a Checkbox in my WPF to list a set of keywords that my app reads from a txt file.
The txt file contains the following items per line:
-id
-key
-pair
-description
WPF code:
<ListView ItemsSource="{Binding XAMLModelKeywords}" SelectedItem="{Binding XAMLModelKeyword}" Margin="5" x:Name="listofallkeys" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" >
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding XAMLAssignedKeys}" Content="{Binding Key}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C#:
public ModelTemplates XAMLModelTemplate { get; set; }
public ModelKeywords XAMLModelKeyword { get; set; }
public List<bool> XAMLAssignedKeys { get; set; }
public string XAMLKeyword { get; set; }
public ViewModelMain()
{
//This creates a new instance of ObservableCollection above
XAMLModelTemplates = new ObservableCollection<ModelTemplates>();
XAMLModelKeywords = new ObservableCollection<ModelKeywords>();
XAMLAssignedKeys = new List<bool>();
Refresh();
}
public void Refresh()
{
XAMLModelTemplates.Clear();
foreach (ModelTemplates tpl in ReadInput.ReadTemplateDirectory(Path))
{
XAMLModelTemplates.Add(tpl);
}
//Selecting the first item from the returned list
XAMLModelTemplate = XAMLModelTemplates.FirstOrDefault();
XAMLModelKeywords.Clear();
foreach (ModelKeywords tpl in ReadInput.ReadKeywordsFile(KeyWordsPath))
{
XAMLModelKeywords.Add(tpl);
}
XAMLModelKeyword = XAMLModelKeywords.FirstOrDefault();
XAMLAssignedKeys.Clear();
foreach (ModelKeywords tpl in XAMLModelKeywords)
{
XAMLAssignedKeys.Add(ReadInput.CheckPairedtemplates(tpl, XAMLModelTemplate));
}
ModelKeyword:
public class ModelKeywords
{
public int Id { get; set; }
public string Key { get; set; }
public List<string> PairedTemplates { get; set; }
public string Description { get; set; }
}
ModelTemplate:
public class ModelTemplates
{
//path to a template
public int Id { get; set; }
public string TemplatePath { get; set; }
public string TemplateName { get; set; }
public ExcelRange TemplateRange { get; set; }
}
ReadKeywordsFile:
Returns a list of Template Models (name of template file, path) and the displays it in a listbox.
ReadKeywordsFile:
Returns a list of Keywords Model (id, key, pair, desc) and then displays it in a generated listbox.
CheckPairedtemplates:
Returns a list of booleans based on the currently selected Template Model matches the Keywords Model pair (list of string).
TLDR:
I have a list of booleans ( XAMLAssignedKeys) and I want to match it to my generated checkbox in WPF, however the generation happens based on an item template and I'm not sure how to link one element from my list of booleans to the checkbox "IsChecked" property.
ScreenshotofApp
Thank you much in advance for the advices.
Since you set ObservableCollection<ModelKeywords> (XAMLModelKeywords) to ItemsSource property of ListView (listofallkeys), each item of the ListView will be bound to a member of the ObservableCollection. In this case, the CheckBox inside DataTemplate will be bound to a ModelKeywords in XAMLModelKeywords. Therefore, you need to bind the properties of CheckBox with the properties of ModelKeywords.
In the Xaml, you mistakenly set XAMLAssignedKeys to IsChecked property of CheckBox. It must be a property of ModelKeywords. The remedy will be to add a bool property to ModelKeywords, somehow copy a value in the XAMLAssignedKeys to the property, and then bind the property with IsChecked properties of CheckBox.
Let's say, if you add IsEnabled bool property to ModelKeywords, the Xaml will be
<CheckBox IsChecked="{Binding IsEnabled}" Content="{Binding Key}"/>
In addition, unless you implement INotifyPropertyChanged interface to ModelKeywords, only initial value of the property will be sent to the CheckBox and the subsequent values will not be notified and reflected to the CheckBox when the property is changed.
I have two comboboxes to represent a customer code and a customer name. Both are bound to the same collection of objects and to the same SelectedItem. I want to update the customer name when I select a customer code and vice versa.
I am using C# with the MVVM pattern. I have tried all the combination of SelectedItem and SelectedValue with selectedvaluepath but nothing seems to work.
These are my two combo boxes:
<ComboBox Name="CmbStockUnitCustomerCode" ItemsSource="{Binding CustomerCodeDtos}"
DisplayMemberPath="Code" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"></ComboBox>
<ComboBox Name="CmbStockUnitCustomerName" ItemsSource="{Binding CustomerCodeDtos}"
DisplayMemberPath="Name" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"></ComboBox>
And these are the bound objects:
public CustomerDto SelectedCustomer
{
get => _selectedcustomer;
set
{
_selectedcustomer = value;
RaisePropertyChanged("SelectedCustomer");
}
}
public class CustomerDto
{
public short Code { get; set; }
public string Name { get; set; }
public CustomerDto(short code, string name)
{
this.Code = code;
this.Name = name;
}
}
public ObservableCollection<CustomerDto> CustomerCodeDtos
{
get => _databaseService.GetAllCustomers();
}
When I update one of the comboboxes, I expect the other one to update to the corresponding value in the object CustomerDto, but nothing happens.
You're recreating your collection every time you reference it, so SelectedItem is referencing a different object. In effect, your two combo boxes are using different collections as ItemsSources. Change the code to
public ObservableCollection<CustomerDto> CustomerCodeDtos
{
get
{
if(_customerCodes==null)
{
_customerCodes = _databaseService.GetAllCustomers();
}
return _customerCodes;
}
}
You have a Picker in Xamarin that displays the name of certain Fabricantes, as follows
this picker is filled by passing an ObservableCollection of the FABRICANTES object
Fabricantes.CS:
public class Fabricante
{
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
}
FiltrosView.XAML:
<Picker Title="Seleccione Fabricante"
Margin="15,5,15,5"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Fabricantes, Mode=TwoWay}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding Id}"
SelectedIndex="{Binding Id}">
</Picker>
How do I fill the Picker?
Here I declare the list and the property that will capture the ID of the picker, to open the list in the picker, I will cast the object that arrives from an API and will fill the Observable collection and then bindarla in sight, it WORKS!
FiltrosViewModel.CS:
public ObservableCollection<Fabricante> Fabricantes { get; set; }
public int IdSustancia
{
get
{
return idSustancia;
}
set
{
if (idSustancia != value)
{
idSustancia = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IdSustancia)));
}
}
}
var response = await apiService.GetList<Fabricante>(urlService, param);
var list = (List<Fabricante>)response.Result;
Fabricantes.Clear();
foreach (var item in list)
{
Fabricantes.Add(item);
}
But, since I capture the ID (int) of the value that the user entered (string), can this be done in xamarin?
I would like to be able to capture the value of the ID of the picker in the property IdSubstance declared previously, how to do it? any help for me?
The best way of doing that is binding the selected index of the picker. In your XAML code, add the following line to your picker:
SelectedIndex={Binding IdSustancia, Mode=TwoWay}
So, when index != -1 (initial value), you can get Fabricante's ID in your ObservableCollection, accessing Fabricantes[IdSustancia].
use the SelectedItem property
SelectedItem="{Binding IdSustancia}"
however, for this to work you need to bind it to a property of type Fabricante.
I have a multi-column List bound to a ComboBox.
It displays List Column Name as the ItemSource.
Problem
SelectedItem does return the Name.
With MessageBox.Show(vm.cboVideoQuality_SelectedItem); the string shows as MyProgram.ViewModel+VideoQuality instead of High.
I tried filtering Name with:
vm.cboVideoQuality_SelectedItem.FirstOrDefault(s => s.Name);
But it gives error 'char' does not contain definition for Name.
XAML
Bind Item Source and Selected Item.
<ComboBox x:Name="cboVideoQuality"
ItemsSource="{Binding cboVideoQuality_Items, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedItem="{Binding cboVideoQuality_SelectedItem, Mode=TwoWay}"
SelectedValuePath="Name"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="0,0,0,0"
Width="100" />
ViewModel
// Item Source
//
public class VideoQuality
{
public string Name { get; set; }
public string Bitrate { get; set; }
}
public List<VideoQuality> _cboVideoQuality_Items = new List<VideoQuality>()
{
new ViewModel.VideoQuality() { Name = "High", Bitrate = "5000K" },
new ViewModel.VideoQuality() { Name = "Medium", Bitrate = "2500K" },
new ViewModel.VideoQuality() { Name = "Low", Bitrate = "500K" },
};
public List<VideoQuality> cboVideoQuality_Items
{
get { return _cboVideoQuality_Items; }
set
{
_cboVideoQuality_Items = value;
OnPropertyChanged("cboVideoQuality_Items");
}
}
// Selected Item
//
public string _cboVideoQuality_SelectedItem { get; set; }
public string cboVideoQuality_SelectedItem
{
get { return _cboVideoQuality_SelectedItem; }
set
{
if (_cboVideoQuality_SelectedItem == value)
{
return;
}
_cboVideoQuality_SelectedItem = value;
OnPropertyChanged("cboVideoQuality_SelectedItem");
}
}
C#
I cannot check if selected = "High" because SelectedItem does not equal Name.
It equals MyProgram.ViewModel+VideoQuality.
ViewModel vm = mainwindow.DataContext as ViewModel;
string selected = vm.cboVideoQuality_SelectedItem;
if (selected == "High")
{
// ...
}
You have already used SelectedValuePath and should of course also use SelectedValue instead of SelectedItem:
<ComboBox
ItemsSource="{Binding cboVideoQuality_Items}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding cboVideoQuality_SelectedItem}" .../>
As a note, in both Bindings Mode=TwoWay is redundant. An ItemsSource binding is never two-way, while a SelectedValue binding is two-way by default.
I'd also recommend not to use binding target property details in view model property names. E.g. cboVideoQuality_SelectedItem should just be SelectedVideoQuality. Thus you may also bind it to the SelectedValue property of a ListBox or even the Text property of a TextBlock without too much confusion.
I have one ObservableCollection<M> fooBar {get;set;}. The class M.cs looks like this:
public class M{
private int _ID;
public int ID {
get {return this._ID;}
set {this._ID = value;}
}
private string _number;
public int Number {
get {return this._number;}
set {this._number = value;}
}
private string _power;
public int Power {
get {return this._power;}
set {this._power = value;}
}
/*
...
*/
}
Now I want to bind the names of these 3 propertys to a ComboBox. I don't want to do it like this:
<ComboBox>
<ComboBoxItem>ID</ComboBoxItem>
<ComboBoxItem>Number</ComboBoxItem>
<ComboBoxItem>Power</ComboBoxItem>
</ComboBox>
Is there a more comfortable way?
Based on the choose of the first ComboBox I want to fill the second ComboBox. As example I choose in the first ComboBox the property Numberthen the second ComboBox should look like this
<ComboBox
SelectedValue="{Binding ???}"
ItemsSource="{Binding fooBar}"
SelectedValuePath="Number"
DisplayMemberPath="Number"
/>
Maybe someone of you can help me, because I have no idea how to connect both comboboxes.
This is how I would do it:
Make a property on the view model which exposes the properties on the model (class M) which can be selected. This way you explicitly control which properties can be selected.
Make a property to hold the selected value of each combo box.
DisplayMemberPath/SelectedValuePath in ComboBox2 binds to the SelectedValue of ComboBox1.
ViewModel:
// ComboBox 1
public Dictionary<string, string> SelectableProperties = new Dictionary<string, string>()
{
{ nameof (M.ID), "ID" }
{ nameof (M.Number), "Nummer" }
{ nameof (M.Power), "Potenz" }
}
// Selection in combobox 1 (not strictly necessary as it can be handled in view, but you may need to know what SelectedValue represents)
private string _selectedValueMember = String.Empty;
public string SelectedValueMember
{
get { return _selectedValueMember; }
set { _selectedValueMember = value; }
}
// Selection in combobox 2 (object just in case there could be other values than int)
private object _selectedValue = null;
public object SelectedValue
{
get { return _selectedValue; }
set { _selectedValue = value; }
}
public ObservableCollection<M> FooBar{ get; set; }
View:
<ComboBox x:Name="ComboBox1"
Width="100"
Margin="5"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedValue="{Binding SelectedValueMember}"
ItemsSource="{Binding SelectableProperties}">
</ComboBox>
<ComboBox Width="100"
Margin="5"
DisplayMemberPath="{Binding ElementName=ComboBox1, Path=SelectedValue}"
SelectedValuePath="{Binding ElementName=ComboBox1, Path=SelectedValue}"
SelectedValue="{Binding SelectedValue}"
ItemsSource="{Binding FooBar}">
</ComboBox>
For the 1st ComboBox: Use Reflection to get the names of all the properties of class M and put those into the ComboBox.
For the 2nd ComboBox: When selection changes in the 1st one, you get the property name. Now set the SelectedValue binding to the property that was selected in the 1st ComboBox.