WPF MVVM: Binding to property of object - c#

I try to display values in my ComboBox using Binding. But I have no idea why isn't it working:
<ComboBox Width="476" Height="30" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,10,0,0" ItemsSource="{Binding Maps.Name}"></ComboBox>
Here is my C#:
public class Map
{
public string Name { get; set; }
public string ImagePath { get; set; }
}
And main:
class MainWindowViewModel : BindableBase
{
public ObservableCollection<Map> Maps { get; set; }
public MainWindowViewModel()
{
Maps = mainWindowModel.LoadMapFiles(); //deserializes maps, i checked it, LoadMapFiles() works
}
}
What should I write in ComboBox ItemSource if I want it to display every Map.Name?

The expression Maps.Name is not a valid Binding Path, because Name is not a property of the ObservableCollection<Map> in Maps.
Bind the ItemsSource property to the collection property, and set the displayed property by DisplayMemberPath:
<ComboBox ItemsSource="{Binding Maps}" DisplayMemberPath="Name" ... />
Also make sure that the Maps property setter fires a change notification, or make the property read-only:
public ObservableCollection<Map> Maps { get; }

Related

ComboBox to assign UserID value and User Object to entity

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.

How to make an object raise a "PropertyChanged" or execute a method on parent when its nested property changed?

I have a model, and I don't want it implement INotifyPropertyChanged:
public class PeopleModel
{
public string name { get; set; }
public string company { get; set; }
}
And now I have a ViewModel:
public class DetailViewModel : CustomViewModelBase
{
private PeopleModel _person;
public PeopleModel person
{
get
{
return _person;
}
set
{
_person=value;
OnPropertyChanged();
}
}
public ICommand customOnUpdateCommand { get; set; }
}
Now, using WPF and only using XAML, I bind a textbox content to the name (The whole window is already bind to the ViewModel):
<StackPanel
DataContext="{Binding Path=person}">
<DockPanel>
<Label Content="name" Target="{Binding ElementName=person_TextBox}" />
<TextBox x:Name="person_TextBox"
Text="{Binding Path=name, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
</DockPanel>
</StackPanel>
Now, I want to do the following: when I change the name property in this person object, the person object should raise a "PropertyChanged" in the ViewModel. Or alternative, execute a custom command, or execute a method(which would be much better).
I already tried data triggers, System.Windows.Interactivity, or using RelativeSource, but they don't work, maybe I didn't use them correctly.
How do I achieve this correctly?
Note: I already confirmed that when the textbox changes, the name in the object will change correctly. So the binding process is correct.

XAML binding not working in C# UWP app

i have a problem with binding an ObservableCollection in XAML
the class :
[DataContract]
public class Result
{
[DataMember]
public string title { get; set; }
[DataMember]
public string href { get; set; }
[DataMember]
public string ingredients { get; set; }
[DataMember]
public string thumbnail { get; set; }
}
the Observable Collection :
private ObservableCollection<Result> resultTest;
the XAML code for binding :
<ListView Name="RecipeListView"
ItemsSource="{Binding resultTest}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Result">
<StackPanel Orientation="Vertical">
<TextBlock Name="RecipeTitleTextBlock"
Text="{Binding title}"
Foreground="Black"
FontSize="24">
</TextBlock>
<Image Name="RecipeImage"
Source="{Binding thumbnail}"
Width="45"
Height="45">
</Image>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Everything seems to be correct as application launches, but there is no content visible that I was binding to.
Thanks you guys for any help.
The issue is you're declaring "resultTest" as a "filed", not a property.
However, the binding system uses reflection to look for property, it does not look for "field".
Changing your resultTest declaration to property would solve the issue.
public ObservableCollection<Result> resultTest {get; private set;}
Also, make sure you have the DataContext properly set with xaml or code-behind like
this.DataContext = this
or
this.DataContext = new ViewModel()
Depending on what your DataContext really is.
My full MainWindow.cs looks like below, and the ListView binding works.
public sealed partial class MainPage : Page
{
public ObservableCollection<Result> resultTest { get; private set; }
public MainPage()
{
resultTest = new ObservableCollection<Result>();
resultTest.Add(new Result() { title = "Hello" });
resultTest.Add(new Result() { title = "World" });
this.DataContext = this;
this.InitializeComponent();
}
}
If you have your datacontext set (i.e. in your code behind have datacontext=this or if you are using mvvm have your datacontext set in your window like this
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
And if you have your collection property setup like
public ObservableCollection<Result> resultTest {get;set;}
as Szabolcs Desi suggested then I would try removing the x:Datatype="data:Result" on your DataTemplate. I tested the code you have minus that and it works for me.
Sorry for this dumb question , the problem was I was using a static method that returned an ObservableCollection to a variable. I thought I should remove it to a void function and fill the collection by foreach inside. Everything in xaml was right.

WPF: Binding with two different lists

I have a few Problems with databinding in WPF.
I have a ListBox which has a binding to a BindingList.
<ListBox x:Name="SampleListBox" ItemsSource="{Binding List1}" ItemContainerStyle="{StaticResource ListBoxStyle}" BorderThickness="0" SelectedIndex="0" Margin="0">
<ListBox.ItemTemplate>
<DataTemplate >
<Border x:Name="border" Width="185">
<TextBlock Text="{Binding name}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Till here, everything works fine. Now I have a DataGrid which should be linked to another BindingList and display some strings of it. So for example, if the first item of the ListBox is selected, the grid should show data of the first item of the second list.
I know, how it would work if both, the ListBox and the Grid get the data from the same list, but I have no idea, what to do, if this is not possible and there are two different lists.
You could bind SelectedIndex for the ListBox control to an property of type Int (Property1) in your ViewModel.
Also two-way bind SelectedItem in the DataGrid to another property (Property2) of the second list type.
In the setter for the Property1, change Property2 to be the item at the index of Property1 - i.e. List2[Property1]. It should change the selected item in the DataGrid.
So you want to use the listbox to, essentially, set a filter on the grid?
Note that LBItem and ViewModel below need to implement INotifyPropertyChanged and fire their PropertyChanged events when properties change, or none of this will work. But I'm leaving out the boilerplate for clarity.
Lots of ways to do that.
C#
public class LBItem {
public ViewModel Parent { get; set; }
public IEnumerable<String> SubItems {
get {
return Parent.List2.Where( /* filter items here */ );
}
}
}
public class ViewModel {
//
public ObservableCollection<LBItem> LBItems { get; set; }
public LBItem SelectedLBItem { get; set; }
public List<String> List2 { get; set; }
}
XAML
<ListBox
Name="MasterLB"
ItemsSource="{Binding LBItems}"
SelectedItem={Binding SelectedLBItem}"
/>
<DataGrid
ItemsSource="{Binding ElementName=MasterLB, Path=SelectedItem.SubItems}"
/>
That will work whether or not you bind MasterLB.SelectedItem to a property on the ViewModel. But as long as you are binding MasterLB.SelectedItem, you could just as easily bind DataGrid.ItemsSource to SelectedLBItem.SubItems on the ViewModel, like so:
<DataGrid
ItemsSource="{Binding Path=SelectedLBItem.SubItems}"
/>
But the ElementName binding is handy for a lot of things, so I'm giving you both.
You could also do something like this:
C#
public class LBItem {
public IEnumerable<String> Filter(IEnumerable<String> fullList) {
return fullList.Where( /* filter items here */ );
}
}
public class ViewModel {
public ObservableCollection<LBItem> LBItems { get; set; }
private LBItem _selectedItem;
public LBItem SelectedLBItem {
get { return _selectedItem; }
set {
_selectedItem = value;
List2Filtered = (null == _selectedItem)
? new List<String>()
: _selectedItem.Filter(List2).ToList();
}
}
public List<String> List2 { get; set; }
public List<String> List2Filtered { get; set; }
}
XAML
<ListBox
Name="MasterLB"
ItemsSource="{Binding LBItems}"
SelectedItem={Binding SelectedLBItem}"
/>
<DataGrid
ItemsSource="{Binding List2Filtered}"
/>

Object binding to listbox does not work

At the moment i try, to build something like that with WPF ! Screenshot: http://i.imgur.com/5G6xBTu.png
I have a ObservableCollection with my "Wecker" Objects. I want to dynamicly add items to the listbox with DataBinding that looks like in the Screenshot. Every try failed so far. What do i need to set in the XAML File??
public static ObservableCollection<Wecker> WeckerCollection = new ObservableCollection<Wecker>();
public ObservableCollection<Wecker> MyWeckerCollection
{
get { return WeckerCollection; }
}
Wecker Class
public class Wecker
{
public ArrayList dayOfWeek { get; set; }
public DateTime Alarm { get; set; }
public bool activated { get; set; }
public bool loop { get; set; }
public int maxRunTime { get; set; }
public int id { get; set; }
public bool schlummern { get; set; }
public bool antiStandby { get; set; }
public bool activateMonitor { get; set; }
public string fileName { get; set; }
public string Mp3 { get; set; }
public string Message { get; set; }
public bool ShowMessage { get; set; }
public int volume { get; set; } }
I tryed that last time:
<ListBox HorizontalAlignment="Left" Height="392" VerticalAlignment="Top" Width="431" Margin="15,89,0,0" ScrollViewer.VerticalScrollBarVisibility="Visible" ItemsSource="{Binding MyWeckerCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding activated, Mode=TwoWay}" />
<Label Content="{Binding Alarm}" />
<Label Content="{Binding dayOfWeek}" />
<Label Content="{Binding Message}" />
<Label Content="{Binding Mp3}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I suspect you have not set the DataContext
In the ctor set the DataContext
this.DataContext = this;
or you can do it in XAML in the Window (top) section
DataContext="{Binding RelativeSource={RelativeSource self}}"
If you had set the DataContext then that should work
Are you sure it is in the Windows section
Try (but Path is the default property so that should not be a problem)
ItemsSource="{Binding Path=MyWeckerCollection}"
This may be your problem - public static?
What is the purpose of public static here?
public static ObservableCollection<Wecker> WeckerCollection = new ObservableCollection<Wecker>();
If you want to use a backing property then do it like this
private ObservableCollection<Wecker> myWeckerCollection = new ObservableCollection<Wecker>();
public ObservableCollection<Wecker> MyWeckerCollection
{
get { return myWeckerCollection ; }
}
It sounds like your DataContext is set incorrectly.
You say you are binding the DataContext to {Binding RelativeSource={RelativeSource Self}}, however that just binds the DataContext to the UI object itself.
For example,
<Window DataContext="{Binding RelativeSource={RelativeSource Self}}">
would set the DataContext to the Window object, however the class Window does not have a property called MyWeckerCollection, so your binding would fail.
If you had
<local:MyCustomWindow DataContext="{RelativeSource={RelativeSource Self}}">
and MyCustomWindow has a property called MyWeckerCollection, then it would work.
I also see your comment here which states:
I am not getting any Data at all and i checked the object, "WeckerCollection" it has Data BEFORE setting it as the DataContext
This leads me to believe that either
A) MyWeckerCollection is not a UI control, in which case you need to update your DataContext binding to something other than Self so it correctly binds to your object containing MyWeckerCollection instead of to the UI object.
B) Or this comment can be read as you are setting the DataContext to MyWeckerCollection itself, and of course the class ObservableCollection<Wecker> does not itself have a property called MyWeckerCollection, so the binding would fail.
So the root cause of your problem is the DataContext is not being set correctly.
Unfortunately, the information you provided is not enough for us to help to identify the correct way to set the DataContext, however if you can provide us with more information I'd be glad to help you out.
Often Visual Studio's binding errors and/or Debug mode is enough to point you in the right direction for fixing the DataContext, or there are some 3rd party tools out there like Snoop which I'd highly recommend for debugging binding errors.
Also if you're new to WPF (which it sounds like you are), and are struggling to understand the purpose of the DataContext and how it works, I'd suggest a blog article of mine written for beginners: What is this "DataContext" you speak of?. Its very important that you understand the DataContext if you are going to be working with WPF. :)
Try to add the ItemSource to your ListBox and change the Xaml like this :
Code behind :
this.YourList.ItemsSource = WeckerCollection;
Xaml :
<ListBox HorizontalAlignment="Left" Height="392" VerticalAlignment="Top" Width="431" Margin="15,89,0,0" ScrollViewer.VerticalScrollBarVisibility="Visible">

Categories

Resources