One of the proprties in my ViewModel is a collection of objects. I cant figure it out how to bind my inner DataTemplate to the properties of these objects inside the collection: The problematic binding is in TestDataTemplate1, it just cannot find N and STR.. Here is my XAML
<Page.Resources>
<DataTemplate x:Key="TestDataTemplate1">
<Grid Background="Cornsilk" HorizontalAlignment="Stretch" Height="Auto">
<TextBlock x:Name="LeftTB" Text="{Binding N}" Foreground="Red" HorizontalAlignment="Left" VerticalAlignment="Stretch"/>
<TextBlock x:Name="RightTB" Text="{Binding STR}" Foreground="Green" HorizontalAlignment="Right" VerticalAlignment="Stretch"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="TestDataTemplate">
<Grid Background="Yellow" HorizontalAlignment="Stretch" Height="Auto">
<TextBlock x:Name="LeftTB" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<ListBox x:Name="MyListBox" x:FieldModifier="Public"
ItemsSource="{Binding SampleCollection}"
ItemTemplate="{StaticResource TestDataTemplate1}"
Background="Blue"
HorizontalAlignment="Center"
VerticalAlignment="Stretch" Width="200">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<TextBlock x:Name="RightTB" Text="{Binding Age}" HorizontalAlignment="Right" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Page.Resources>
This is my Type that contains these properties:
class AlaBalaVM : INotifyPropertyChanged
{
AlaBala _ab = null;
public AlaBalaVM(AlaBala ab)
{
_ab = ab;
}
public string N
{
get
{
return "num: " + _ab._n.ToString();
}
set { RaisePropertyChanged("N"); }
}
public string STR
{
get
{
return _ab._str.ToString() + " string";
}
set { RaisePropertyChanged("STR"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And this is my ViewModel in which I fill the DataSource collection:
public class PersonVMWrapper : INotifyPropertyChanged
{
PersonModel _pm = null;
public PersonVMWrapper(PersonModel pm)
{
_pm = pm;
}
public string Name
{
get
{
return "mr." + _pm.Name;
}
set { RaisePropertyChanged("Name"); }
}
public string Age
{
get
{
return _pm.Age.ToString() + " years";
}
set { RaisePropertyChanged("Age"); }
}
public ObservableCollection<AlaBala> SampleCollection
{
get
{
return _pm.SampleCollection;
}
set { RaisePropertyChanged("SampleCollection"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class PersonVM : INotifyPropertyChanged
{
DispatcherTimer _refreshTimer = new DispatcherTimer();
private ObservableCollection<PersonVMWrapper> personDataSource;
public PersonModel PM { get; set; }
public PersonVM()
{
AddItemToDataSource(null);
}
private void AddItemToDataSource(object o)
{
DataSource.Add(new PersonVMWrapper(new PersonModel() { Name = "asdasd", Age = 23, SampleCollection = new ObservableCollection<AlaBala> { new AlaBala(3, "e1"), new AlaBala(4, "e2") } }));
}
public ObservableCollection<PersonVMWrapper> DataSource
{
get
{
if (this.personDataSource == null)
{
this.personDataSource = new ObservableCollection<PersonVMWrapper>();
}
return this.personDataSource;
}
set
{
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You haven't posted the definition of the "Alabala" class -- does it contain the "N" and "STR" properties? As #KooKiz pointed out, it looks like you meant to make "SampleCollection" an ObservableCollection<AlaBalaVM>, ie:
SampleCollection = new ObservableCollection<AlaBalaVM>
{
new AlaBalaVM(new AlaBala(3, "e1")),
new AlaBalaVM(new AlaBala(4, "e2")),
}
Related
I have been having this issue and have been trying to figure out for the past few days, I set up a second test project as my first project is too big to try and nail this down. I'm still fairly new to mvvm so I'm still learning. Whenever I try to save a field using Entity it starts complaining the FirstName field is required, I sort of nailed down the issue to being apart of something with the button because whenever I move the button into the same UserControl as the textbox it will save. The TabControl in the MainWindow has a UserControl for each tab and each one has their own respective ViewModel and then the TabControl itself has a ViewModel.
MainWindow
Just contains the button and the tab control
<Window.Resources>
<vm:TabControlViewModel x:Key="tab"/>
</Window.Resources>
<Grid DataContext="{StaticResource tab}">
<TabControl Margin="10"
Width="500"
Height="500">
<TabItem Header="Test Tab 1">
<custom:TabOneUserControl/>
</TabItem>
<TabItem Header="Test Tab 2">
<custom:TabTwoUserControl/>
</TabItem>
</TabControl>
<Button Content="Save"
Width="120"
Height="50"
Margin="1114,604,41,38.5"
Command="{Binding SaveCommand}"/>
</Grid>
TabOneUserControl
Contains a textbox and label
<UserControl.Resources>
<vm:TabOneUserControlViewModel x:Key="vm"/>
</UserControl.Resources>
<Grid DataContext="{StaticResource vm}">
<StackPanel VerticalAlignment="Center">
<Label Content="First Name"
HorizontalAlignment="Center"/>
<TextBox Width="250"
Height="50"
Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
</UserControl>
TabTwoUserControl
Contains a textbox and label
<UserControl.Resources>
<vm:TabTwoUserControlViewModel x:Key="vm"/>
</UserControl.Resources>
<Grid DataContext="{StaticResource vm}">
<StackPanel VerticalAlignment="Center">
<Label Content="Last Name"
HorizontalAlignment="Center"/>
<TextBox Width="250"
Height="50"
Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
</UserControl>
TabControlViewModel
For the tab control
public class TabControlViewModel
{
public SaveCommand SaveCommand { get; set; }
private TabOneUserControlViewModel tabOneUserControl;
private TabTwoUserControlViewModel tabTwoUserControl;
public TabControlViewModel()
{
tabOneUserControl = new TabOneUserControlViewModel();
tabTwoUserControl = new TabTwoUserControlViewModel();
SaveCommand = new SaveCommand(this);
}
public void SaveInformation()
{
using (TestDbEntities test = new TestDbEntities())
{
test.FNs.Add(new FN
{
FirstName = tabOneUserControl.FirstName
});
test.LNs.Add(new LN
{
LastName = tabTwoUserControl.LastName
});
try
{
test.SaveChanges();
Debug.Print("SAVED CHANGES!");
}
catch (DbEntityValidationException ex)
{
foreach (var validationErrors in ex.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation(
"Class: {0}, Property: {1}, Error: {2}",
validationErrors.Entry.Entity.GetType().FullName,
validationError.PropertyName,
validationError.ErrorMessage);
}
}
}
}
}
}
TabOneUserControlViewModel
For the first tab user control
public class TabOneUserControlViewModel : INotifyPropertyChanged
{
private string firstName;
public event PropertyChangedEventHandler PropertyChanged;
public string FirstName
{
get { return firstName; }
set
{
firstName = value;
OnPropertyChanged("FirstName");
}
}
public TabOneUserControlViewModel()
{
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
TabTwoUserControlViewModel
For the second tab user control
public class TabTwoUserControlViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string lastName;
public string LastName
{
get { return lastName; }
set
{
lastName = value;
OnPropertyChanged("LastName");
}
}
public TabTwoUserControlViewModel()
{
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Thanks to everyone in the comments for your suggestions, Nawed's comment nailed it perfectly for what I was doing wrong. Here is the final code changes I made:
TabControlViewModel
Removed the fields and made them into properties
public class TabControlViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public SaveCommand SaveCommand { get; set; }
public TabOneUserControlViewModel TabOneUserControl { get; set; }
public TabTwoUserControlViewModel TabTwoUserControl { get; set; }
public TabControlViewModel()
{
TabOneUserControl = new TabOneUserControlViewModel();
TabTwoUserControl = new TabTwoUserControlViewModel();
SaveCommand = new SaveCommand(this);
}
public void SaveInformation()
{
using (TestDbEntities test = new TestDbEntities())
{
test.FNs.Add(new FN
{
FirstName = TabOneUserControl.FirstName
});
test.LNs.Add(new LN
{
LastName = TabTwoUserControl.LastName
});
try
{
test.SaveChanges();
Debug.Print("SAVED CHANGES!");
}
catch (DbEntityValidationException ex)
{
foreach (var validationErrors in ex.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation(
"Class: {0}, Property: {1}, Error: {2}",
validationErrors.Entry.Entity.GetType().FullName,
validationError.PropertyName,
validationError.ErrorMessage);
}
}
}
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
TabOneUserControl
Removed the static resource from the grid and replaced it with a regular binding and then changed the binding in the Text element, this was the same thing I did in the second user control so I will negate posting the second UserControl:
<Grid DataContext="{Binding TabOneUserControl}">
<StackPanel VerticalAlignment="Center">
<Label Content="First Name"
HorizontalAlignment="Center"/>
<TextBox Width="250"
Height="50"
Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
I tried to bind a property to a nested object, but it fails.
I have taken a look at those questions, but i think i made another mistake somewhere else. Maybe someone can give i hind.
WPF: How to bind to a nested property?
binding to a property of an object
To upper slider/textbox has a correct binding while the lower one fails to do so.
I have two sliders with corrosponding textboxes:
<StackPanel>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox Text="{Binding Path= boundnumber, Mode=TwoWay, FallbackValue='binding failed'}" ></TextBox>
<Slider Value="{Binding Path= boundnumber, Mode=TwoWay}" Width="500" Maximum="1000" ></Slider>
</StackPanel>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" DataContext="{Binding Path=myDatarow}">
<TextBox Text="{Binding Path= boundnumber, Mode=TwoWay, FallbackValue='binding failed'}" ></TextBox>
<Slider Value="{Binding Path= boundnumber, Mode=TwoWay}" Width="500" Maximum="1000" ></Slider>
</StackPanel>
</StackPanel>
Code behind:
public partial class MainWindow : INotifyPropertyChanged
{
public MainWindow()
{
DataContext = this;
InitializeComponent();
}
private int _boundnumber;
public int boundnumber
{
get { return _boundnumber; }
set
{
if (value != _boundnumber)
{
_boundnumber = value;
OnPropertyChanged();
}
}
}
Datarow myDatarow = new Datarow(11);
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
class Datarow : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
public Datarow()
{
}
public Datarow(int number)
{
boundnumber = number;
}
private int _boundnumber;
public int boundnumber
{
get { return _boundnumber; }
set
{
if (value != _boundnumber)
{
_boundnumber = value;
OnPropertyChanged();
}
}
}
}
You need to expose your myDatarow into a public property like your boundnumber.
private DataRow _myDatarow = new DataRow(11);
public DataRow myDataRow
{
get { return _myDatarow; }
}
And just an additional advice.
It's better to separate your DataContext class from the MainWindow.
My ListBox doesn't react to my ObservableCollection.
This is the ListBox I am talking about.
<ListBox x:Name="CreateFieldsList"
HorizontalAlignment="Left"
Height="218"
VerticalAlignment="Top"
Width="244"
Margin="0,86,0,0"
BorderBrush="#FFB9B9B9">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="4"
Width="215"
Height="32.96"
Background="White">
<TextBlock Text="{Binding Name}"
FontWeight="Normal"
FontSize="18.667"
Padding="8,3,0,0" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In my MainWindow, I prepare the data binding like this
private ObservableCollection<FieldListItem> _fieldItems;
public MainWindow()
{
InitializeComponent();
_fieldItems = new ObservableCollection<FieldListItem>();
CreateFieldsList.ItemSource = _fieldItems;
}
A FieldListItem is following
public class FieldListItem : ViewItem
{
private Field _field;
public string Name
{
get { return _field.Name; }
}
public string Value
{
get { return _field.Value; }
}
public FieldListItem(Field f)
{
_field = f;
}
}
and finally the ViewItem
public class ViewItem : INotifyPropertyChanged
{
private event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string caller = "")
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
//The interface forces me to implement this. Why?
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add { }
remove { }
}
}
I don't know why this isn't working. Could you please help?
The INotifyPropertyChanged interface needs you to implement an event. Your event implementation does not work because registrations and deregistrations are ignored because the add and remove blocks are empty.
Implement the event without add and remove:
public class ViewItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string caller = "")
{
var copy = PropertyChanged;
if (copy != null)
copy(this, new PropertyChangedEventArgs(caller));
}
}
I have a ListBox that is populated through a ViewModel and a DataTemplate in which I also have a binding. The problem is the binding in the DataTemplate is not working. Here is my code:
ViewModel:
private void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private string strHeaderText;
public string HeaderText
{
get
{
if (null != this.Trips)
{
if (null != this._trips.name)
{
return this._trips.name;
}
}
return "Trip";
}
set
{
this.strHeaderText = value;
}
}
public TripTypeViewModel()
{
this.TripTypeViewModelDataSource.Clear();
foreach (TripType tt in TripsResponseTypeViewModelDataSource.FirstOrDefault().Trip)
{
this.TripTypeViewModelDataSource.Add(tt);
}
}
...
private ObservableCollection<TripType> tripTypeViewModelDataSource;
public ObservableCollection<TripType> TripTypeViewModelDataSource
{
get
{
if (null == this.tripTypeViewModelDataSource)
{
this.tripTypeViewModelDataSource = new ObservableCollection<TripType>();
}
return this.tripTypeViewModelDataSource;
}
}
Here is my View
<ListBox x:Name="ItemsLB" ItemsSource="{Binding tripTypeViewModel.TripTypeViewModelDataSource}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding tripTypeViewModel.HeaderText}" Width="Auto" Height="Auto"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I am trying to bind a label to an Object.Object.Property and I don't get it run.
Here is my code:
XAML
<Window x:Class="MyApp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyWindow" Height="1120" Width="800">
<Grid Name="MyGrid">
<StackPanel Orientation="Horizontal">
<Label FontWeight="Bold" FontSize="40" Content="{Binding MyDataObject/AnotherSubObject/MyProperty}"/>
</StackPanel>
</Grid>
</Window>
And the Code:
public partial class MyWindow : Window
{
public MySubObject MyDataObject { get; set; }
public MyWindow(MySubObject object)
{
InitializeComponent();
this.MyDataObject = object; // Contains MyDataObject.AnotherObject.MyProperty
DataContext = this;
}
}
And the code for MySubObject object looks like this:
public class MySubObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
AnotherObject _AnotherObject;
public MySubObject()
{
this._AnotherObject = new AnotherObject();
this._AnotherObject.Property = "Some Value";
}
public AnotherObject AnotherObject
{
get { return _AnotherObject; }
set { _AnotherObject = value; OnPropertyChanged("AnotherObject"); }
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I would be glad to get dome support for this case.
Use Dot(.) as binding property path separator not Forward slash(/)
<Label FontWeight="Bold" FontSize="40"
Content="{Binding MyDataObject.AnotherSubObject.MyProperty}"/>