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"
Related
I have one class MovieSystem which inherits from AvailableMovies collection.
public class MovieSystem : IEnumerable<AvailableMovies >, IEnumerable
{
public AvailableMovies this[int i] { get; }
public AvailableMovies this[TheatreLocation location] { get; }
public AvailableMovies [] ReleasedMovies { get; }
}
TheatreLocation is an enum with values like
public enum TheatreLocation
{
Citycentre = 0,
CityNorth = 1,
CitySouth = 2,
CityEast = 3,
CityWest = 4,
}
AvailableMovies collection is with below properties
public class AvailableMovies
{
public AvailableMovies ();
public int Index { get; }
public TheatreLocation Location { get; set; }
public string TheatreID { get; set; }
public double Ticketprice{ get; }
}
My Xaml code looks like below
<ItemsControl ItemsSource="{Binding MovieSystem }">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Path= ""What should I bind here? ""}"
DisplayMemberPath="{Binding Path = ???}"
SelectedIndex="{Binding Path =Location , Mode=TwoWay, Converter={StaticResource MovieSystemLocationConverter}}">
</ComboBox>
<ComboBox
ItemsSource="{Binding Path = ???}"
SelectedIndex="{Binding Path=TheatreID , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="{Binding Path= ??}">
</ComboBox>
<TextBox Grid.Column="3"
Text="{Binding Path= Ticketprice, Mode =TwoWay,StringFormat=F3, NotifyOnValidationError=True}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I have a view model, xaml datacontext is set to this view model.
internal class MovieSystemPanelViewModel
{
///tried this collection but not worked.
public ObservableCollection<LocationViewModel> Locations { get; } = new ObservableCollection<LocationViewModel>();
public MovieSystem MovieSystem { get; }
public MovieSystemPanelViewModel() {}
}
Now my problem is both ComboBox which should display Theatre id and location not displaying due to binding issue. If try to bind that Locations observable collection inside ComboBox itemsource its not allowing, inside items template only AvailableMovies properties are in bindable condition.
Ticketprice is binding properly and displaying.
The issue here is since you are binding Items.Control with a collection, child elements of that Items.Control will have access to that collection's property only. So, if you want to use another collection you need to use relative source to get access in datacontext level. And since it's an enum to properly display, you need to display the dictionary values using an extension method and also using another Itemtemplate inside ComboBox to get the proper display.
internal class MovieSystemPanelViewModel
{
///Use dictionary instead of observable collection
public Dictionary<TheatreLocation , string> Locations { get; } = new Dictionary<TheatreLocation , string>();
public MovieSystem MovieSystem { get; };
public MovieSystemPanelViewModel()
{
// Fill up data in dictionary
foreach (Enum item in Enum.GetValues(typeof(TheatreLocation)))
{
Locations.Add((TheatreLocation)item, ((TheatreLocation)item).GetDisplayName());
}
}
}
Write a Extension method in project somewhere, for display member according to requirement.
public static string GetDisplayName(this TheatreLocation location)
{
switch (location)
{
case TheatreLocation.CityCentre:
return "Center city location";
case TheatreLocation.CityNorth :
return "Northern city location";
case TheatreLocation.CitySouth :
return "Southern city location";
case TheatreLocation.CityEast :
return "Eastern city location";
case CameraLocation.CityWest :
return "Western city location";
default:
return "Unknown";
}
}
Now Bind xaml with these properties and methods.
<ComboBox
ItemsSource="{Binding Path= DataContext.Locations, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
SelectedIndex="{Binding Path =Location , Mode=TwoWay, Converter={StaticResource MovieSystemLocationConverter}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Value}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I have a list of objects as an ObservableCollection<MyObject>. I am already able to display the name property of these objects in a combobox using XAML within a DataGrid.
Now I have another object AnotherObject which has a property that is defined as a list of strings and each item of that list is the name property of MyObject mentioned above.
In the combobox I want to display the MyObject.name property preceeded by a checkbox.
Let's say that there are 30 items in the checkbox and an instance of AnotherObject.names holds three of them.
Now I want select the checkboxes of those items that are equal to the three items in AnotherObject.names.
How can I achieve this?
Some code:
MyObjectViewModel.cs:
public class MyObjectViewModel
{
private MyObject _myObject;
public MyObjectViewModel(MyObject myObject)
{
this._myObject = myObject;
}
public MyObject MyObject
{
get
{
return _myObject;
}
set
{
_myObject = value;
}
}
public string Name
{
get { return _myObject.Name; }
set
{
_myObject.Name = value;
}
}
public override string ToString()
{
return Name;
}
}
AnotherObjectRowViewmodel.cs:
public class AnotherObjectRowViewModel : INotifyPropertyChanged
{
private AnotherObject _anotherObject;
private ObservableCollection<MyObjectViewModel> _myObjects;
public AnotherObjectRowViewModel(AnotherObject anotherObject, ObservableCollection<MyObjectViewModel> myObjects)
{
this._anotherObject = anotherObject;
this._myObjects = myObjects;
}
public AnotherObject AnotherObject
{
get
{
return _anotherObject;
}
set
{
this._anotherObject = value;
}
}
public string Name
{
get { return _anotherObject.Name; }
set { _anotherObject.Name = value; }
}
public ObservableCollection<MyObjectViewModel> MyObjects {
get
{
return this._myObjects;
}
set
{
_myObjects = value;
}
}
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add
{
//throw new NotImplementedException();
}
remove
{
//throw new NotImplementedException();
}
}
}
That's what I tried in the XAML file:
<DataGridTemplateColumn x:Name="NamesColumn" Header="Names">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox Name="Name" DataContext="{Binding}" ItemsSource="{Binding Path=myObjects}" IsEditable="True" IsReadOnly="True"
VerticalAlignment="Center" SelectionChanged="OnDetailParamsSelectionChanged" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="chbNames" Width="20" VerticalAlignment="Center" Checked="OnChbDetailParamsCheckBoxChecked" Unchecked="OnChbDetailParamsCheckBoxChecked"></CheckBox>
<TextBlock DataContext="{Binding Path=MyObject}" Text="{Binding Path=Name, Converter={StaticResource StringListConverter}}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
An example:
The combobox holds a list of 30 names (Name1, ..., Name30);
AnotherObject.names is { Name1, Name2, Name4, Name7 };
In the combobox the selected items shall be Name1, Name2, Name4, Name7. All other items shall stay unselected.
Update 2019-01-06:
This means that the Combobox's ItemsSource={Binding} is MyObject but the checked items shall be stored in AnotherObject. This is why I get this exception whenever I tick a checkbox:
System.Windows.Data Error: 40 : BindingExpression path error: 'xxx' property not found on 'object' ''MyObjectViewModel' (HashCode=34649765)'. BindingExpression:Path=xxx.DetailParams; DataItem='MyObjectViewModel' (HashCode=34649765); target element is 'CheckBox' (Name='chbDetailParams'); target property is 'IsChecked' (type 'Nullable`1')
My XAML contains thefollowing code snippet according to the IsItemsSelectedConverter:
<UserControl.Resources>
<ctb:IsItemSelectedConverter x:Key="IsItemSelectedConverter"/>
</UserControl.Resources>
The checkboxes IsChecked property looks like this:
IsChecked="{Binding Path=Names, Mode=TwoWay, Converter={StaticResource IsItemSelectedConverter}}"
but it doesn't work. Debugging this code, the IsItemsSelectedConverter is never used.
Create a Boolean property in the view model of MyObject, i.e. in MyObjectViewModel that will return a value if Name is in the list of names in AnotherObject.
Create an IsItemSelectedConverter and pass in the list of objects from your data model. Implement IValueConverter in a new class and bind the IsChecked property for each checkbox to two things: value should be the current item, and parameter should be the list of items. The converter should determine whether the current item is in the list of items and return the appropriate boolean.
You will have to create an instance of the Converter for the UI to use. I usually define converters in a separate Resource Dictionary that I make globally available for all XAML files.
My IsItemSelectedConverter looks like this:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is MyObjectViewModel objectViewModel && parameter is AnotherObjectRowViewModel anotherObjectRowViewModel)
{
return anotherObjectRowViewModel.MyObjects.Contains(objectViewModel);
}
return false;
}
On the XAML, my CheckBox code looks like this: <CheckBox IsChecked="{Binding Mode=OneWay, Converter={StaticResource converter}, ConverterParameter={StaticResource viewModel} }" />
Please note: there are a few problems with the code that will prevent it from running as is, but I'm assuming these are artifacts of copying and pasting your actual code.
I have an db generated entity:
public partial class UserMobileDevice
{
public string DeviceID { get; set; }
public string DeviceType { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
}
Now I would like to hook up a ComboBox that selects a User and updates the UserID and User object on a record.
So this is my xaml:
<ComboBox Name="cboDefaultUser"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
ItemsSource="{Binding Users}"
DisplayMemberPath="Username"
SelectedValuePath="UserID"
SelectedValue="{Binding TheEntity.UserID,
UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True,
Mode=TwoWay}" />
Which updates the UserID on the local entity just fine, however what about the User object? How do I go about assigning that at the same time?
(Using MVVM as well)
[Addition]
As you can see from the SelectedValue binding, my ViewModel has the entity in a property called TheEntity. So there is no UserID{get;set;} property actually in the ViewModel, which I can also use to also set the User object. So how could I accomplish this?
The ComboBox will only set one property for you. If you want to set both the User property and the UserId property, you could bind to a custom property that you define in another partial class definition of the same type and that does this for you:
public partial class UserMobileDevice
{
public User UiUser
{
get { return User; }
set
{
User = value;
if (value != null)
UserId = value.UserId;
}
}
}
You then bind the SelectedItem property of the ComboBox to this one:
<ComboBox Name="cboDefaultUser"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
ItemsSource="{Binding Users}"
DisplayMemberPath="Username"
SelectedItem="{Binding TheEntity.UiUser,
UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True}" />
Trying to use auto-generated types exactly "as-is" is seldom very useful.
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.
So, I have this class
public class Multimedia : INotifyPropertyChanged
{
private string title;
private string artist;
private string genre;
private MediaType type;
public Multimedia (string _title, string _artist, string _genre, MediaType _type)
{
this.title = _title;
this.artist = _artist;
this.genre = _genre;
this.type = _type;
}
public String Title { get { return this.title; } }
public String Artist { get { return this.artist; } }
public String Genre { get { return this.genre; } }
public MediaType Type { get { return this.type; } }
public event PropertyChangedEventHandler PropertyChanged;
}
A collection in which I store the objects:
public class MultiMediaList : ObservableCollection<Multimedia>
{
public MultiMediaList()
{
Add(new Multimedia("YMCA", "Village People", "disco", MediaType.CASSETTE));
Add(new Multimedia("Free", "Rudimental", "D&B/Liquid", MediaType.DVD));
Add(new Multimedia("November Rain", "Guns'n'Roses", "rock", MediaType.CD));
}
}
And the XAML of the ListBox:
<ListBox Name="LB_media" SelectionChanged="ListBox_SelectionChanged" DisplayMemberPath="Title" />
Code-behind of the Window:
MultiMediaList mediaList;
public MainWindow()
{
InitializeComponent();
mediaList = new MultiMediaList();
LB_media.ItemsSource = mediaList;
}
The ListBox I populate with the Title property value of the objects in the collection. Now, I have code-behind function that populates 3 other TextBox elements (for the properties Title, Artist and Genre and I want to populate them with the corresponding property values, depending on which item in the ListBox is selected. But, can I do that in XAML?
You have to set the DataContext for the surrounding container:
<StackPanel DataContext="{Binding ElementName=LB_media, Path=SelectedItem}">
<TextBox x:Name="tbArtist" Text="{Binding Artist}" />
[..]
</StackPanel>
Of couse you could access the SelectedItem directly but this solution is more elegant and robust to changes. If you decide to alter the DataContext you'd only have to replace one line.
You can bind to property of SelectedItem in your ListBox
<StackPanel>
<TextBox Text="{Binding ElementName=LB_media, Path=SelectedItem.Artist, Mode=OneWay}" IsReadOnly="True"/>
<TextBox Text="{Binding ElementName=LB_media, Path=SelectedItem.Genre, Mode=OneWay}" IsReadOnly="True"/>
<TextBox Text="{Binding ElementName=LB_media, Path=SelectedItem.Type, Mode=OneWay}" IsReadOnly="True"/>
</StackPanel>
because you use TextBox and property is read-only you must use Mode=OneWay otherwise you'll have some exceptions